/*
* Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
/*
* This is a reimplementation of a subset of the pthread_getspecific/setspecific
* interface. This appears to outperform the standard linuxthreads one
* by a significant margin.
* The major restriction is that each thread may only make a single
* pthread_setspecific call on a single key. (The current data structure
* doesn't really require that. The restriction should be easily removable.)
* We don't currently support the destruction functions, though that
* could be done.
* We also currently assume that only one pthread_setspecific call
* can be executed at a time, though that assumption would be easy to remove
* by adding a lock.
*/
#ifndef GC_SPECIFIC_H
#define GC_SPECIFIC_H
#include <errno.h>
EXTERN_C_BEGIN
/* Called during key creation or setspecific. */
/* For the GC we already hold lock. */
/* Currently allocated objects leak on thread exit. */
/* That's hard to fix, but OK if we allocate garbage */
/* collected memory. */
#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL)
#ifdef GC_ASSERTIONS
/* Thread-local storage is not guaranteed to be scanned by GC. */
/* We hide values stored in "specific" entries for a test purpose. */
typedef GC_hidden_pointer ts_entry_value_t;
# define TS_HIDE_VALUE(p) GC_HIDE_POINTER(p)
# define TS_REVEAL_PTR(p) GC_REVEAL_POINTER(p)
#else
typedef void * ts_entry_value_t;
# define TS_HIDE_VALUE(p) (p)
# define TS_REVEAL_PTR(p) (p)
#endif
/* An entry describing a thread-specific value for a given thread. */
/* All such accessible structures preserve the invariant that if either */
/* thread is a valid pthread id or qtid is a valid "quick thread id" */
/* for a thread, then value holds the corresponding thread specific */
/* value. This invariant must be preserved at ALL times, since */
/* asynchronous reads are allowed. */
typedef struct thread_specific_entry {
volatile AO_t qtid; /* quick thread id, only for cache */
ts_entry_value_t value;
struct thread_specific_entry *next;
pthread_t thread;
} tse;
/* We represent each thread-specific datum as two tables. The first is */
/* a cache, indexed by a "quick thread identifier". The "quick" thread */
/* identifier is an easy to compute value, which is guaranteed to */
/* determine the thread, though a thread may correspond to more than */
/* one value. We typically use the address of a page in the stack. */
/* The second is a hash table, indexed by pthread_self(). It is used */
/* only as a backup. */
/* Return the "quick thread id". Default version. Assumes page size, */
/* or at least thread stack separation, is at least 4 KB. */
/* Must be defined so that it never returns 0. (Page 0 can't really be */
/* part of any stack, since that would make 0 a valid stack pointer.) */
#define quick_thread_id() (((word)GC_approx_sp()) >> 12)
typedef struct thread_specific_data {
tse * volatile cache[TS_CACHE_SIZE];
/* A faster index to the hash table */
union ptse_ao_u hash[TS_HASH_SIZE];
pthread_mutex_t lock;
} tsd;
/* An internal version of getspecific that assumes a cache miss. */
GC_INNER void * GC_slow_getspecific(tsd * key, word qtid,
tse * volatile * cache_entry);
/* GC_INLINE is defined in gc_priv.h. */
GC_INLINE void * GC_getspecific(tsd * key)
{
word qtid = quick_thread_id();
tse * volatile * entry_ptr = &key->cache[CACHE_HASH(qtid)];
tse * entry = *entry_ptr; /* Must be loaded only once. */