Blame thread_local_alloc.c

Packit d28291
/*
Packit d28291
 * Copyright (c) 2000-2005 by Hewlett-Packard Company.  All rights reserved.
Packit d28291
 *
Packit d28291
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
Packit d28291
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
Packit d28291
 *
Packit d28291
 * Permission is hereby granted to use or copy this program
Packit d28291
 * for any purpose,  provided the above notices are retained on all copies.
Packit d28291
 * Permission to modify the code and to distribute modified code is granted,
Packit d28291
 * provided the above notices are retained, and a notice that the code was
Packit d28291
 * modified is included with the above copyright notice.
Packit d28291
 */
Packit d28291
Packit d28291
#include "private/gc_priv.h"
Packit d28291
Packit d28291
#if defined(THREAD_LOCAL_ALLOC)
Packit d28291
Packit d28291
#ifndef THREADS
Packit d28291
# error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS"
Packit d28291
#endif
Packit d28291
Packit d28291
#include "private/thread_local_alloc.h"
Packit d28291
Packit d28291
#include <stdlib.h>
Packit d28291
Packit d28291
#if defined(USE_COMPILER_TLS)
Packit d28291
  __thread GC_ATTR_TLS_FAST
Packit d28291
#elif defined(USE_WIN32_COMPILER_TLS)
Packit d28291
  __declspec(thread) GC_ATTR_TLS_FAST
