Limit DEFAULT_MMAP_THRESHOLD_MAX even further too keep 1M alignment
[kopensolaris-gnu/glibc.git] / malloc / arena.c
index 8324b68..6f4b0c4 100644 (file)
@@ -1,30 +1,34 @@
 /* Malloc implementation for multiple threads without lock contention.
-   Copyright (C) 2001 Free Software Foundation, Inc.
+   Copyright (C) 2001,2002,2003,2004,2005,2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
 
    The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
    License, or (at your option) any later version.
 
    The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
+   You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; see the file COPYING.LIB.  If not,
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
-/* $Id$ */
+#include <stdbool.h>
 
 /* Compile-time constants.  */
 
 #define HEAP_MIN_SIZE (32*1024)
 #ifndef HEAP_MAX_SIZE
-#define HEAP_MAX_SIZE (1024*1024) /* must be a power of two */
+# ifdef DEFAULT_MMAP_THRESHOLD_MAX
+#  define HEAP_MAX_SIZE (2 * DEFAULT_MMAP_THRESHOLD_MAX)
+# else
+#  define HEAP_MAX_SIZE (1024*1024) /* must be a power of two */
+# endif
 #endif
 
 /* HEAP_MIN_SIZE and HEAP_MAX_SIZE limit the size of mmap()ed heaps
@@ -55,9 +59,18 @@ typedef struct _heap_info {
   mstate ar_ptr; /* Arena for this heap. */
   struct _heap_info *prev; /* Previous heap. */
   size_t size;   /* Current size in bytes. */
-  size_t pad;    /* Make sure the following data is properly aligned. */
+  /* Make sure the following data is properly aligned, particularly
+     that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
+     MALLOG_ALIGNMENT. */
+  char pad[-5 * SIZE_SZ & MALLOC_ALIGN_MASK];
 } heap_info;
 
+/* Get a compile-time error if the heap_info padding is not correct
+   to make alignment work as expected in sYSMALLOc.  */
+extern int sanity_check_heap_info_alignment[(sizeof (heap_info)
+                                            + 2 * SIZE_SZ) % MALLOC_ALIGNMENT
+                                           ? -1 : 1];
+
 /* Thread specific data */
 
 static tsd_key_t arena_key;
@@ -73,6 +86,9 @@ static int stat_n_heaps;
 /* Mapped memory in non-main arenas (reliable only for NO_THREADS). */
 static unsigned long arena_mem;
 
+/* Already initialized? */
+int __malloc_initialized = -1;
+
 /**************************************************************************/
 
 #if USE_ARENAS
@@ -131,10 +147,14 @@ static unsigned long arena_mem;
 
 /* atfork support.  */
 
-static __malloc_ptr_t (*save_malloc_hook) __MALLOC_P ((size_t __size,
-                                                      __const __malloc_ptr_t));
-static void           (*save_free_hook) __MALLOC_P ((__malloc_ptr_t __ptr,
-                                                    __const __malloc_ptr_t));
+static __malloc_ptr_t (*save_malloc_hook) (size_t __size,
+                                          __const __malloc_ptr_t);
+# if !defined _LIBC || !defined USE_TLS || (defined SHARED && !USE___THREAD)
+static __malloc_ptr_t (*save_memalign_hook) (size_t __align, size_t __size,
+                                            __const __malloc_ptr_t);
+# endif
+static void           (*save_free_hook) (__malloc_ptr_t __ptr,
+                                        __const __malloc_ptr_t);
 static Void_t*        save_arena;
 
 /* Magic value for the thread-specific arena pointer when
@@ -201,6 +221,10 @@ free_atfork(Void_t* mem, const Void_t *caller)
     (void)mutex_unlock(&ar_ptr->mutex);
 }
 
+
+/* Counter for number of times the list is locked by the same thread.  */
+static unsigned int atfork_recursive_cntr;
+
 /* The following two functions are registered via thread_atfork() to
    make sure that the mutexes remain in a consistent state in the
    fork()ed version of a thread.  Also adapt the malloc and free hooks
@@ -208,11 +232,24 @@ free_atfork(Void_t* mem, const Void_t *caller)
    malloc/free internally (e.g. in LinuxThreads). */
 
 static void
