2 * Copyright (c) 2000-2005 by Hewlett-Packard Company. All rights reserved.
4 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
5 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
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.
14 #include "private/gc_priv.h"
16 #if defined(THREAD_LOCAL_ALLOC)
19 # error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS"
22 #include "private/thread_local_alloc.h"
26 #if defined(USE_COMPILER_TLS)
28 #elif defined(USE_WIN32_COMPILER_TLS)
31 GC_key_t GC_thread_key;
33 static GC_bool keys_initialized;
35 /* Return a single nonempty freelist fl to the global one pointed to */
38 static void return_single_freelist(void *fl, void **gfl)
45 GC_ASSERT(GC_size(fl) == GC_size(*gfl));
47 qptr = &(obj_link(fl));
48 while ((word)(q = *qptr) >= HBLKSIZE)
49 qptr = &(obj_link(q));
56 /* Recover the contents of the freelist array fl into the global one gfl.*/
57 /* We hold the allocator lock. */
58 static void return_freelists(void **fl, void **gfl)
62 for (i = 1; i < TINY_FREELISTS; ++i) {
63 if ((word)(fl[i]) >= HBLKSIZE) {
64 return_single_freelist(fl[i], gfl+i);
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 fl[i] = (ptr_t)HBLKSIZE;
70 /* The 0 granule freelist really contains 1 granule objects. */
71 # ifdef GC_GCJ_SUPPORT
72 if (fl[0] == ERROR_FL) return;
74 if ((word)(fl[0]) >= HBLKSIZE) {
75 return_single_freelist(fl[0], gfl+1);
79 /* Each thread structure must be initialized. */
80 /* This call must be made from the new thread. */
81 GC_INNER void GC_init_thread_local(GC_tlfs p)
85 GC_ASSERT(I_HOLD_LOCK());
86 if (!keys_initialized) {
87 if (0 != GC_key_create(&GC_thread_key, 0)) {
88 ABORT("Failed to create key for local allocator");
90 keys_initialized = TRUE;
92 if (0 != GC_setspecific(GC_thread_key, p)) {
93 ABORT("Failed to set thread specific allocation pointers");
95 for (i = 1; i < TINY_FREELISTS; ++i) {
96 p -> ptrfree_freelists[i] = (void *)(word)1;
97 p -> normal_freelists[i] = (void *)(word)1;
98 # ifdef GC_GCJ_SUPPORT
99 p -> gcj_freelists[i] = (void *)(word)1;
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 p -> ptrfree_freelists[0] = (void *)(word)1;
107 p -> normal_freelists[0] = (void *)(word)1;
108 # ifdef GC_GCJ_SUPPORT
109 p -> gcj_freelists[0] = ERROR_FL;
113 /* We hold the allocator lock. */
114 GC_INNER void GC_destroy_thread_local(GC_tlfs p)
116 /* We currently only do this from the thread itself or from */
117 /* the fork handler for a child process. */
119 GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);
121 return_freelists(p -> ptrfree_freelists, GC_aobjfreelist);
122 return_freelists(p -> normal_freelists, GC_objfreelist);
123 # ifdef GC_GCJ_SUPPORT
124 return_freelists(p -> gcj_freelists, (void **)GC_gcjobjfreelist);
129 /* Defined in pthread_support.c or win32_threads.c. */
130 GC_bool GC_is_thread_tsd_valid(void *tsd);
133 GC_API void * GC_CALL GC_malloc(size_t bytes)
135 size_t granules = ROUNDED_UP_GRANULES(bytes);
140 # if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
141 GC_key_t k = GC_thread_key;
142 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 return GC_core_malloc(bytes);
147 tsd = GC_getspecific(k);
149 tsd = GC_getspecific(GC_thread_key);
151 # if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
152 if (EXPECT(0 == tsd, FALSE)) {
153 return GC_core_malloc(bytes);
156 GC_ASSERT(GC_is_initialized);
158 GC_ASSERT(GC_is_thread_tsd_valid(tsd));
160 tiny_fl = ((GC_tlfs)tsd) -> normal_freelists;
161 GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
162 NORMAL, GC_core_malloc(bytes), obj_link(result)=0);
164 GC_err_printf("GC_malloc(%u) = %p : %u\n",
165 (unsigned)bytes, result, (unsigned)GC_gc_no);
170 GC_API void * GC_CALL GC_malloc_atomic(size_t bytes)
172 size_t granules = ROUNDED_UP_GRANULES(bytes);
177 # if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
178 GC_key_t k = GC_thread_key;
179 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 return GC_core_malloc_atomic(bytes);
184 tsd = GC_getspecific(k);
186 tsd = GC_getspecific(GC_thread_key);
188 # if !defined(USE_COMPILER_TLS) && !defined(USE_WIN32_COMPILER_TLS)
189 if (EXPECT(0 == tsd, FALSE)) {
190 return GC_core_malloc_atomic(bytes);
193 GC_ASSERT(GC_is_initialized);
194 tiny_fl = ((GC_tlfs)tsd) -> ptrfree_freelists;
195 GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES, PTRFREE,
196 GC_core_malloc_atomic(bytes), (void)0 /* no init */);
200 #ifdef GC_GCJ_SUPPORT
202 # include "atomic_ops.h" /* for AO_compiler_barrier() */
204 # include "include/gc_gcj.h"
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 GC_API void * GC_CALL GC_gcj_malloc(size_t bytes,
227 void * ptr_to_struct_containing_descr)
229 if (GC_EXPECT(GC_incremental, 0)) {
230 return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr);
232 size_t granules = ROUNDED_UP_GRANULES(bytes);
234 void **tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))
236 GC_ASSERT(GC_gcj_malloc_initialized);
237 GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
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 */
251 /* A real memory barrier is not needed, since the */
252 /* action of stopping this thread will cause prior writes */
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 */
265 #endif /* GC_GCJ_SUPPORT */
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 GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p)
276 for (j = 0; j < TINY_FREELISTS; ++j) {
277 q = p -> ptrfree_freelists[j];
278 if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
279 q = p -> normal_freelists[j];
280 if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
281 # ifdef GC_GCJ_SUPPORT
283 q = p -> gcj_freelists[j];
284 if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
286 # endif /* GC_GCJ_SUPPORT */
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)
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]);
304 #endif /* GC_ASSERTIONS */
306 #endif /* THREAD_LOCAL_ALLOC */