Packit d28291
#endif
Packit d28291
GC_key_t GC_thread_key;
Packit d28291
Packit d28291
static GC_bool keys_initialized;
Packit d28291
Packit d28291
/* Return a single nonempty freelist fl to the global one pointed to    */
Packit d28291
/* by gfl.                                                              */
Packit d28291
Packit d28291
static void return_single_freelist(void *fl, void **gfl)
Packit d28291
{
Packit d28291
    if (*gfl == 0) {
Packit d28291
      *gfl = fl;
Packit d28291
    } else {
Packit d28291
      void *q, **qptr;
Packit d28291
Packit d28291
      GC_ASSERT(GC_size(fl) == GC_size(*gfl));
Packit d28291
      /* Concatenate: */
Packit d28291
        qptr = &(obj_link(fl));
Packit d28291
        while ((word)(q = *qptr) >= HBLKSIZE)
Packit d28291
          qptr = &(obj_link(q));
Packit d28291
        GC_ASSERT(0 == q);
Packit d28291
        *qptr = *gfl;
Packit d28291
        *gfl = fl;
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
/* Recover the contents of the freelist array fl into the global one gfl.*/
Packit d28291
/* We hold the allocator lock.                                          */
Packit d28291
static void return_freelists(void **fl, void **gfl)
Packit d28291
{
Packit d28291
    int i;
Packit d28291
Packit d28291
    for (i = 1; i < TINY_FREELISTS; ++i) {
Packit d28291
        if ((word)(fl[i]) >= HBLKSIZE) {
Packit d28291
          return_single_freelist(fl[i], &gfl[i]);
Packit d28291
        }
Packit d28291
        /* Clear fl[i], since the thread structure may hang around.     */
Packit d28291
        /* Do it in a way that is likely to trap if we access it.       */
Packit d28291
        fl[i] = (ptr_t)HBLKSIZE;
Packit d28291
    }
Packit d28291
    /* The 0 granule freelist really contains 1 granule objects.        */
Packit d28291
#   ifdef GC_GCJ_SUPPORT
Packit d28291
      if (fl[0] == ERROR_FL) return;
Packit d28291
#   endif
Packit d28291
    if ((word)(fl[0]) >= HBLKSIZE) {
Packit d28291
        return_single_freelist(fl[0], &gfl[1]);
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
#ifdef USE_PTHREAD_SPECIFIC
Packit d28291
  /* Re-set the TLS value on thread cleanup to allow thread-local       */
Packit d28291
  /* allocations to happen in the TLS destructors.                      */
Packit d28291
  /* GC_unregister_my_thread (and similar routines) will finally set    */
Packit d28291
  /* the GC_thread_key to NULL preventing this destructor from being    */
Packit d28291
  /* called repeatedly.                                                 */
Packit d28291
  static void reset_thread_key(void* v) {
Packit d28291
    pthread_setspecific(GC_thread_key, v);
Packit d28291
  }
Packit d28291
#else
Packit d28291
# define reset_thread_key 0
Packit d28291
#endif
Packit d28291
Packit d28291
/* Each thread structure must be initialized.   */
Packit d28291
/* This call must be made from the new thread.  */
Packit d28291
GC_INNER void GC_init_thread_local(GC_tlfs p)
Packit d28291
{
Packit d28291
    int i, j, res;
Packit d28291
Packit d28291
    GC_ASSERT(I_HOLD_LOCK());
Packit d28291
    if (!EXPECT(keys_initialized, TRUE)) {
Packit d28291
        GC_ASSERT((word)&GC_thread_key % sizeof(word) == 0);
Packit d28291
        res = GC_key_create(&GC_thread_key, reset_thread_key);
Packit d28291
        if (COVERT_DATAFLOW(res) != 0) {
Packit d28291
            ABORT("Failed to create key for local allocator");
Packit d28291
        }
Packit d28291
        keys_initialized = TRUE;
Packit d28291
    }
Packit d28291
    res = GC_setspecific(GC_thread_key, p);
Packit d28291
    if (COVERT_DATAFLOW(res) != 0) {
Packit d28291
        ABORT("Failed to set thread specific allocation pointers");
Packit d28291
    }
Packit d28291
    for (j = 0; j < TINY_FREELISTS; ++j) {
Packit d28291
        for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) {
Packit d28291
            p -> _freelists[i][j] = (void *)(word)1;
Packit d28291
        }
Packit d28291
#       ifdef GC_GCJ_SUPPORT
Packit d28291
            p -> gcj_freelists[j] = (void *)(word)1;
Packit d28291
#       endif
Packit d28291
    }
Packit d28291
    /* The size 0 free lists are handled like the regular free lists,   */
Packit d28291
    /* to ensure that the explicit deallocation works.  However,        */
Packit d28291
    /* allocation of a size 0 "gcj" object is always an error.          */
Packit d28291
#   ifdef GC_GCJ_SUPPORT
Packit d28291
        p -> gcj_freelists[0] = ERROR_FL;
Packit d28291
#   endif
Packit d28291
}
Packit d28291
Packit d28291
/* We hold the allocator lock.  */
Packit d28291
GC_INNER void GC_destroy_thread_local(GC_tlfs p)
Packit d28291
{
Packit d28291
    int k;
Packit d28291
Packit d28291
    /* We currently only do this from the thread itself.        */
Packit d28291
    GC_STATIC_ASSERT(THREAD_FREELISTS_KINDS <= MAXOBJKINDS);
Packit d28291
    for (k = 0; k < THREAD_FREELISTS_KINDS; ++k) {
Packit d28291
        if (k == (int)GC_n_kinds)
Packit d28291
            break; /* kind is not created */
Packit d28291
        return_freelists(p -> _freelists[k], GC_obj_kinds[k].ok_freelist);
Packit d28291
    }
Packit d28291
#   ifdef GC_GCJ_SUPPORT
Packit d28291
        return_freelists(p -> gcj_freelists, (void **)GC_gcjobjfreelist);
Packit d28291
#   endif
Packit d28291
}
Packit d28291
Packit d28291
#ifdef GC_ASSERTIONS
Packit d28291
  /* Defined in pthread_support.c or win32_threads.c. */
Packit d28291
  GC_bool GC_is_thread_tsd_valid(void *tsd);
Packit d28291
#endif
Packit d28291
Packit d28291
GC_API GC_ATTR_MALLOC void * GC_CALL GC_malloc_kind(size_t bytes, int knd)
Packit d28291
{
Packit d28291
    size_t granules;
Packit d28291
    void *tsd;
Packit d28291
    void *result;
Packit d28291
Packit d28291
#   if MAXOBJKINDS > THREAD_FREELISTS_KINDS
Packit d28291
      if (EXPECT(knd >= THREAD_FREELISTS_KINDS, FALSE)) {
Packit d28291
        return GC_malloc_kind_global(bytes, knd);
Packit d28291
      }
Packit d28291
#   endif
Packit d28291
#   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
Packit d28291
    {
Packit d28291
      GC_key_t k = GC_thread_key;
Packit d28291
Packit d28291
      if (EXPECT(0 == k, FALSE)) {
Packit d28291
        /* We haven't yet run GC_init_parallel.  That means     */
Packit d28291
        /* we also aren't locking, so this is fairly cheap.     */
Packit d28291
        return GC_malloc_kind_global(bytes, knd);
Packit d28291
      }
Packit d28291
      tsd = GC_getspecific(k);
Packit d28291
    }
Packit d28291
#   else
Packit d28291
      if (!EXPECT(keys_initialized, TRUE))
Packit d28291
        return GC_malloc_kind_global(bytes, knd);
Packit d28291
      tsd = GC_getspecific(GC_thread_key);
Packit d28291
#   endif
Packit d28291
#   if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
Packit d28291
      if (EXPECT(0 == tsd, FALSE)) {
Packit d28291
        return GC_malloc_kind_global(bytes, knd);
Packit d28291
      }
Packit d28291
#   endif
Packit d28291
    GC_ASSERT(GC_is_initialized);
Packit d28291
    GC_ASSERT(GC_is_thread_tsd_valid(tsd));
Packit d28291
    granules = ROUNDED_UP_GRANULES(bytes);
Packit d28291
    GC_FAST_MALLOC_GRANS(result, granules,
Packit d28291
                         ((GC_tlfs)tsd) -> _freelists[knd], DIRECT_GRANULES,
Packit d28291
                         knd, GC_malloc_kind_global(bytes, knd),
Packit d28291
                         (void)(knd == PTRFREE ? NULL
Packit d28291
                                               : (obj_link(result) = 0)));
Packit d28291
#   ifdef LOG_ALLOCS
Packit d28291
      GC_log_printf("GC_malloc_kind(%lu, %d) returned %p, recent GC #%lu\n",
Packit d28291
                    (unsigned long)bytes, knd, result,
Packit d28291
                    (unsigned long)GC_gc_no);
Packit d28291
#   endif
Packit d28291
    return result;
Packit d28291
}
Packit d28291
Packit d28291
#ifdef GC_GCJ_SUPPORT
Packit d28291
Packit d28291
# include "atomic_ops.h" /* for AO_compiler_barrier() */
Packit d28291
Packit d28291
# include "include/gc_gcj.h"
Packit d28291
Packit d28291
/* Gcj-style allocation without locks is extremely tricky.  The         */
Packit d28291
/* fundamental issue is that we may end up marking a free list, which   */
Packit d28291
/* has freelist links instead of "vtable" pointers.  That is usually    */
Packit d28291
/* OK, since the next object on the free list will be cleared, and      */
Packit d28291
/* will thus be interpreted as containing a zero descriptor.  That's    */
Packit d28291
/* fine if the object has not yet been initialized.  But there are      */
Packit d28291
/* interesting potential races.                                         */
Packit d28291
/* In the case of incremental collection, this seems hopeless, since    */
Packit d28291
/* the marker may run asynchronously, and may pick up the pointer to    */
Packit d28291
/* the next freelist entry (which it thinks is a vtable pointer), get   */
Packit d28291
/* suspended for a while, and then see an allocated object instead      */
Packit d28291
/* of the vtable.  This may be avoidable with either a handshake with   */
Packit d28291
/* the collector or, probably more easily, by moving the free list      */
Packit d28291
/* links to the second word of each object.  The latter isn't a         */
Packit d28291
/* universal win, since on architecture like Itanium, nonzero offsets   */
Packit d28291
/* are not necessarily free.  And there may be cache fill order issues. */
Packit d28291
/* For now, we punt with incremental GC.  This probably means that      */
Packit d28291
/* incremental GC should be enabled before we fork a second thread.     */
Packit d28291
/* Unlike the other thread local allocation calls, we assume that the   */
Packit d28291
/* collector has been explicitly initialized.                           */
Packit d28291
GC_API GC_ATTR_MALLOC void * GC_CALL GC_gcj_malloc(size_t bytes,
Packit d28291
                                    void * ptr_to_struct_containing_descr)
Packit d28291
{
Packit d28291
  if (EXPECT(GC_incremental, FALSE)) {
Packit d28291
    return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr);
Packit d28291
  } else {
Packit d28291
    size_t granules = ROUNDED_UP_GRANULES(bytes);
Packit d28291
    void *result;
Packit d28291
    void **tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))
Packit d28291
                                        -> gcj_freelists;
Packit d28291
    GC_ASSERT(GC_gcj_malloc_initialized);
Packit d28291
    GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
Packit d28291
                         GC_gcj_kind,
Packit d28291
                         GC_core_gcj_malloc(bytes,
Packit d28291
                                            ptr_to_struct_containing_descr),
Packit d28291
                         {AO_compiler_barrier();
Packit d28291
                          *(void **)result = ptr_to_struct_containing_descr;});
Packit d28291
        /* This forces the initialization of the "method ptr".          */
Packit d28291
        /* This is necessary to ensure some very subtle properties      */
Packit d28291
        /* required if a GC is run in the middle of such an allocation. */
Packit d28291
        /* Here we implicitly also assume atomicity for the free list.  */
Packit d28291
        /* and method pointer assignments.                              */
Packit d28291
        /* We must update the freelist before we store the pointer.     */
Packit d28291
        /* Otherwise a GC at this point would see a corrupted           */
Packit d28291
        /* free list.                                                   */
Packit d28291
        /* A real memory barrier is not needed, since the               */
Packit d28291
        /* action of stopping this thread will cause prior writes       */
Packit d28291
        /* to complete.                                                 */
Packit d28291
        /* We assert that any concurrent marker will stop us.           */
Packit d28291
        /* Thus it is impossible for a mark procedure to see the        */
Packit d28291
        /* allocation of the next object, but to see this object        */
Packit d28291
        /* still containing a free list pointer.  Otherwise the         */
Packit d28291
        /* marker, by misinterpreting the freelist link as a vtable     */
Packit d28291
        /* pointer, might find a random "mark descriptor" in the next   */
Packit d28291
        /* object.                                                      */
Packit d28291
    return result;
Packit d28291
  }