-ptmalloc_lock_all __MALLOC_P((void))
+ptmalloc_lock_all (void)
 {
   mstate ar_ptr;
 
-  (void)mutex_lock(&list_lock);
+  if(__malloc_initialized < 1)
+    return;
+  if (mutex_trylock(&list_lock))
+    {
+      Void_t *my_arena;
+      tsd_getspecific(arena_key, my_arena);
+      if (my_arena == ATFORK_ARENA_PTR)
+       /* This is the same thread which already locks the global list.
+          Just bump the counter.  */
+       goto out;
+
+      /* This thread has to wait its turn.  */
+      (void)mutex_lock(&list_lock);
+    }
   for(ar_ptr = &main_arena;;) {
     (void)mutex_lock(&ar_ptr->mutex);
     ar_ptr = ar_ptr->next;
@@ -225,13 +262,19 @@ ptmalloc_lock_all __MALLOC_P((void))
   /* Only the current thread may perform malloc/free calls now. */
   tsd_getspecific(arena_key, save_arena);
   tsd_setspecific(arena_key, ATFORK_ARENA_PTR);
+ out:
+  ++atfork_recursive_cntr;
 }
 
 static void
-ptmalloc_unlock_all __MALLOC_P((void))
+ptmalloc_unlock_all (void)
 {
   mstate ar_ptr;
 
+  if(__malloc_initialized < 1)
+    return;
+  if (--atfork_recursive_cntr != 0)
+    return;
   tsd_setspecific(arena_key, save_arena);
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
@@ -245,27 +288,30 @@ ptmalloc_unlock_all __MALLOC_P((void))
 
 #ifdef __linux__
 
-/* In LinuxThreads, unlocking a mutex in the child process after a
+/* In NPTL, unlocking a mutex in the child process after a
    fork() is currently unsafe, whereas re-initializing it is safe and
    does not leak resources.  Therefore, a special atfork handler is
    installed for the child. */
 
 static void
-ptmalloc_unlock_all2 __MALLOC_P((void))
+ptmalloc_unlock_all2 (void)
 {
   mstate ar_ptr;
 
+  if(__malloc_initialized < 1)
+    return;
 #if defined _LIBC || defined MALLOC_HOOKS
   tsd_setspecific(arena_key, save_arena);
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
 #endif
   for(ar_ptr = &main_arena;;) {
-    (void)mutex_init(&ar_ptr->mutex);
+    mutex_init(&ar_ptr->mutex);
     ar_ptr = ar_ptr->next;
     if(ar_ptr == &main_arena) break;
   }
-  (void)mutex_init(&list_lock);
+  mutex_init(&list_lock);
+  atfork_recursive_cntr = 0;
 }
 
 #else
@@ -276,9 +322,6 @@ ptmalloc_unlock_all2 __MALLOC_P((void))
 
 #endif /* !defined NO_THREADS */
 
-/* Already initialized? */
-int __malloc_initialized = -1;
-
 /* Initialization routine. */
 #ifdef _LIBC
 #include <string.h>
@@ -316,8 +359,63 @@ next_env_entry (char ***position)
 }
 #endif /* _LIBC */
 
+/* Set up basic state so that _int_malloc et al can work.  */
 static void
-ptmalloc_init __MALLOC_P((void))
+ptmalloc_init_minimal (void)
+{
+#if DEFAULT_TOP_PAD != 0
+  mp_.top_pad        = DEFAULT_TOP_PAD;
+#endif
+  mp_.n_mmaps_max    = DEFAULT_MMAP_MAX;
+  mp_.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
+  mp_.trim_threshold = DEFAULT_TRIM_THRESHOLD;
+  mp_.pagesize       = malloc_getpagesize;
+}
+
+
+#ifdef _LIBC
+# ifdef SHARED
+static void *
+__failing_morecore (ptrdiff_t d)
+{
+  return (void *) MORECORE_FAILURE;
+}
+
+extern struct dl_open_hook *_dl_open_hook;
+libc_hidden_proto (_dl_open_hook);
+# endif
+
+# if defined SHARED && defined USE_TLS && !USE___THREAD
+/* This is called by __pthread_initialize_minimal when it needs to use
+   malloc to set up the TLS state.  We cannot do the full work of
+   ptmalloc_init (below) until __pthread_initialize_minimal has finished,
+   so it has to switch to using the special startup-time hooks while doing
+   those allocations.  */
+void
+__libc_malloc_pthread_startup (bool first_time)
+{
+  if (first_time)
+    {
+      ptmalloc_init_minimal ();
+      save_malloc_hook = __malloc_hook;
+      save_memalign_hook = __memalign_hook;
+      save_free_hook = __free_hook;
+      __malloc_hook = malloc_starter;
+      __memalign_hook = memalign_starter;
+      __free_hook = free_starter;
+    }
+  else
+    {
+      __malloc_hook = save_malloc_hook;
+      __memalign_hook = save_memalign_hook;
+      __free_hook = save_free_hook;
+    }
+}
+# endif
+#endif
+
+static void
+ptmalloc_init (void)
 {
 #if __STD_C
   const char* s;
@@ -329,83 +427,118 @@ ptmalloc_init __MALLOC_P((void))
   if(__malloc_initialized >= 0) return;
   __malloc_initialized = 0;
 
-  mp_.top_pad        = DEFAULT_TOP_PAD;
-  mp_.n_mmaps_max    = DEFAULT_MMAP_MAX;
-  mp_.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
-  mp_.trim_threshold = DEFAULT_TRIM_THRESHOLD;
-  mp_.pagesize       = malloc_getpagesize;
+#ifdef _LIBC
+# if defined SHARED && defined USE_TLS && !USE___THREAD
+  /* ptmalloc_init_minimal may already have been called via
+     __libc_malloc_pthread_startup, above.  */
+  if (mp_.pagesize == 0)
+# endif
+#endif
+    ptmalloc_init_minimal();
 
 #ifndef NO_THREADS
+# if defined _LIBC && defined USE_TLS
+  /* We know __pthread_initialize_minimal has already been called,
+     and that is enough.  */
+#   define NO_STARTER
+# endif
+# ifndef NO_STARTER
   /* With some threads implementations, creating thread-specific data
      or initializing a mutex may call malloc() itself.  Provide a
      simple starter version (realloc() won't work). */
   save_malloc_hook = __malloc_hook;
+  save_memalign_hook = __memalign_hook;
   save_free_hook = __free_hook;
   __malloc_hook = malloc_starter;
+  __memalign_hook = memalign_starter;
   __free_hook = free_starter;
-#ifdef _LIBC
+#  ifdef _LIBC
   /* Initialize the pthreads interface. */
   if (__pthread_initialize != NULL)
     __pthread_initialize();
-#endif
+#  endif /* !defined _LIBC */
+# endif        /* !defined NO_STARTER */
 #endif /* !defined NO_THREADS */
   mutex_init(&main_arena.mutex);
   main_arena.next = &main_arena;
 
+#if defined _LIBC && defined SHARED
+  /* In case this libc copy is in a non-default namespace, never use brk.
+     Likewise if dlopened from statically linked program.  */
+  Dl_info di;
+  struct link_map *l;
+
+  if (_dl_open_hook != NULL
+      || (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
+         && l->l_ns != LM_ID_BASE))
+    __morecore = __failing_morecore;
+#endif
+
   mutex_init(&list_lock);
   tsd_key_create(&arena_key, NULL);
   tsd_setspecific(arena_key, (Void_t *)&main_arena);
   thread_atfork(ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
 #ifndef NO_THREADS
+# ifndef NO_STARTER
   __malloc_hook = save_malloc_hook;
+  __memalign_hook = save_memalign_hook;
   __free_hook = save_free_hook;
+# else
+#  undef NO_STARTER
+# endif
 #endif
 #ifdef _LIBC
   secure = __libc_enable_secure;
   s = NULL;
-  {
-    char **runp = _environ;
-    char *envline;
-
-    while (__builtin_expect ((envline = next_env_entry (&runp)) != NULL,
-                            0))
-      {
-       size_t len = strcspn (envline, "=");
-
-       if (envline[len] != '=')
-         /* This is a "MALLOC_" variable at the end of the string
-            without a '=' character.  Ignore it since otherwise we
-            will access invalid memory below.  */
-         continue;
-
-       switch (len)
-         {
-         case 6:
-           if (memcmp (envline, "CHECK_", 6) == 0)
-             s = &envline[7];
-           break;
-         case 8:
-           if (! secure && memcmp (envline, "TOP_PAD_", 8) == 0)
-             mALLOPt(M_TOP_PAD, atoi(&envline[9]));
-           break;
-         case 9:
-           if (! secure && memcmp (envline, "MMAP_MAX_", 9) == 0)
-             mALLOPt(M_MMAP_MAX, atoi(&envline[10]));
-           break;
-         case 15:
-           if (! secure)
-             {
-               if (memcmp (envline, "TRIM_THRESHOLD_", 15) == 0)
-                 mALLOPt(M_TRIM_THRESHOLD, atoi(&envline[16]));
-               else if (memcmp (envline, "MMAP_THRESHOLD_", 15) == 0)
-                 mALLOPt(M_MMAP_THRESHOLD, atoi(&envline[16]));
-             }
-           break;
-         default:
-           break;
-         }
-      }
-  }
+  if (__builtin_expect (_environ != NULL, 1))
+    {
+      char **runp = _environ;
+      char *envline;
+
+      while (__builtin_expect ((envline = next_env_entry (&runp)) != NULL,
+                              0))
+       {
+         size_t len = strcspn (envline, "=");
+
+         if (envline[len] != '=')
+           /* This is a "MALLOC_" variable at the end of the string
+              without a '=' character.  Ignore it since otherwise we
+              will access invalid memory below.  */
+           continue;
+
+         switch (len)
+           {
+           case 6:
+             if (memcmp (envline, "CHECK_", 6) == 0)
+               s = &envline[7];
+             break;
+           case 8:
+             if (! secure)
+               {
+                 if (memcmp (envline, "TOP_PAD_", 8) == 0)
+                   mALLOPt(M_TOP_PAD, atoi(&envline[9]));
+                 else if (memcmp (envline, "PERTURB_", 8) == 0)
+                   mALLOPt(M_PERTURB, atoi(&envline[9]));
+               }
+             break;
+           case 9:
+             if (! secure && memcmp (envline, "MMAP_MAX_", 9) == 0)
+               mALLOPt(M_MMAP_MAX, atoi(&envline[10]));
+             break;
+           case 15:
+             if (! secure)
+               {
+                 if (memcmp (envline, "TRIM_THRESHOLD_", 15) == 0)
+                   mALLOPt(M_TRIM_THRESHOLD, atoi(&envline[16]));
+                 else if (memcmp (envline, "MMAP_THRESHOLD_", 15) == 0)
+                   mALLOPt(M_MMAP_THRESHOLD, atoi(&envline[16]));
+               }
+             break;
+           default:
+             break;
+           }
+       }
+    }
 #else
   if (! secure)
     {
@@ -413,6 +546,8 @@ ptmalloc_init __MALLOC_P((void))
        mALLOPt(M_TRIM_THRESHOLD, atoi(s));
       if((s = getenv("MALLOC_TOP_PAD_")))
        mALLOPt(M_TOP_PAD, atoi(s));
+      if((s = getenv("MALLOC_PERTURB_")))
+       mALLOPt(M_PERTURB, atoi(s));
       if((s = getenv("MALLOC_MMAP_THRESHOLD_")))
        mALLOPt(M_MMAP_THRESHOLD, atoi(s));
       if((s = getenv("MALLOC_MMAP_MAX_")))
@@ -420,9 +555,10 @@ ptmalloc_init __MALLOC_P((void))
     }
   s = getenv("MALLOC_CHECK_");
 #endif
-  if(s) {
-    if(s[0]) mALLOPt(M_CHECK_ACTION, (int)(s[0] - '0'));
-    __malloc_check_init();
+  if(s && s[0]) {
+    mALLOPt(M_CHECK_ACTION, (int)(s[0] - '0'));
+    if (check_action != 0)
+      __malloc_check_init();
   }
   if(__malloc_initialize_hook != NULL)
     (*__malloc_initialize_hook)();
@@ -476,6 +612,16 @@ dump_heap(heap) heap_info *heap;
 
 #endif /* MALLOC_DEBUG > 1 */
 
+/* If consecutive mmap (0, HEAP_MAX_SIZE << 1, ...) calls return decreasing
+   addresses as opposed to increasing, new_heap would badly fragment the
+   address space.  In that case remember the second HEAP_MAX_SIZE part
+   aligned to HEAP_MAX_SIZE from last mmap (0, HEAP_MAX_SIZE << 1, ...)
+   call (if it is already aligned) and try to reuse it next time.  We need
+   no locking for it, as kernel ensures the atomicity for us - worst case
+   we'll call mmap (addr, HEAP_MAX_SIZE, ...) for some value of addr in
+   multiple threads, but only one will succeed.  */
+static char *aligned_heap_area;
+
 /* Create a new heap.  size is automatically rounded up to a multiple
    of the page size. */
 
@@ -506,21 +652,38 @@ new_heap(size, top_pad) size_t size, top_pad;
      No swap space needs to be reserved for the following large
      mapping (on Linux, this is the case for all non-writable mappings
      anyway). */
-  p1 = (char *)MMAP(0, HEAP_MAX_SIZE<<1, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
-  if(p1 != MAP_FAILED) {
-    p2 = (char *)(((unsigned long)p1 + (HEAP_MAX_SIZE-1)) & ~(HEAP_MAX_SIZE-1));
-    ul = p2 - p1;
-    munmap(p1, ul);
-    munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul);
-  } else {
-    /* Try to take the chance that an allocation of only HEAP_MAX_SIZE
-       is already aligned. */
-    p2 = (char *)MMAP(0, HEAP_MAX_SIZE, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
-    if(p2 == MAP_FAILED)
-      return 0;
-    if((unsigned long)p2 & (HEAP_MAX_SIZE-1)) {
+  p2 = MAP_FAILED;
+  if(aligned_heap_area) {
+    p2 = (char *)MMAP(aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE,
+                     MAP_PRIVATE|MAP_NORESERVE);
+    aligned_heap_area = NULL;
+    if (p2 != MAP_FAILED && ((unsigned long)p2 & (HEAP_MAX_SIZE-1))) {
       munmap(p2, HEAP_MAX_SIZE);
-      return 0;
+      p2 = MAP_FAILED;
+    }
+  }
+  if(p2 == MAP_FAILED) {
+    p1 = (char *)MMAP(0, HEAP_MAX_SIZE<<1, PROT_NONE,
+                     MAP_PRIVATE|MAP_NORESERVE);
+    if(p1 != MAP_FAILED) {
+      p2 = (char *)(((unsigned long)p1 + (HEAP_MAX_SIZE-1))
+                   & ~(HEAP_MAX_SIZE-1));
+      ul = p2 - p1;
+      if (ul)
+       munmap(p1, ul);
+      else
+       aligned_heap_area = p2 + HEAP_MAX_SIZE;
+      munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul);
+    } else {
+      /* Try to take the chance that an allocation of only HEAP_MAX_SIZE
+        is already aligned. */
+      p2 = (char *)MMAP(0, HEAP_MAX_SIZE, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
+      if(p2 == MAP_FAILED)
+       return 0;
+      if((unsigned long)p2 & (HEAP_MAX_SIZE-1)) {
+       munmap(p2, HEAP_MAX_SIZE);
+       return 0;
+      }
     }
   }
   if(mprotect(p2, size, PROT_READ|PROT_WRITE) != 0) {
@@ -570,7 +733,12 @@ grow_heap(h, diff) heap_info *h; long diff;
 
 /* Delete a heap. */
 
-#define delete_heap(heap) munmap((char*)(heap), HEAP_MAX_SIZE)
+#define delete_heap(heap) \
+  do {                                                         \
+    if ((char *)(heap) + HEAP_MAX_SIZE == aligned_heap_area)   \
+      aligned_heap_area = NULL;                                        \
+    munmap((char*)(heap), HEAP_MAX_SIZE);                      \
+  } while (0)
 
 static int
 internal_function
@@ -629,6 +797,48 @@ heap_trim(heap, pad) heap_info *heap; size_t pad;
   return 1;
 }
 
+/* Create a new arena with initial size "size".  */
+
+static mstate
+_int_new_arena(size_t size)
+{
+  mstate a;
+  heap_info *h;
+  char *ptr;
+  unsigned long misalign;
+
+  h = new_heap(size + (sizeof(*h) + sizeof(*a) + MALLOC_ALIGNMENT),
+              mp_.top_pad);
+  if(!h) {
+    /* Maybe size is too large to fit in a single heap.  So, just try
+       to create a minimally-sized arena and let _int_malloc() attempt
+       to deal with the large request via mmap_chunk().  */
+    h = new_heap(sizeof(*h) + sizeof(*a) + MALLOC_ALIGNMENT, mp_.top_pad);
+    if(!h)
+      return 0;
+  }
+  a = h->ar_ptr = (mstate)(h+1);
+  malloc_init_state(a);
+  /*a->next = NULL;*/
+  a->system_mem = a->max_system_mem = h->size;
+  arena_mem += h->size;
+#ifdef NO_THREADS
+  if((unsigned long)(mp_.mmapped_mem + arena_mem + main_arena.system_mem) >
+     mp_.max_total_mem)
+    mp_.max_total_mem = mp_.mmapped_mem + arena_mem + main_arena.system_mem;
+#endif
+
+  /* Set up the top chunk, with proper alignment. */
+  ptr = (char *)(a + 1);
+  misalign = (unsigned long)chunk2mem(ptr) & MALLOC_ALIGN_MASK;
+  if (misalign > 0)
+    ptr += MALLOC_ALIGNMENT - misalign;
+  top(a) = (mchunkptr)ptr;
+  set_head(top(a), (((char*)h + h->size) - ptr) | PREV_INUSE);
+
+  return a;
+}
+
 static mstate
 internal_function
 #if __STD_C
@@ -638,7 +848,6 @@ arena_get2(a_tsd, size) mstate a_tsd; size_t size;
 #endif
 {
   mstate a;
-  int err;
 
   if(!a_tsd)
     a = a_tsd = &main_arena;
@@ -653,9 +862,12 @@ arena_get2(a_tsd, size) mstate a_tsd; size_t size;
   }
 
   /* Check the global, circularly linked list for available arenas. */
+  bool retried = false;
  repeat:
   do {
     if(!mutex_trylock(&a->mutex)) {
+      if (retried)
+       (void)mutex_unlock(&list_lock);
       THREAD_STAT(++(a->stat_lock_loop));
       tsd_setspecific(arena_key, (Void_t *)a);
       return a;
@@ -667,73 +879,33 @@ arena_get2(a_tsd, size) mstate a_tsd; size_t size;
      happen during `atfork', or for example on systems where thread
      creation makes it temporarily impossible to obtain _any_
      locks. */
-  if(mutex_trylock(&list_lock)) {
+  if(!retried && mutex_trylock(&list_lock)) {
+    /* We will block to not run in a busy loop.  */
+    (void)mutex_lock(&list_lock);
+
+    /* Since we blocked there might be an arena available now.  */
+    retried = true;
     a = a_tsd;
     goto repeat;
   }
-  (void)mutex_unlock(&list_lock);
 
   /* Nothing immediately available, so generate a new arena.  */
   a = _int_new_arena(size);
-  if(!a)
-    return 0;
+  if(a)
+    {
+      tsd_setspecific(arena_key, (Void_t *)a);
+      mutex_init(&a->mutex);
+      mutex_lock(&a->mutex); /* remember result */
 
-  tsd_setspecific(arena_key, (Void_t *)a);
-  mutex_init(&a->mutex);
-  err = mutex_lock(&a->mutex); /* remember result */
+      /* Add the new arena to the global list.  */
+      a->next = main_arena.next;
+      atomic_write_barrier ();
+      main_arena.next = a;
 
-  /* Add the new arena to the global list.  */
-  (void)mutex_lock(&list_lock);
-  a->next = main_arena.next;
-  main_arena.next = a;
+      THREAD_STAT(++(a->stat_lock_loop));
+    }
   (void)mutex_unlock(&list_lock);
 
-  if(err) /* locking failed; keep arena for further attempts later */
-    return 0;
-
-  THREAD_STAT(++(a->stat_lock_loop));
-  return a;
-}
-
-/* Create a new arena with initial size "size".  */
-
-mstate
-_int_new_arena(size_t size)
-{
-  mstate a;
-  heap_info *h;
-  char *ptr;
-  unsigned long misalign;
-
-  h = new_heap(size + (sizeof(*h) + sizeof(*a) + MALLOC_ALIGNMENT),
-              mp_.top_pad);
-  if(!h) {
-    /* Maybe size is too large to fit in a single heap.  So, just try
-       to create a minimally-sized arena and let _int_malloc() attempt
-       to deal with the large request via mmap_chunk().  */
-    h = new_heap(sizeof(*h) + sizeof(*a) + MALLOC_ALIGNMENT, mp_.top_pad);
-    if(!h)
-      return 0;
-  }
-  a = h->ar_ptr = (mstate)(h+1);
-  malloc_init_state(a);
-  /*a->next = NULL;*/
-  a->system_mem = a->max_system_mem = h->size;
-  arena_mem += h->size;
-#ifdef NO_THREADS
-  if((unsigned long)(mp_.mmapped_mem + arena_mem + main_arena.system_mem) >
-     mp_.max_total_mem)
-    mp_.max_total_mem = mp_.mmapped_mem + arena_mem + main_arena.system_mem;
-#endif
-
-  /* Set up the top chunk, with proper alignment. */
-  ptr = (char *)(a + 1);
-  misalign = (unsigned long)chunk2mem(ptr) & MALLOC_ALIGN_MASK;
-  if (misalign > 0)
-    ptr += MALLOC_ALIGNMENT - misalign;
-  top(a) = (mchunkptr)ptr;
-  set_head(top(a), (((char*)h + h->size) - ptr) | PREV_INUSE);
-
   return a;
 }