about summary refs log tree commit diff
path: root/malloc
diff options
context:
space:
mode:
Diffstat (limited to 'malloc')
-rw-r--r--malloc/malloc.c124
-rw-r--r--malloc/thread-m.h21
2 files changed, 143 insertions, 2 deletions
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 1a5c886b6c..fb51483279 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1185,6 +1185,8 @@ static Void_t*   realloc_check(Void_t* oldmem, size_t bytes);
 static Void_t*   memalign_check(size_t alignment, size_t bytes);
 static Void_t*   malloc_starter(size_t sz);
 static void      free_starter(Void_t* mem);
+static Void_t*   malloc_atfork(size_t sz);
+static void      free_atfork(Void_t* mem);
 #endif
 
 #else
@@ -1204,6 +1206,8 @@ static Void_t*   realloc_check();
 static Void_t*   memalign_check();
 static Void_t*   malloc_starter();
 static void      free_starter();
+static Void_t*   malloc_atfork();
+static void      free_atfork();
 #endif
 
 #endif
@@ -1510,6 +1514,58 @@ static unsigned long max_mmapped_mem = 0;
 int __malloc_initialized = 0;
 
 
+/* 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
+   temporarily, because the `atfork' handler mechanism may use
+   malloc/free internally (e.g. in LinuxThreads). */
+
+#if defined(_LIBC) || defined(MALLOC_HOOKS)
+static __malloc_ptr_t (*save_malloc_hook) __MALLOC_P ((size_t __size));
+static void           (*save_free_hook) __MALLOC_P ((__malloc_ptr_t __ptr));
+static Void_t*        save_arena;
+#endif
+
+static void
+ptmalloc_lock_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
+
+  (void)mutex_lock(&list_lock);
+  for(ar_ptr = &main_arena;;) {
+    (void)mutex_lock(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+#if defined(_LIBC) || defined(MALLOC_HOOKS)
+  save_malloc_hook = __malloc_hook;
+  save_free_hook = __free_hook;
+  __malloc_hook = malloc_atfork;
+  __free_hook = free_atfork;
+  /* Only the current thread may perform malloc/free calls now. */
+  tsd_getspecific(arena_key, save_arena);
+  tsd_setspecific(arena_key, (Void_t*)0);
+#endif
+}
+
+static void
+ptmalloc_unlock_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
+
+#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_unlock(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+  (void)mutex_unlock(&list_lock);
+}
+
 /* Initialization routine. */
 #if defined(_LIBC)
 #if 0
@@ -1524,8 +1580,6 @@ ptmalloc_init __MALLOC_P((void))
 #endif
 {
 #if defined(_LIBC) || defined(MALLOC_HOOKS)
-  __malloc_ptr_t (*save_malloc_hook) __MALLOC_P ((size_t __size));
-  void (*save_free_hook) __MALLOC_P ((__malloc_ptr_t __ptr));
   const char* s;
 #endif
 
@@ -1550,6 +1604,7 @@ ptmalloc_init __MALLOC_P((void))
   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_all);
 #endif
 #if defined(_LIBC) || defined(MALLOC_HOOKS)
   if((s = getenv("MALLOC_TRIM_THRESHOLD_")))
@@ -1572,6 +1627,12 @@ ptmalloc_init __MALLOC_P((void))
 #endif
 }
 
+/* There are platforms (e.g. Hurd) with a link-time hook mechanism. */
+#ifdef thread_atfork_static
+thread_atfork_static(ptmalloc_lock_all, ptmalloc_unlock_all, \
+                     ptmalloc_unlock_all)
+#endif
+
 #if defined(_LIBC) || defined(MALLOC_HOOKS)
 
 /* Hooks for debugging versions.  The initial hooks just call the
@@ -4240,6 +4301,65 @@ free_starter(mem) Void_t* mem;
   chunk_free(&main_arena, p);
 }
 
+/* The following hooks are used while the `atfork' handling mechanism
+   is active. */
+
+static Void_t*
+#if __STD_C
+malloc_atfork(size_t sz)
+#else
+malloc_atfork(sz) size_t sz;
+#endif
+{
+  Void_t *vptr = NULL;
+
+  tsd_getspecific(arena_key, vptr);
+  if(!vptr) {
+    mchunkptr victim = chunk_alloc(&main_arena, request2size(sz));
+    return victim ? chunk2mem(victim) : 0;
+  } else {
+    /* Suspend the thread until the `atfork' handlers have completed.
+       By that time, the hooks will have been reset as well, so that
+       mALLOc() can be used again. */
+    (void)mutex_lock(&list_lock);
+    (void)mutex_unlock(&list_lock);
+    return mALLOc(sz);
+  }
+}
+
+static void
+#if __STD_C
+free_atfork(Void_t* mem)
+#else
+free_atfork(mem) Void_t* mem;
+#endif
+{
+  Void_t *vptr = NULL;
+  arena *ar_ptr;
+  mchunkptr p;                          /* chunk corresponding to mem */
+
+  if (mem == 0)                              /* free(0) has no effect */
+    return;
+
+  p = mem2chunk(mem);
+
+#if HAVE_MMAP
+  if (chunk_is_mmapped(p))                       /* release mmapped memory. */
+  {
+    munmap_chunk(p);
+    return;
+  }
+#endif
+
+  ar_ptr = arena_for_ptr(p);
+  tsd_getspecific(arena_key, vptr);
+  if(vptr)
+    (void)mutex_lock(&ar_ptr->mutex);
+  chunk_free(ar_ptr, p);
+  if(vptr)
+    (void)mutex_unlock(&ar_ptr->mutex);
+}
+
 #endif /* defined(_LIBC) || defined(MALLOC_HOOKS) */
 
 
