Upgrade Boehm GC to 7.2alpha4.
[cacao.git] / src / mm / boehm-gc / thread_local_alloc.c
index 4d907fe4c35176481747c13910bd75b3286a37c8..1e9120d46aed53d701db6493925614f39527d114 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright (c) 2000-2005 by Hewlett-Packard Company.  All rights reserved.
  *
  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  * provided the above notices are retained, and a notice that the code was
  * modified is included with the above copyright notice.
  */
+
 #include "private/gc_priv.h"
 
-# if defined(THREAD_LOCAL_ALLOC)
+#if defined(THREAD_LOCAL_ALLOC)
+
+#ifndef THREADS
+# error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS"
+#endif
 
 #include "private/thread_local_alloc.h"
-#include "gc_inline.h"
 
-# include <stdlib.h>
+#include <stdlib.h>
 
 #if defined(USE_COMPILER_TLS)
   __thread
@@ -28,8 +32,8 @@ GC_key_t GC_thread_key;
 
 static GC_bool keys_initialized;
 
-/* Return a single nonempty freelist fl to the global one pointed to   */
-/* by gfl.     */
+/* Return a single nonempty freelist fl to the global one pointed to    */
+/* by gfl.      */
 
 static void return_single_freelist(void *fl, void **gfl)
 {
@@ -40,30 +44,30 @@ static void return_single_freelist(void *fl, void **gfl)
     } else {
       GC_ASSERT(GC_size(fl) == GC_size(*gfl));
       /* Concatenate: */
-       qptr = &(obj_link(fl));
-       while ((word)(q = *qptr) >= HBLKSIZE)
-         qptr = &(obj_link(q));
-       GC_ASSERT(0 == q);
-       *qptr = *gfl;
-       *gfl = fl;
+        qptr = &(obj_link(fl));
+        while ((word)(q = *qptr) >= HBLKSIZE)
+          qptr = &(obj_link(q));
+        GC_ASSERT(0 == q);
+        *qptr = *gfl;
+        *gfl = fl;
     }
 }
 
 /* Recover the contents of the freelist array fl into the global one gfl.*/
-/* We hold the allocator lock.                                         */
+/* We hold the allocator lock.                                          */
 static void return_freelists(void **fl, void **gfl)
 {
     int i;
 
     for (i = 1; i < TINY_FREELISTS; ++i) {
-       if ((word)(fl[i]) >= HBLKSIZE) {
-         return_single_freelist(fl[i], gfl+i);
-       }
-       /* Clear fl[i], since the thread structure may hang around.     */
-       /* Do it in a way that is likely to trap if we access it.       */
-       fl[i] = (ptr_t)HBLKSIZE;
+        if ((word)(fl[i]) >= HBLKSIZE) {
+          return_single_freelist(fl[i], gfl+i);
+        }
+        /* Clear fl[i], since the thread structure may hang around.     */
+        /* Do it in a way that is likely to trap if we access it.       */
+        fl[i] = (ptr_t)HBLKSIZE;
     }
-    /* The 0 granule freelist really contains 1 granule objects.       */
+    /* The 0 granule freelist really contains 1 granule objects.        */
 #   ifdef GC_GCJ_SUPPORT
       if (fl[0] == ERROR_FL) return;
 #   endif
@@ -72,33 +76,33 @@ static void return_freelists(void **fl, void **gfl)
     }
 }
 
-/* Each thread structure must be initialized.  */
-/* This call must be made from the new thread. */
-void GC_init_thread_local(GC_tlfs p)
+/* Each thread structure must be initialized.   */
+/* This call must be made from the new thread.  */
+GC_INNER void GC_init_thread_local(GC_tlfs p)
 {
     int i;
 
     GC_ASSERT(I_HOLD_LOCK());
     if (!keys_initialized) {
-       if (0 != GC_key_create(&GC_thread_key, 0)) {
-           ABORT("Failed to create key for local allocator");
+        if (0 != GC_key_create(&GC_thread_key, 0)) {
+            ABORT("Failed to create key for local allocator");
         }
-       keys_initialized = TRUE;
+        keys_initialized = TRUE;
     }
     if (0 != GC_setspecific(GC_thread_key, p)) {
-       ABORT("Failed to set thread specific allocation pointers");
+        ABORT("Failed to set thread specific allocation pointers");
     }
     for (i = 1; i < TINY_FREELISTS; ++i) {
-       p -> ptrfree_freelists[i] = (void *)(word)1;
-       p -> normal_freelists[i] = (void *)(word)1;
-#      ifdef GC_GCJ_SUPPORT
-         p -> gcj_freelists[i] = (void *)(word)1;
-#      endif
-    }   
-    /* Set up the size 0 free lists.   */
-    /* We now handle most of them like regular free lists, to ensure   */
-    /* That explicit deallocation works.  However, allocation of a     */
-    /* size 0 "gcj" object is always an error.                         */
+        p -> ptrfree_freelists[i] = (void *)(word)1;
+        p -> normal_freelists[i] = (void *)(word)1;
+#       ifdef GC_GCJ_SUPPORT
+          p -> gcj_freelists[i] = (void *)(word)1;
+#       endif
+    }
+    /* Set up the size 0 free lists.    */
+    /* We now handle most of them like regular free lists, to ensure    */
+    /* That explicit deallocation works.  However, allocation of a      */
+    /* size 0 "gcj" object is always an error.                          */
     p -> ptrfree_freelists[0] = (void *)(word)1;
     p -> normal_freelists[0] = (void *)(word)1;
 #   ifdef GC_GCJ_SUPPORT
@@ -106,33 +110,24 @@ void GC_init_thread_local(GC_tlfs p)
 #   endif
 }
 
