specific.h raw

   1  /*
   2   * Copyright (c) 2000 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  /*
  15   * This is a reimplementation of a subset of the
  16   * `pthread_getspecific`/`pthread_setspecific` interface.  This appears
  17   * to outperform the standard LinuxThreads one by a significant margin.
  18   * The major restriction is that each thread may only make a single
  19   * `pthread_setspecific()` call on a single key.  (The current data
  20   * structure does not really require that.  This restriction should be
  21   * easily removable.)  We do not currently support the destruction
  22   * functions, though that could be done.  We also currently assume that
  23   * only one `pthread_setspecific()` call can be executed at a time,
  24   * though that assumption would be easy to remove by adding a lock.
  25   */
  26  
  27  #ifndef GC_SPECIFIC_H
  28  #define GC_SPECIFIC_H
  29  
  30  #if !defined(GC_THREAD_LOCAL_ALLOC_H)
  31  #  error specific.h should be included from thread_local_alloc.h
  32  #endif
  33  
  34  #include <errno.h>
  35  
  36  EXTERN_C_BEGIN
  37  
  38  /*
  39   * Called during key creation or by `GC_setspecific()`.  For the GC we
  40   * already hold the allocator lock.  Currently allocated objects leak
  41   * on thread exit.  It is hard to avoid, but OK if we allocate garbage
  42   * collected memory.
  43   */
  44  #define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL)
  45  
  46  #define TS_CACHE_SIZE 1024
  47  #define TS_CACHE_HASH(n) ((((n) >> 8) ^ (n)) & (TS_CACHE_SIZE - 1))
  48  
  49  #define TS_HASH_SIZE 1024
  50  #define TS_HASH(p) ((unsigned)((ADDR(p) >> 8) ^ ADDR(p)) & (TS_HASH_SIZE - 1))
  51  
  52  #ifdef GC_ASSERTIONS
  53  /*
  54   * Thread-local storage is not guaranteed to be scanned by the collector.
  55   * We hide values stored in "specific" entries for a test purpose.
  56   */
  57  typedef GC_hidden_pointer ts_entry_value_t;
  58  #  define TS_HIDE_VALUE(p) GC_HIDE_NZ_POINTER(p)
  59  #  define TS_REVEAL_PTR(p) GC_REVEAL_NZ_POINTER(p)
  60  #else
  61  typedef void *ts_entry_value_t;
  62  #  define TS_HIDE_VALUE(p) (p)
  63  #  define TS_REVEAL_PTR(p) (p)
  64  #endif
  65  
  66  /*
  67   * An entry describing a thread-specific value for a given thread.
  68   * All such accessible structures preserve the invariant that if
  69   * either thread is a valid `pthreads` id or `qtid` is a valid
  70   * "quick thread identifier" for a thread, then value holds the
  71   * corresponding thread specific value.  This invariant must be
  72   * preserved at all times, since asynchronous reads are allowed.
  73   */
  74  typedef struct thread_specific_entry {
  75    volatile AO_t qtid; /*< a "quick thread identifier", only for cache */
  76    ts_entry_value_t value;
  77    struct thread_specific_entry *next;
  78    pthread_t thread;
  79  } tse;
  80  
  81  /*
  82   * We represent each thread-specific datum as two tables.  The first is
  83   * a cache, indexed by a "quick thread identifier".  The latter one is
  84   * an easy-to-compute value, which is guaranteed to determine the thread,
  85   * though a thread may correspond to more than one value.  We typically
  86   * use the address of a page in the stack.  The second is a hash table,
  87   * indexed by `pthread_self()`.  It is used only as a backup.
  88   */
  89  
  90  /*
  91   * Return the "quick thread identifier".  The default variant.
  92   * Assumes the page size, or at least thread stack separation, is at
  93   * least 4 KB.  Must be defined so that it never returns 0.  (Page zero
  94   * cannot really be part of any stack, since that would make 0 a valid
  95   * stack pointer.)
  96   */
  97  #define ts_quick_thread_id() ((size_t)(ADDR(GC_approx_sp()) >> 12))
  98  
  99  #define INVALID_QTID ((size_t)0)
 100  #define INVALID_THREADID ((pthread_t)0)
 101  
 102  typedef struct thread_specific_data {
 103    tse *volatile cache[TS_CACHE_SIZE]; /*< a faster index to the hash table */
 104    tse *hash[TS_HASH_SIZE];
 105    pthread_mutex_t lock;
 106  } tsd;
 107  
 108  typedef tsd *GC_key_t;
 109  
 110  #define GC_key_create(key, d) GC_key_create_inner(key)
 111  GC_INNER int GC_key_create_inner(tsd **key_ptr);
 112  
 113  /*
 114   * Set the thread-local value associated with `key`.
 115   * Should not be used to overwrite a previously set value.
 116   */
 117  GC_INNER int GC_setspecific(tsd *key, void *value);
 118  
 119  /* This function is called on thread exit. */
 120  #define GC_remove_specific(key) \
 121    GC_remove_specific_after_fork(key, pthread_self())
 122  
 123  /*
 124   * Remove thread-specific data for a given thread.  This function is called
 125   * at `fork` from the child process for all threads except for the survived
 126   * one.
 127   */
 128  GC_INNER void GC_remove_specific_after_fork(tsd *key, pthread_t t);
 129  
 130  #ifdef CAN_HANDLE_FORK
 131  /*
 132   * Update thread-specific data for the survived thread of the child process.
 133   * Should be called once after removing thread-specific data for other
 134   * threads.
 135   */
 136  GC_INNER void GC_update_specific_after_fork(tsd *key);
 137  #endif
 138  
 139  /*
 140   * An internal variant of `GC_getspecific` that assumes a cache miss.
 141   * Note that even the slow path does not lock.
 142   */
 143  GC_INNER void *GC_slow_getspecific(tsd *key, size_t qtid,
 144                                     tse *volatile *entry_ptr);
 145  
 146  GC_INLINE void *
 147  GC_getspecific(tsd *key)
 148  {
 149    size_t qtid = ts_quick_thread_id();
 150    tse *volatile *entry_ptr = &key->cache[TS_CACHE_HASH(qtid)];
 151    const tse *entry = *entry_ptr; /*< must be loaded only once */
 152  
 153    GC_ASSERT(qtid != INVALID_QTID);
 154    if (LIKELY(entry->qtid == qtid)) {
 155      GC_ASSERT(entry->thread == pthread_self());
 156      return TS_REVEAL_PTR(entry->value);
 157    }
 158  
 159    return GC_slow_getspecific(key, qtid, entry_ptr);
 160  }
 161  
 162  EXTERN_C_END
 163  
 164  #endif /* GC_SPECIFIC_H */
 165