LCOV - code coverage report
Current view: top level - mm/boehm-gc - thread_local_alloc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 49 70 70.0 %
Date: 2015-06-10 18:10:59 Functions: 6 8 75.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2000-2005 by Hewlett-Packard Company.  All rights reserved.
       3             :  *
       4             :  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
       5             :  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
       6             :  *
       7             :  * Permission is hereby granted to use or copy this program
       8             :  * for any purpose,  provided the above notices are retained on all copies.
       9             :  * Permission to modify the code and to distribute modified code is granted,
      10             :  * provided the above notices are retained, and a notice that the code was
      11             :  * modified is included with the above copyright notice.
      12             :  */
      13             : 
      14             : #include "private/gc_priv.h"
      15             : 
      16             : #if defined(THREAD_LOCAL_ALLOC)
      17             : 
      18             : #ifndef THREADS
      19             : # error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS"
      20             : #endif
      21             : 
      22             : #include "private/thread_local_alloc.h"
      23             : 
      24             : #include <stdlib.h>
      25             : 
      26             : #if defined(USE_COMPILER_TLS)
      27             :   __thread
      28             : #elif defined(USE_WIN32_COMPILER_TLS)
      29             :   __declspec(thread)
      30             : #endif
      31             : GC_key_t GC_thread_key;
      32             : 
      33             : static GC_bool keys_initialized;
      34             : 
      35             : /* Return a single nonempty freelist fl to the global one pointed to    */
      36             : /* by gfl.      */
      37             : 
      38           0 : static void return_single_freelist(void *fl, void **gfl)
      39             : {
      40             :     void *q, **qptr;
      41             : 
      42           0 :     if (*gfl == 0) {
      43           0 :       *gfl = fl;
      44             :     } else {
      45             :       GC_ASSERT(GC_size(fl) == GC_size(*gfl));
      46             :       /* Concatenate: */
      47           0 :         qptr = &(obj_link(fl));
      48           0 :         while ((word)(q = *qptr) >= HBLKSIZE)
      49           0 :           qptr = &(obj_link(q));
      50             :         GC_ASSERT(0 == q);
      51           0 :         *qptr = *gfl;
      52           0 :         *gfl = fl;
      53             :     }
      54           0 : }
      55             : 
      56             : /* Recover the contents of the freelist array fl into the global one gfl.*/
      57             : /* We hold the allocator lock.                                          */
      58         453 : static void return_freelists(void **fl, void **gfl)
      59             : {
      60             :     int i;
      61             : 
      62       11325 :     for (i = 1; i < TINY_FREELISTS; ++i) {
      63       10872 :         if ((word)(fl[i]) >= HBLKSIZE) {
      64           0 :           return_single_freelist(fl[i], gfl+i);
      65             :         }
      66             :         /* Clear fl[i], since the thread structure may hang around.     */
      67             :         /* Do it in a way that is likely to trap if we access it.       */
      68       10872 :         fl[i] = (ptr_t)HBLKSIZE;
      69             :     }
      70             :     /* The 0 granule freelist really contains 1 granule objects.        */
      71             : #   ifdef GC_GCJ_SUPPORT
      72         453 :       if (fl[0] == ERROR_FL) return;
      73             : #   endif
      74         302 :     if ((word)(fl[0]) >= HBLKSIZE) {
      75           0 :         return_single_freelist(fl[0], gfl+1);
      76             :     }
      77             : }
      78             : 
      79             : /* Each thread structure must be initialized.   */
      80             : /* This call must be made from the new thread.  */
      81         662 : GC_INNER void GC_init_thread_local(GC_tlfs p)
      82             : {
      83             :     int i;
      84             : 
      85             :     GC_ASSERT(I_HOLD_LOCK());
      86         662 :     if (!keys_initialized) {
      87             :         if (0 != GC_key_create(&GC_thread_key, 0)) {
      88             :             ABORT("Failed to create key for local allocator");
      89             :         }
      90         163 :         keys_initialized = TRUE;
      91             :     }
      92         662 :     if (0 != GC_setspecific(GC_thread_key, p)) {
      93             :         ABORT("Failed to set thread specific allocation pointers");
      94             :     }
      95       16550 :     for (i = 1; i < TINY_FREELISTS; ++i) {
      96       15888 :         p -> ptrfree_freelists[i] = (void *)(word)1;
      97       15888 :         p -> normal_freelists[i] = (void *)(word)1;
      98             : #       ifdef GC_GCJ_SUPPORT
      99       15888 :           p -> gcj_freelists[i] = (void *)(word)1;
     100             : #       endif
     101             :     }
     102             :     /* Set up the size 0 free lists.    */
     103             :     /* We now handle most of them like regular free lists, to ensure    */
     104             :     /* That explicit deallocation works.  However, allocation of a      */
     105             :     /* size 0 "gcj" object is always an error.                          */
     106         662 :     p -> ptrfree_freelists[0] = (void *)(word)1;
     107         662 :     p -> normal_freelists[0] = (void *)(word)1;
     108             : #   ifdef GC_GCJ_SUPPORT
     109         662 :         p -> gcj_freelists[0] = ERROR_FL;
     110             : #   endif
     111         662 : }
     112             : 
     113             : /* We hold the allocator lock.  */
     114         151 : GC_INNER void GC_destroy_thread_local(GC_tlfs p)
     115             : {
     116             :     /* We currently only do this from the thread itself or from */
     117             :     /* the fork handler for a child process.                    */
     118             : #   ifndef HANDLE_FORK
     119             :       GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);
     120             : #   endif
     121         151 :     return_freelists(p -> ptrfree_freelists, GC_aobjfreelist);
     122         151 :     return_freelists(p -> normal_freelists, GC_objfreelist);
     123             : #   ifdef GC_GCJ_SUPPORT
     124         151 :         return_freelists(p -> gcj_freelists, (void **)GC_gcjobjfreelist);
     125             : #   endif
     126         151 : }
     127             : 
     128             : #ifdef GC_ASSERTIONS
     129             :   /* Defined in pthread_support.c or win32_threads.c. */
     130             :   GC_bool GC_is_thread_tsd_valid(void *tsd);
     131             : #endif
     132             : 
     133     1437417 : GC_API void * GC_CALL GC_malloc(size_t bytes)
     134             : {
     135     1437417 :     size_t granules = ROUNDED_UP_GRANULES(bytes);
     136             :     void *tsd;
     137             :     void *result;
     138             :     void **tiny_fl;
     139             : 
     140             : #   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
     141     1437417 :       GC_key_t k = GC_thread_key;
     142     1437417 :       if (EXPECT(0 == k, FALSE)) {
     143             :         /* We haven't yet run GC_init_parallel.  That means     */
     144             :         /* we also aren't locking, so this is fairly cheap.     */
     145           0 :         return GC_core_malloc(bytes);
     146             :       }
     147     1437417 :       tsd = GC_getspecific(k);
     148             : #   else
     149             :       tsd = GC_getspecific(GC_thread_key);
     150             : #   endif
     151             : #   if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
     152             :       if (EXPECT(0 == tsd, FALSE)) {
     153             :         return GC_core_malloc(bytes);
     154             :       }
     155             : #   endif
     156             :     GC_ASSERT(GC_is_initialized);
     157             : 
     158             :     GC_ASSERT(GC_is_thread_tsd_valid(tsd));
     159             : 
     160     1437417 :     tiny_fl = ((GC_tlfs)tsd) -> normal_freelists;
     161     1437417 :     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
     162             :                          NORMAL, GC_core_malloc(bytes), obj_link(result)=0);
     163             : #   ifdef LOG_ALLOCS
     164             :       GC_err_printf("GC_malloc(%u) = %p : %u\n",
     165             :                         (unsigned)bytes, result, (unsigned)GC_gc_no);
     166             : #   endif
     167     1437417 :     return result;
     168             : }
     169             : 
     170      580358 : GC_API void * GC_CALL GC_malloc_atomic(size_t bytes)
     171             : {
     172      580358 :     size_t granules = ROUNDED_UP_GRANULES(bytes);
     173             :     void *tsd;
     174             :     void *result;
     175             :     void **tiny_fl;
     176             : 
     177             : #   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
     178      580358 :       GC_key_t k = GC_thread_key;
     179      580358 :       if (EXPECT(0 == k, FALSE)) {
     180             :         /* We haven't yet run GC_init_parallel.  That means     */
     181             :         /* we also aren't locking, so this is fairly cheap.     */
     182           0 :         return GC_core_malloc_atomic(bytes);
     183             :       }
     184      580358 :       tsd = GC_getspecific(k);
     185             : #   else
     186             :       tsd = GC_getspecific(GC_thread_key);
     187             : #   endif
     188             : #   if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
     189             :       if (EXPECT(0 == tsd, FALSE)) {
     190             :         return GC_core_malloc_atomic(bytes);
     191             :       }
     192             : #   endif
     193             :     GC_ASSERT(GC_is_initialized);
     194      580358 :     tiny_fl = ((GC_tlfs)tsd) -> ptrfree_freelists;
     195      580358 :     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES, PTRFREE,
     196             :                          GC_core_malloc_atomic(bytes), (void)0 /* no init */);
     197      580358 :     return result;
     198             : }
     199             : 
     200             : #ifdef GC_GCJ_SUPPORT
     201             : 
     202             : # include "atomic_ops.h" /* for AO_compiler_barrier() */
     203             : 
     204             : # include "include/gc_gcj.h"
     205             : 
     206             : /* Gcj-style allocation without locks is extremely tricky.  The         */
     207             : /* fundamental issue is that we may end up marking a free list, which   */
     208             : /* has freelist links instead of "vtable" pointers.  That is usually    */
     209             : /* OK, since the next object on the free list will be cleared, and      */
     210             : /* will thus be interpreted as containing a zero descriptor.  That's    */
     211             : /* fine if the object has not yet been initialized.  But there are      */
     212             : /* interesting potential races.                                         */
     213             : /* In the case of incremental collection, this seems hopeless, since    */
     214             : /* the marker may run asynchronously, and may pick up the pointer to    */
     215             : /* the next freelist entry (which it thinks is a vtable pointer), get   */
     216             : /* suspended for a while, and then see an allocated object instead      */
     217             : /* of the vtable.  This may be avoidable with either a handshake with   */
     218             : /* the collector or, probably more easily, by moving the free list      */
     219             : /* links to the second word of each object.  The latter isn't a         */
     220             : /* universal win, since on architecture like Itanium, nonzero offsets   */
     221             : /* are not necessarily free.  And there may be cache fill order issues. */
     222             : /* For now, we punt with incremental GC.  This probably means that      */
     223             : /* incremental GC should be enabled before we fork a second thread.     */
     224             : /* Unlike the other thread local allocation calls, we assume that the   */
     225             : /* collector has been explicitly initialized.                           */
     226           0 : GC_API void * GC_CALL GC_gcj_malloc(size_t bytes,
     227             :                                     void * ptr_to_struct_containing_descr)
     228             : {
     229           0 :   if (GC_EXPECT(GC_incremental, 0)) {
     230           0 :     return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr);
     231             :   } else {
     232           0 :     size_t granules = ROUNDED_UP_GRANULES(bytes);
     233             :     void *result;
     234           0 :     void **tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))
     235           0 :                                         -> gcj_freelists;
     236             :     GC_ASSERT(GC_gcj_malloc_initialized);
     237           0 :     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
     238             :                          GC_gcj_kind,
     239             :                          GC_core_gcj_malloc(bytes,
     240             :                                             ptr_to_struct_containing_descr),
     241             :                          {AO_compiler_barrier();
     242             :                           *(void **)result = ptr_to_struct_containing_descr;});
     243             :         /* This forces the initialization of the "method ptr".          */
     244             :         /* This is necessary to ensure some very subtle properties      */
     245             :         /* required if a GC is run in the middle of such an allocation. */
     246             :         /* Here we implicitly also assume atomicity for the free list.  */
     247             :         /* and method pointer assignments.                              */
     248             :         /* We must update the freelist before we store the pointer.     */
     249             :         /* Otherwise a GC at this point would see a corrupted           */
     250             :         /* free list.                                                   */
     251             :         /* A real memory barrier is not needed, since the               */
     252             :         /* action of stopping this thread will cause prior writes       */
     253             :         /* to complete.                                                 */
     254             :         /* We assert that any concurrent marker will stop us.           */
     255             :         /* Thus it is impossible for a mark procedure to see the        */
     256             :         /* allocation of the next object, but to see this object        */
     257             :         /* still containing a free list pointer.  Otherwise the         */
     258             :         /* marker, by misinterpreting the freelist link as a vtable     */
     259             :         /* pointer, might find a random "mark descriptor" in the next   */
     260             :         /* object.                                                      */
     261           0 :     return result;
     262             :   }
     263             : }
     264             : 
     265             : #endif /* GC_GCJ_SUPPORT */
     266             : 
     267             : /* The thread support layer must arrange to mark thread-local   */
     268             : /* free lists explicitly, since the link field is often         */
     269             : /* invisible to the marker.  It knows how to find all threads;  */
     270             : /* we take care of an individual thread freelist structure.     */
     271         543 : GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p)
     272             : {
     273             :     ptr_t q;
     274             :     int j;
     275             : 
     276       14118 :     for (j = 0; j < TINY_FREELISTS; ++j) {
     277       13575 :       q = p -> ptrfree_freelists[j];
     278       13575 :       if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
     279       13575 :       q = p -> normal_freelists[j];
     280       13575 :       if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
     281             : #     ifdef GC_GCJ_SUPPORT
     282       13575 :         if (j > 0) {
     283       13032 :           q = p -> gcj_freelists[j];
     284       13032 :           if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
     285             :         }
     286             : #     endif /* GC_GCJ_SUPPORT */
     287             :     }
     288         543 : }
     289             : 
     290             : #if defined(GC_ASSERTIONS)
     291             :     /* Check that all thread-local free-lists in p are completely marked. */
     292             :     void GC_check_tls_for(GC_tlfs p)
     293             :     {
     294             :         int j;
     295             : 
     296             :         for (j = 1; j < TINY_FREELISTS; ++j) {
     297             :           GC_check_fl_marks(&p->ptrfree_freelists[j]);
     298             :           GC_check_fl_marks(&p->normal_freelists[j]);
     299             : #         ifdef GC_GCJ_SUPPORT
     300             :             GC_check_fl_marks(&p->gcj_freelists[j]);
     301             : #         endif
     302             :         }
     303             :     }
     304             : #endif /* GC_ASSERTIONS */
     305             : 
     306             : #endif /* THREAD_LOCAL_ALLOC */

Generated by: LCOV version 1.11