-#ifdef GC_GCJ_SUPPORT
-  extern void ** GC_gcjobjfreelist;
-#endif
-
-/* We hold the allocator lock. */
-void GC_destroy_thread_local(GC_tlfs p)
+/* We hold the allocator lock.  */
+GC_INNER void GC_destroy_thread_local(GC_tlfs p)
 {
-    /* We currently only do this from the thread itself or from        */
-    /* the fork handler for a child process.                   */
+    /* We currently only do this from the thread itself or from */
+    /* the fork handler for a child process.                    */
 #   ifndef HANDLE_FORK
       GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);
 #   endif
     return_freelists(p -> ptrfree_freelists, GC_aobjfreelist);
     return_freelists(p -> normal_freelists, GC_objfreelist);
 #   ifdef GC_GCJ_SUPPORT
-       return_freelists(p -> gcj_freelists, GC_gcjobjfreelist);
+        return_freelists(p -> gcj_freelists, (void **)GC_gcjobjfreelist);
 #   endif
 }
 
-#if defined(GC_ASSERTIONS) && defined(GC_PTHREADS) && !defined(CYGWIN32) \
-    && !defined(GC_WIN32_PTHREADS)
-# include <pthread.h>
-  extern char * GC_lookup_thread(pthread_t id);
-#endif
-
-#if defined(GC_ASSERTIONS) && defined(GC_WIN32_THREADS)
-  void * /*GC_thread*/ GC_lookup_thread_inner(unsigned /*DWORD*/ thread_id);
+#ifdef GC_ASSERTIONS
+  /* Defined in pthread_support.c or win32_threads.c. */
+  GC_bool GC_is_thread_tsd_valid(void *tsd);
 #endif
 
 GC_API void * GC_CALL GC_malloc(size_t bytes)
@@ -145,9 +140,9 @@ GC_API void * GC_CALL GC_malloc(size_t bytes)
 #   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
       GC_key_t k = GC_thread_key;
       if (EXPECT(0 == k, 0)) {
-       /* We haven't yet run GC_init_parallel.  That means     */
-       /* we also aren't locking, so this is fairly cheap.     */
-       return GC_core_malloc(bytes);
+        /* We haven't yet run GC_init_parallel.  That means     */
+        /* we also aren't locking, so this is fairly cheap.     */
+        return GC_core_malloc(bytes);
       }
       tsd = GC_getspecific(k);
 #   else
@@ -155,30 +150,19 @@ GC_API void * GC_CALL GC_malloc(size_t bytes)
 #   endif
 #   if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
       if (EXPECT(0 == tsd, 0)) {
-       return GC_core_malloc(bytes);
+        return GC_core_malloc(bytes);
       }
 #   endif
     GC_ASSERT(GC_is_initialized);
-#   ifdef GC_ASSERTIONS
-      /* We can't check tsd correctly, since we don't have access to   */
-      /* the right declarations.  But we can check that it's close.    */
-      LOCK();
-      {
-#      if defined(GC_WIN32_THREADS)
-         char * me = (char *)GC_lookup_thread_inner(GetCurrentThreadId());
-#       else
-         char * me = GC_lookup_thread(pthread_self());
-#      endif
-        GC_ASSERT((char *)tsd > me && (char *)tsd < me + 1000);
-      }
-      UNLOCK();
-#   endif
+
+    GC_ASSERT(GC_is_thread_tsd_valid(tsd));
+
     tiny_fl = ((GC_tlfs)tsd) -> normal_freelists;
     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
-                        NORMAL, GC_core_malloc(bytes), obj_link(result)=0);
+                         NORMAL, GC_core_malloc(bytes), obj_link(result)=0);
 #   ifdef LOG_ALLOCS
       GC_err_printf("GC_malloc(%u) = %p : %u\n",
-                       (unsigned)bytes, result, (unsigned)GC_gc_no);
+                        (unsigned)bytes, result, (unsigned)GC_gc_no);
 #   endif
     return result;
 }
