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