Packit d28291
}
Packit d28291
Packit d28291
#endif /* GC_GCJ_SUPPORT */
Packit d28291
Packit d28291
/* The thread support layer must arrange to mark thread-local   */
Packit d28291
/* free lists explicitly, since the link field is often         */
Packit d28291
/* invisible to the marker.  It knows how to find all threads;  */
Packit d28291
/* we take care of an individual thread freelist structure.     */
Packit d28291
GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p)
Packit d28291
{
Packit d28291
    ptr_t q;
Packit d28291
    int i, j;
Packit d28291
Packit d28291
    for (j = 0; j < TINY_FREELISTS; ++j) {
Packit d28291
      for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) {
Packit d28291
        q = p -> _freelists[i][j];
Packit d28291
        if ((word)q > HBLKSIZE)
Packit d28291
          GC_set_fl_marks(q);
Packit d28291
      }
Packit d28291
#     ifdef GC_GCJ_SUPPORT
Packit d28291
        if (EXPECT(j > 0, TRUE)) {
Packit d28291
          q = p -> gcj_freelists[j];
Packit d28291
          if ((word)q > HBLKSIZE)
Packit d28291
            GC_set_fl_marks(q);
Packit d28291
        }
Packit d28291
#     endif
Packit d28291
    }
Packit d28291
}
Packit d28291
Packit d28291
#if defined(GC_ASSERTIONS)
Packit d28291
    /* Check that all thread-local free-lists in p are completely marked. */
Packit d28291
    void GC_check_tls_for(GC_tlfs p)
Packit d28291
    {
Packit d28291
        int i, j;
Packit d28291
Packit d28291
        for (j = 1; j < TINY_FREELISTS; ++j) {
Packit d28291
          for (i = 0; i < THREAD_FREELISTS_KINDS; ++i) {
Packit d28291
            GC_check_fl_marks(&p->_freelists[i][j]);
Packit d28291
          }
Packit d28291
#         ifdef GC_GCJ_SUPPORT
Packit d28291
            GC_check_fl_marks(&p->gcj_freelists[j]);
Packit d28291
#         endif
Packit d28291
        }
Packit d28291
    }
Packit d28291
#endif /* GC_ASSERTIONS */
Packit d28291
Packit d28291
#endif /* THREAD_LOCAL_ALLOC */