@@ -193,9 +177,9 @@ GC_API void * GC_CALL GC_malloc_atomic(size_t bytes)
 #   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
       GC_key_t k = GC_thread_key;
       if (EXPECT(0 == k, 0)) {
-       /* We haven't yet run GC_init_parallel.  That means     */
-       /* we also aren't locking, so this is fairly cheap.     */
-       return GC_core_malloc(bytes);
+        /* We haven't yet run GC_init_parallel.  That means     */
+        /* we also aren't locking, so this is fairly cheap.     */
+        return GC_core_malloc(bytes);
       }
       tsd = GC_getspecific(k);
 #   else
@@ -203,48 +187,44 @@ GC_API void * GC_CALL GC_malloc_atomic(size_t bytes)
 #   endif
 #   if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
       if (EXPECT(0 == tsd, 0)) {
-       return GC_core_malloc(bytes);
+        return GC_core_malloc(bytes);
       }
 #   endif
     GC_ASSERT(GC_is_initialized);
     tiny_fl = ((GC_tlfs)tsd) -> ptrfree_freelists;
     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES, PTRFREE,
-                        GC_core_malloc_atomic(bytes), (void)0 /* no init */);
+                         GC_core_malloc_atomic(bytes), (void)0 /* no init */);
     return result;
 }
 
 #ifdef GC_GCJ_SUPPORT
 
-#include "include/gc_gcj.h"
+# include "atomic_ops.h" /* for AO_compiler_barrier() */
 
-#ifdef GC_ASSERTIONS
-  extern GC_bool GC_gcj_malloc_initialized;
-#endif
+# include "include/gc_gcj.h"
 
-extern int GC_gcj_kind;
-
-/* Gcj-style allocation without locks is extremely tricky.  The        */
-/* fundamental issue is that we may end up marking a free list, which  */
-/* has freelist links instead of "vtable" pointers.  That is usually   */
-/* OK, since the next object on the free list will be cleared, and     */
-/* will thus be interpreted as containing a zero descriptor.  That's   */
-/* fine if the object has not yet been initialized.  But there are     */
-/* interesting potential races.                                                */
-/* In the case of incremental collection, this seems hopeless, since   */
-/* the marker may run asynchronously, and may pick up the pointer to   */
-/* the next freelist entry (which it thinks is a vtable pointer), get  */
-/* suspended for a while, and then see an allocated object instead     */
-/* of the vtable.  This made be avoidable with either a handshake with */
-/* the collector or, probably more easily, by moving the free list     */
-/* links to the second word of each object.  The latter isn't a                */
-/* universal win, since on architecture like Itanium, nonzero offsets  */
-/* are not necessarily free.  And there may be cache fill order issues.        */
-/* For now, we punt with incremental GC.  This probably means that     */
-/* incremental GC should be enabled before we fork a second thread.    */
-/* Unlike the other thread local allocation calls, we assume that the  */
-/* collector has been explicitly initialized.                          */
+/* Gcj-style allocation without locks is extremely tricky.  The         */
+/* fundamental issue is that we may end up marking a free list, which   */
+/* has freelist links instead of "vtable" pointers.  That is usually    */
+/* OK, since the next object on the free list will be cleared, and      */
+/* will thus be interpreted as containing a zero descriptor.  That's    */
+/* fine if the object has not yet been initialized.  But there are      */
+/* interesting potential races.                                         */
+/* In the case of incremental collection, this seems hopeless, since    */
+/* the marker may run asynchronously, and may pick up the pointer to    */
+/* the next freelist entry (which it thinks is a vtable pointer), get   */
+/* suspended for a while, and then see an allocated object instead      */
+/* of the vtable.  This may be avoidable with either a handshake with   */
+/* the collector or, probably more easily, by moving the free list      */
+/* links to the second word of each object.  The latter isn't a         */
+/* universal win, since on architecture like Itanium, nonzero offsets   */
+/* are not necessarily free.  And there may be cache fill order issues. */
+/* For now, we punt with incremental GC.  This probably means that      */
+/* incremental GC should be enabled before we fork a second thread.     */
+/* Unlike the other thread local allocation calls, we assume that the   */
+/* collector has been explicitly initialized.                           */
 GC_API void * GC_CALL GC_gcj_malloc(size_t bytes,
-                           void * ptr_to_struct_containing_descr)
+                                    void * ptr_to_struct_containing_descr)
 {
   if (GC_EXPECT(GC_incremental, 0)) {
     return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr);
@@ -252,80 +232,79 @@ GC_API void * GC_CALL GC_gcj_malloc(size_t bytes,
     size_t granules = ROUNDED_UP_GRANULES(bytes);
     void *result;
     void **tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))
-                                       -> gcj_freelists;
+                                        -> gcj_freelists;
     GC_ASSERT(GC_gcj_malloc_initialized);
     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