diff --git a/malloc/thread-m.h b/malloc/thread-m.h
index 9afd058ccb..24d95dfb31 100644
--- a/malloc/thread-m.h
+++ b/malloc/thread-m.h
@@ -26,6 +26,8 @@
 #ifndef _THREAD_M_H
 #define _THREAD_M_H
 
+#undef thread_atfork_static
+
 #if defined(_LIBC) /* The GNU C library, a special case of Posix threads */
 
 #include <bits/libc-lock.h>
@@ -65,6 +67,9 @@ static Void_t *malloc_key_data;
 #define mutex_unlock(m)		\
    (__pthread_mutex_unlock != NULL ? __pthread_mutex_unlock (m) : 0)
 
+#define thread_atfork(prepare, parent, child) \
+   (__pthread_atfork != NULL ? __pthread_atfork(prepare, parent, child) : 0)
+
 #elif defined(MUTEX_INITIALIZER)
 /* Assume hurd, with cthreads */
 
@@ -95,6 +100,12 @@ static int tsd_keys_alloced = 0;
 #define tsd_getspecific(key, vptr) \
   ((vptr) = (void *)*__hurd_threadvar_location (_HURD_THREADVAR_MALLOC))
 
+#define thread_atfork(prepare, parent, child) do {} while(0)
+#define thread_atfork_static(prepare, parent, child) \
+ text_set_element(_hurd_fork_prepare_hook, prepare); \
+ text_set_element(_hurd_fork_parent_hook, parent); \
+ text_set_element(_hurd_fork_child_hook, child);
+
 /* No we're *not* using pthreads.  */
 #define __pthread_initialize ((void (*)(void))0)
 
@@ -126,6 +137,10 @@ typedef pthread_key_t tsd_key_t;
 #define tsd_setspecific(key, data) pthread_setspecific(key, data)
 #define tsd_getspecific(key, vptr) (vptr = pthread_getspecific(key))
 
+/* at fork */
+#define thread_atfork(prepare, parent, child) \
+                                   pthread_atfork(prepare, parent, child)
+
 #elif USE_THR /* Solaris threads */
 
 #include <thread.h>
@@ -147,6 +162,8 @@ typedef void *tsd_key_t[256];
 #define tsd_setspecific(key, data) (key[(unsigned)thr_self() % 256] = (data))
 #define tsd_getspecific(key, vptr) (vptr = key[(unsigned)thr_self() % 256])
 
+#define thread_atfork(prepare, parent, child) do {} while(0)
+
 #elif USE_SPROC /* SGI sproc() threads */
 
 #include <sys/wait.h>
@@ -170,6 +187,8 @@ int tsd_key_next;
 #define tsd_setspecific(key, data) (((void **)(&PRDA->usr_prda))[key] = data)
 #define tsd_getspecific(key, vptr) (vptr = ((void **)(&PRDA->usr_prda))[key])
 
+#define thread_atfork(prepare, parent, child) do {} while(0)
+
 #else /* no _LIBC or USE_... are defined */
 
 #define NO_THREADS
@@ -193,6 +212,8 @@ typedef void *tsd_key_t;
 #define tsd_setspecific(key, data) do {} while(0)
 #define tsd_getspecific(key, vptr) (vptr = NULL)
 
+#define thread_atfork(prepare, parent, child) do {} while(0)
+
 #endif /* defined(NO_THREADS) */
 
 #endif /* !defined(_THREAD_M_H) */