doc: Add serial to list of ci file reserved words
[charm.git] / src / conv-core / memory-gnu-hooks.c
1 /* Malloc implementation for multiple threads without lock contention.
2    Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public License as
8    published by the Free Software Foundation; either version 2.1 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 /* $Id$ */
22
23 #ifndef DEFAULT_CHECK_ACTION
24 #define DEFAULT_CHECK_ACTION 1
25 #endif
26
27 /* What to do if the standard debugging hooks are in place and a
28    corrupt pointer is detected: do nothing (0), print an error message
29    (1), or call abort() (2). */
30
31 /* Hooks for debugging versions.  The initial hooks just call the
32    initialization routine, then do the normal work. */
33
34 static Void_t*
35 #if __STD_C
36 malloc_hook_ini(size_t sz, const __malloc_ptr_t caller)
37 #else
38 malloc_hook_ini(sz, caller)
39      size_t sz; const __malloc_ptr_t caller;
40 #endif
41 {
42   __malloc_hook = NULL;
43   ptmalloc_init();
44   return public_mALLOc(sz);
45 }
46
47 static Void_t*
48 #if __STD_C
49 realloc_hook_ini(Void_t* ptr, size_t sz, const __malloc_ptr_t caller)
50 #else
51 realloc_hook_ini(ptr, sz, caller)
52      Void_t* ptr; size_t sz; const __malloc_ptr_t caller;
53 #endif
54 {
55   __malloc_hook = NULL;
56   __realloc_hook = NULL;
57   ptmalloc_init();
58   return public_rEALLOc(ptr, sz);
59 }
60
61 static Void_t*
62 #if __STD_C
63 memalign_hook_ini(size_t alignment, size_t sz, const __malloc_ptr_t caller)
64 #else
65 memalign_hook_ini(alignment, sz, caller)
66      size_t alignment; size_t sz; const __malloc_ptr_t caller;
67 #endif
68 {
69   __memalign_hook = NULL;
70   ptmalloc_init();
71   return public_mEMALIGn(alignment, sz);
72 }
73
74
75 static int check_action = DEFAULT_CHECK_ACTION;
76
77 /* Whether we are using malloc checking.  */
78 static int using_malloc_checking;
79
80 /* A flag that is set by malloc_set_state, to signal that malloc checking
81    must not be enabled on the request from the user (via the MALLOC_CHECK_
82    environment variable).  It is reset by __malloc_check_init to tell
83    malloc_set_state that the user has requested malloc checking.
84
85    The purpose of this flag is to make sure that malloc checking is not
86    enabled when the heap to be restored was constructed without malloc
87    checking, and thus does not contain the required magic bytes.
88    Otherwise the heap would be corrupted by calls to free and realloc.  If
89    it turns out that the heap was created with malloc checking and the
90    user has requested it malloc_set_state just calls __malloc_check_init
91    again to enable it.  On the other hand, reusing such a heap without
92    further malloc checking is safe.  */
93 static int disallow_malloc_check;
94
95 /* A simple, standard set of debugging hooks.  Overhead is `only' one
96    byte per chunk; still this will catch most cases of double frees or
97    overruns.  The goal here is to avoid obscure crashes due to invalid
98    usage, unlike in the MALLOC_DEBUG code. */
99
100 #define MAGICBYTE(p) ( ( ((size_t)p >> 3) ^ ((size_t)p >> 11)) & 0xFF )
101
102 /* Instrument a chunk with overrun detector byte(s) and convert it
103    into a user pointer with requested size sz. */
104
105 static Void_t*
106 internal_function
107 #if __STD_C
108 mem2mem_check(Void_t *ptr, size_t sz)
109 #else
110 mem2mem_check(ptr, sz) Void_t *ptr; size_t sz;
111 #endif
112 {
113   mchunkptr p;
114   unsigned char* m_ptr = (unsigned char*)BOUNDED_N(ptr, sz);
115   size_t i;
116
117   if (!ptr)
118     return ptr;
119   p = mem2chunk(ptr);
120   for(i = chunksize(p) - (chunk_is_mmapped(p) ? 2*SIZE_SZ+1 : SIZE_SZ+1);
121       i > sz;
122       i -= 0xFF) {
123     if(i-sz < 0x100) {
124       m_ptr[i] = (unsigned char)(i-sz);
125       break;
126     }
127     m_ptr[i] = 0xFF;
128   }
129   m_ptr[sz] = MAGICBYTE(p);
130   return (Void_t*)m_ptr;
131 }
132
133 /* Convert a pointer to be free()d or realloc()ed to a valid chunk
134    pointer.  If the provided pointer is not valid, return NULL. */
135
136 static mchunkptr
137 internal_function
138 #if __STD_C
139 mem2chunk_check(Void_t* mem)
140 #else
141 mem2chunk_check(mem) Void_t* mem;
142 #endif
143 {
144   mchunkptr p;
145   INTERNAL_SIZE_T sz, c;
146   unsigned char magic;
147
148   if(!aligned_OK(mem)) return NULL;
149   p = mem2chunk(mem);
150   if( (char*)p>=mp_.sbrk_base &&
151       (char*)p<(mp_.sbrk_base+main_arena.system_mem) ) {
152     /* Must be a chunk in conventional heap memory. */
153     if(chunk_is_mmapped(p) ||
154        ( (sz = chunksize(p)),
155          ((char*)p + sz)>=(mp_.sbrk_base+main_arena.system_mem) ) ||
156        sz<MINSIZE || sz&MALLOC_ALIGN_MASK || !inuse(p) ||
157        ( !prev_inuse(p) && (p->prev_size&MALLOC_ALIGN_MASK ||
158                             (long)prev_chunk(p)<(long)mp_.sbrk_base ||
159                             next_chunk(prev_chunk(p))!=p) ))
160       return NULL;
161     magic = MAGICBYTE(p);
162     for(sz += SIZE_SZ-1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
163       if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
164     }
165     ((unsigned char*)p)[sz] ^= 0xFF;
166   } else {
167     unsigned long offset, page_mask = malloc_getpagesize-1;
168
169     /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two
170        alignment relative to the beginning of a page.  Check this
171        first. */
172     offset = (unsigned long)mem & page_mask;
173     if((offset!=MALLOC_ALIGNMENT && offset!=0 && offset!=0x10 &&
174         offset!=0x20 && offset!=0x40 && offset!=0x80 && offset!=0x100 &&
175         offset!=0x200 && offset!=0x400 && offset!=0x800 && offset!=0x1000 &&
176         offset<0x2000) ||
177        !chunk_is_mmapped(p) || (p->size & PREV_INUSE) ||
178        ( (((unsigned long)p - p->prev_size) & page_mask) != 0 ) ||
179        ( (sz = chunksize(p)), ((p->prev_size + sz) & page_mask) != 0 ) )
180       return NULL;
181     magic = MAGICBYTE(p);
182     for(sz -= 1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
183       if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
184     }
185     ((unsigned char*)p)[sz] ^= 0xFF;
186   }
187   return p;
188 }
189
190 /* Check for corruption of the top chunk, and try to recover if
191    necessary. */
192
193 static int
194 internal_function
195 #if __STD_C
196 top_check(void)
197 #else
198 top_check()
199 #endif
200 {
201   mchunkptr t = top(&main_arena);
202   char* brk, * new_brk;
203   INTERNAL_SIZE_T front_misalign, sbrk_size;
204   unsigned long pagesz = malloc_getpagesize;
205
206   if((char*)t + chunksize(t) == mp_.sbrk_base + main_arena.system_mem ||
207      t == initial_top(&main_arena)) return 0;
208
209   if(check_action & 1)
210     fprintf(stderr, "malloc: top chunk is corrupt\n");
211   if(check_action & 2)
212     abort();
213
214   /* Try to set up a new top chunk. */
215   brk = MORECORE(0);
216   front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK;
217   if (front_misalign > 0)
218     front_misalign = MALLOC_ALIGNMENT - front_misalign;
219   sbrk_size = front_misalign + mp_.top_pad + MINSIZE;
220   sbrk_size += pagesz - ((unsigned long)(brk + sbrk_size) & (pagesz - 1));
221   new_brk = (char*)(MORECORE (sbrk_size));
222   if (new_brk == (char*)(MORECORE_FAILURE)) return -1;
223   /* Call the `morecore' hook if necessary.  */
224   if (__after_morecore_hook)
225     (*__after_morecore_hook) ();
226   main_arena.system_mem = (new_brk - mp_.sbrk_base) + sbrk_size;
227
228   top(&main_arena) = (mchunkptr)(brk + front_misalign);
229   set_head(top(&main_arena), (sbrk_size - front_misalign) | PREV_INUSE);
230
231   return 0;
232 }
233
234 static Void_t*
235 #if __STD_C
236 malloc_check(size_t sz, const Void_t *caller)
237 #else
238 malloc_check(sz, caller) size_t sz; const Void_t *caller;
239 #endif
240 {
241   Void_t *victim;
242
243   (void)mutex_lock(&main_arena.mutex);
244   victim = (top_check() >= 0) ? _int_malloc(&main_arena, sz+1) : NULL;
245   (void)mutex_unlock(&main_arena.mutex);
246   return mem2mem_check(victim, sz);
247 }
248
249 #if HAVE_MMAP
250 /* forward declaration */
251 void
252 internal_function
253 #if __STD_C
254 munmap_chunk(mchunkptr p);
255 #else
256 munmap_chunk();
257 #endif
258 #endif
259
260 static void
261 #if __STD_C
262 free_check(Void_t* mem, const Void_t *caller)
263 #else
264 free_check(mem, caller) Void_t* mem; const Void_t *caller;
265 #endif
266 {
267   mchunkptr p;
268
269   if(!mem) return;
270   (void)mutex_lock(&main_arena.mutex);
271   p = mem2chunk_check(mem);
272   if(!p) {
273     (void)mutex_unlock(&main_arena.mutex);
274     if(check_action & 1)
275       fprintf(stderr, "free(): invalid pointer %p!\n", mem);
276     if(check_action & 2)
277       abort();
278     return;
279   }
280 #if HAVE_MMAP
281   if (chunk_is_mmapped(p)) {
282     (void)mutex_unlock(&main_arena.mutex);
283     munmap_chunk(p);
284     return;
285   }
286 #endif
287 #if 0 /* Erase freed memory. */
288   memset(mem, 0, chunksize(p) - (SIZE_SZ+1));
289 #endif
290   _int_free(&main_arena, mem);
291   (void)mutex_unlock(&main_arena.mutex);
292 }
293
294 static Void_t*
295 #if __STD_C
296 realloc_check(Void_t* oldmem, size_t bytes, const Void_t *caller)
297 #else
298 realloc_check(oldmem, bytes, caller)
299      Void_t* oldmem; size_t bytes; const Void_t *caller;
300 #endif
301 {
302   mchunkptr oldp;
303   INTERNAL_SIZE_T nb, oldsize;
304   Void_t* newmem = 0;
305
306   if (oldmem == 0) return malloc_check(bytes, NULL);
307   (void)mutex_lock(&main_arena.mutex);
308   oldp = mem2chunk_check(oldmem);
309   (void)mutex_unlock(&main_arena.mutex);
310   if(!oldp) {
311     if(check_action & 1)
312       fprintf(stderr, "realloc(): invalid pointer %p!\n", oldmem);
313     if(check_action & 2)
314       abort();
315     return malloc_check(bytes, NULL);
316   }
317   oldsize = chunksize(oldp);
318
319   checked_request2size(bytes+1, nb);
320   (void)mutex_lock(&main_arena.mutex);
321
322 #if HAVE_MMAP
323   if (chunk_is_mmapped(oldp)) {
324 #if HAVE_MREMAP
325     mchunkptr newp = mremap_chunk(oldp, nb);
326     if(newp)
327       newmem = chunk2mem(newp);
328     else
329 #endif
330     {
331       /* Note the extra SIZE_SZ overhead. */
332       if(oldsize - SIZE_SZ >= nb)
333         newmem = oldmem; /* do nothing */
334       else {
335         /* Must alloc, copy, free. */
336         if (top_check() >= 0)
337           newmem = _int_malloc(&main_arena, bytes+1);
338         if (newmem) {
339           MALLOC_COPY(BOUNDED_N(newmem, bytes+1), oldmem, oldsize - 2*SIZE_SZ);
340           munmap_chunk(oldp);
341         }
342       }
343     }
344   } else {
345 #endif /* HAVE_MMAP */
346     if (top_check() >= 0)
347       newmem = _int_realloc(&main_arena, oldmem, bytes+1);
348 #if 0 /* Erase freed memory. */
349     if(newmem)
350       newp = mem2chunk(newmem);
351     nb = chunksize(newp);
352     if(oldp<newp || oldp>=chunk_at_offset(newp, nb)) {
353       memset((char*)oldmem + 2*sizeof(mbinptr), 0,
354              oldsize - (2*sizeof(mbinptr)+2*SIZE_SZ+1));
355     } else if(nb > oldsize+SIZE_SZ) {
356       memset((char*)BOUNDED_N(chunk2mem(newp), bytes) + oldsize,
357              0, nb - (oldsize+SIZE_SZ));
358     }
359 #endif
360 #if HAVE_MMAP
361   }
362 #endif
363   (void)mutex_unlock(&main_arena.mutex);
364
365   return mem2mem_check(newmem, bytes);
366 }
367
368 static Void_t*
369 #if __STD_C
370 memalign_check(size_t alignment, size_t bytes, const Void_t *caller)
371 #else
372 memalign_check(alignment, bytes, caller)
373      size_t alignment; size_t bytes; const Void_t *caller;
374 #endif
375 {
376   INTERNAL_SIZE_T nb;
377   Void_t* mem;
378
379   if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
380   if (alignment <  MINSIZE) alignment = MINSIZE;
381
382   checked_request2size(bytes+1, nb);
383   (void)mutex_lock(&main_arena.mutex);
384   mem = (top_check() >= 0) ? _int_memalign(&main_arena, alignment, bytes+1) :
385     NULL;
386   (void)mutex_unlock(&main_arena.mutex);
387   return mem2mem_check(mem, bytes);
388 }
389
390 /* Activate a standard set of debugging hooks. */
391 void
392 __malloc_check_init()
393 {
394   if (disallow_malloc_check) {
395     disallow_malloc_check = 0;
396     return;
397   }
398   using_malloc_checking = 1;
399   __malloc_hook = malloc_check;
400   __free_hook = free_check;
401   __realloc_hook = realloc_check;
402   __memalign_hook = memalign_check;
403   if(check_action & 1)
404     fprintf(stderr, "malloc: using debugging hooks\n");
405 }
406
407 #ifndef NO_THREADS
408
409 # ifdef _LIBC
410 #  if USE___THREAD || (defined USE_TLS && !defined SHARED)
411     /* These routines are never needed in this configuration.  */
412 #   define NO_STARTER
413 #  endif
414 # endif
415
416 # ifdef NO_STARTER
417 #  undef NO_STARTER
418 # else
419
420 /* The following hooks are used when the global initialization in
421    ptmalloc_init() hasn't completed yet. */
422
423 static Void_t*
424 #if __STD_C
425 malloc_starter(size_t sz, const Void_t *caller)
426 #else
427 malloc_starter(sz, caller) size_t sz; const Void_t *caller;
428 #endif
429 {
430   Void_t* victim;
431
432   victim = _int_malloc(&main_arena, sz);
433
434   return victim ? BOUNDED_N(victim, sz) : 0;
435 }
436
437 static Void_t*
438 #if __STD_C
439 memalign_starter(size_t align, size_t sz, const Void_t *caller)
440 #else
441 memalign_starter(align, sz, caller) size_t align, sz; const Void_t *caller;
442 #endif
443 {
444   Void_t* victim;
445
446   victim = _int_memalign(&main_arena, align, sz);
447
448   return victim ? BOUNDED_N(victim, sz) : 0;
449 }
450
451 static void
452 #if __STD_C
453 free_starter(Void_t* mem, const Void_t *caller)
454 #else
455 free_starter(mem, caller) Void_t* mem; const Void_t *caller;
456 #endif
457 {
458   mchunkptr p;
459
460   if(!mem) return;
461   p = mem2chunk(mem);
462 #if HAVE_MMAP
463   if (chunk_is_mmapped(p)) {
464     munmap_chunk(p);
465     return;
466   }
467 #endif
468   _int_free(&main_arena, mem);
469 }
470
471 # endif /* !defiend NO_STARTER */
472 #endif /* NO_THREADS */
473
474
475 /* Get/set state: malloc_get_state() records the current state of all
476    malloc variables (_except_ for the actual heap contents and `hook'
477    function pointers) in a system dependent, opaque data structure.
478    This data structure is dynamically allocated and can be free()d
479    after use.  malloc_set_state() restores the state of all malloc
480    variables to the previously obtained state.  This is especially
481    useful when using this malloc as part of a shared library, and when
482    the heap contents are saved/restored via some other method.  The
483    primary example for this is GNU Emacs with its `dumping' procedure.
484    `Hook' function pointers are never saved or restored by these
485    functions, with two exceptions: If malloc checking was in use when
486    malloc_get_state() was called, then malloc_set_state() calls
487    __malloc_check_init() if possible; if malloc checking was not in
488    use in the recorded state but the user requested malloc checking,
489    then the hooks are reset to 0.  */
490
491 #define MALLOC_STATE_MAGIC   0x444c4541l
492 #define MALLOC_STATE_VERSION (0*0x100l + 2l) /* major*0x100 + minor */
493
494 struct malloc_save_state {
495   long          magic;
496   long          version;
497   mbinptr       av[NBINS * 2 + 2];
498   char*         sbrk_base;
499   int           sbrked_mem_bytes;
500   unsigned long trim_threshold;
501   unsigned long top_pad;
502   unsigned int  n_mmaps_max;
503   unsigned long mmap_threshold;
504   int           check_action;
505   unsigned long max_sbrked_mem;
506   unsigned long max_total_mem;
507   unsigned int  n_mmaps;
508   unsigned int  max_n_mmaps;
509   unsigned long mmapped_mem;
510   unsigned long max_mmapped_mem;
511   int           using_malloc_checking;
512 };
513
514 Void_t*
515 public_gET_STATe(void)
516 {
517   struct malloc_save_state* ms;
518   int i;
519   mbinptr b;
520
521   ms = (struct malloc_save_state*)public_mALLOc(sizeof(*ms));
522   if (!ms)
523     return 0;
524   (void)mutex_lock(&main_arena.mutex);
525   malloc_consolidate(&main_arena);
526   ms->magic = MALLOC_STATE_MAGIC;
527   ms->version = MALLOC_STATE_VERSION;
528   ms->av[0] = 0;
529   ms->av[1] = 0; /* used to be binblocks, now no longer used */
530   ms->av[2] = top(&main_arena);
531   ms->av[3] = 0; /* used to be undefined */
532   for(i=1; i<NBINS; i++) {
533     b = bin_at(&main_arena, i);
534     if(first(b) == b)
535       ms->av[2*i+2] = ms->av[2*i+3] = 0; /* empty bin */
536     else {
537       ms->av[2*i+2] = first(b);
538       ms->av[2*i+3] = last(b);
539     }
540   }
541   ms->sbrk_base = mp_.sbrk_base;
542   ms->sbrked_mem_bytes = main_arena.system_mem;
543   ms->trim_threshold = mp_.trim_threshold;
544   ms->top_pad = mp_.top_pad;
545   ms->n_mmaps_max = mp_.n_mmaps_max;
546   ms->mmap_threshold = mp_.mmap_threshold;
547   ms->check_action = check_action;
548   ms->max_sbrked_mem = main_arena.max_system_mem;
549 #ifdef NO_THREADS
550   ms->max_total_mem = mp_.max_total_mem;
551 #else
552   ms->max_total_mem = 0;
553 #endif
554   ms->n_mmaps = mp_.n_mmaps;
555   ms->max_n_mmaps = mp_.max_n_mmaps;
556   ms->mmapped_mem = mp_.mmapped_mem;
557   ms->max_mmapped_mem = mp_.max_mmapped_mem;
558   ms->using_malloc_checking = using_malloc_checking;
559   (void)mutex_unlock(&main_arena.mutex);
560   return (Void_t*)ms;
561 }
562
563 int
564 public_sET_STATe(Void_t* msptr)
565 {
566   struct malloc_save_state* ms = (struct malloc_save_state*)msptr;
567   size_t i;
568   mbinptr b;
569
570   disallow_malloc_check = 1;
571   ptmalloc_init();
572   if(ms->magic != MALLOC_STATE_MAGIC) return -1;
573   /* Must fail if the major version is too high. */
574   if((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) return -2;
575   (void)mutex_lock(&main_arena.mutex);
576   /* There are no fastchunks.  */
577   clear_fastchunks(&main_arena);
578   set_max_fast(&main_arena, DEFAULT_MXFAST);
579   for (i=0; i<NFASTBINS; ++i)
580     main_arena.fastbins[i] = 0;
581   for (i=0; i<BINMAPSIZE; ++i)
582     main_arena.binmap[i] = 0;
583   top(&main_arena) = ms->av[2];
584   main_arena.last_remainder = 0;
585   for(i=1; i<NBINS; i++) {
586     b = bin_at(&main_arena, i);
587     if(ms->av[2*i+2] == 0) {
588       assert(ms->av[2*i+3] == 0);
589       first(b) = last(b) = b;
590     } else {
591       if(i<NSMALLBINS || (largebin_index(chunksize(ms->av[2*i+2]))==i &&
592                           largebin_index(chunksize(ms->av[2*i+3]))==i)) {
593         first(b) = ms->av[2*i+2];
594         last(b) = ms->av[2*i+3];
595         /* Make sure the links to the bins within the heap are correct.  */
596         first(b)->bk = b;
597         last(b)->fd = b;
598         /* Set bit in binblocks.  */
599         mark_bin(&main_arena, i);
600       } else {
601         /* Oops, index computation from chunksize must have changed.
602            Link the whole list into unsorted_chunks.  */
603         first(b) = last(b) = b;
604         b = unsorted_chunks(&main_arena);
605         ms->av[2*i+2]->bk = b;
606         ms->av[2*i+3]->fd = b->fd;
607         b->fd->bk = ms->av[2*i+3];
608         b->fd = ms->av[2*i+2];
609       }
610     }
611   }
612   mp_.sbrk_base = ms->sbrk_base;
613   main_arena.system_mem = ms->sbrked_mem_bytes;
614   mp_.trim_threshold = ms->trim_threshold;
615   mp_.top_pad = ms->top_pad;
616   mp_.n_mmaps_max = ms->n_mmaps_max;
617   mp_.mmap_threshold = ms->mmap_threshold;
618   check_action = ms->check_action;
619   main_arena.max_system_mem = ms->max_sbrked_mem;
620 #ifdef NO_THREADS
621   mp_.max_total_mem = ms->max_total_mem;
622 #endif
623   mp_.n_mmaps = ms->n_mmaps;
624   mp_.max_n_mmaps = ms->max_n_mmaps;
625   mp_.mmapped_mem = ms->mmapped_mem;
626   mp_.max_mmapped_mem = ms->max_mmapped_mem;
627   /* add version-dependent code here */
628   if (ms->version >= 1) {
629     /* Check whether it is safe to enable malloc checking, or whether
630        it is necessary to disable it.  */
631     if (ms->using_malloc_checking && !using_malloc_checking &&
632         !disallow_malloc_check)
633       __malloc_check_init ();
634     else if (!ms->using_malloc_checking && using_malloc_checking) {
635       __malloc_hook = 0;
636       __free_hook = 0;
637       __realloc_hook = 0;
638       __memalign_hook = 0;
639       using_malloc_checking = 0;
640     }
641   }
642   check_malloc_state(&main_arena);
643
644   (void)mutex_unlock(&main_arena.mutex);
645   return 0;
646 }
647
648 /*
649  * Local variables:
650  * c-basic-offset: 2
651  * End:
652  */