-                        GC_gcj_kind,
-                        GC_core_gcj_malloc(bytes,
-                                           ptr_to_struct_containing_descr),
-                        {AO_compiler_barrier();
-                         *(void **)result = ptr_to_struct_containing_descr;});
-       /* This forces the initialization of the "method ptr".          */
-        /* This is necessary to ensure some very subtle properties     */
-       /* required if a GC is run in the middle of such an allocation. */
-       /* Here we implicitly also assume atomicity for the free list.  */
-        /* and method pointer assignments.                             */
-       /* We must update the freelist before we store the pointer.     */
-       /* Otherwise a GC at this point would see a corrupted           */
-       /* free list.                                                   */
-       /* A real memory barrier is not needed, since the               */
-       /* action of stopping this thread will cause prior writes       */
-       /* to complete.                                                 */
-       /* We assert that any concurrent marker will stop us.           */
-       /* Thus it is impossible for a mark procedure to see the        */
-       /* allocation of the next object, but to see this object        */
-       /* still containing a free list pointer.  Otherwise the         */
-       /* marker, by misinterpreting the freelist link as a vtable     */
-        /* pointer, might find a random "mark descriptor" in the next  */
-        /* object.                                                     */
+                         GC_gcj_kind,
+                         GC_core_gcj_malloc(bytes,
+                                            ptr_to_struct_containing_descr),
+                         {AO_compiler_barrier();
+                          *(void **)result = ptr_to_struct_containing_descr;});
+        /* This forces the initialization of the "method ptr".          */
+        /* This is necessary to ensure some very subtle properties      */
+        /* required if a GC is run in the middle of such an allocation. */
+        /* Here we implicitly also assume atomicity for the free list.  */
+        /* and method pointer assignments.                              */
+        /* We must update the freelist before we store the pointer.     */
+        /* Otherwise a GC at this point would see a corrupted           */
+        /* free list.                                                   */
+        /* A real memory barrier is not needed, since the               */
+        /* action of stopping this thread will cause prior writes       */
+        /* to complete.                                                 */
+        /* We assert that any concurrent marker will stop us.           */
+        /* Thus it is impossible for a mark procedure to see the        */
+        /* allocation of the next object, but to see this object        */
+        /* still containing a free list pointer.  Otherwise the         */
+        /* marker, by misinterpreting the freelist link as a vtable     */
+        /* pointer, might find a random "mark descriptor" in the next   */
+        /* object.                                                      */
     return result;
   }
 }
 
 #endif /* GC_GCJ_SUPPORT */
 
-/* The thread support layer must arrange to mark thread-local  */
-/* free lists explicitly, since the link field is often        */
-/* invisible to the marker.  It knows how to find all threads; */
-/* we take care of an individual thread freelist structure.    */
-void GC_mark_thread_local_fls_for(GC_tlfs p)
+/* The thread support layer must arrange to mark thread-local   */
+/* free lists explicitly, since the link field is often         */
+/* invisible to the marker.  It knows how to find all threads;  */
+/* we take care of an individual thread freelist structure.     */
+GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p)
 {
     ptr_t q;
     int j;
-    
+
     for (j = 0; j < TINY_FREELISTS; ++j) {
       q = p -> ptrfree_freelists[j];
       if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
       q = p -> normal_freelists[j];
       if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
 #     ifdef GC_GCJ_SUPPORT
-       if (j > 0) {
+        if (j > 0) {
           q = p -> gcj_freelists[j];
           if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
-       }
+        }
 #     endif /* GC_GCJ_SUPPORT */
     }
 }
 
 #if defined(GC_ASSERTIONS)
-    /* Check that all thread-local free-lists in p are completely marked.      */
+    /* Check that all thread-local free-lists in p are completely marked.       */
     void GC_check_tls_for(GC_tlfs p)
     {
-       ptr_t q;
-       int j;
-       
-       for (j = 1; j < TINY_FREELISTS; ++j) {
-         q = p -> ptrfree_freelists[j];
-         if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
-         q = p -> normal_freelists[j];
-         if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
-#        ifdef GC_GCJ_SUPPORT
-           q = p -> gcj_freelists[j];
-           if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
-#        endif /* GC_GCJ_SUPPORT */
-       }
+        ptr_t q;
+        int j;
+
+        for (j = 1; j < TINY_FREELISTS; ++j) {
+          q = p -> ptrfree_freelists[j];
+          if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
+          q = p -> normal_freelists[j];
+          if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
+#         ifdef GC_GCJ_SUPPORT
+            q = p -> gcj_freelists[j];
+            if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
+#         endif /* GC_GCJ_SUPPORT */
+        }
     }
 #endif /* GC_ASSERTIONS */
 
-# endif /* THREAD_LOCAL_ALLOC */
-
+#endif /* THREAD_LOCAL_ALLOC */