6cbbad4c61eb6e9d01751ef768155723db392010
[cacao.git] / src / mm / boehm-gc / thread_local_alloc.c
1 /* 
2  * Copyright (c) 2000-2005 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 #include "config.h"
15
16 #include "private/gc_priv.h"
17
18 # if defined(THREAD_LOCAL_ALLOC)
19
20 #include "private/thread_local_alloc.h"
21 #include "gc_inline.h"
22
23 # include <stdlib.h>
24
25 #if defined(USE_COMPILER_TLS)
26   __thread
27 #elif defined(USE_WIN32_COMPILER_TLS)
28   __declspec(thread)
29 #endif
30 GC_key_t GC_thread_key;
31
32 static GC_bool keys_initialized;
33
34 /* Return a single nonempty freelist fl to the global one pointed to    */
35 /* by gfl.      */
36
37 static void return_single_freelist(void *fl, void **gfl)
38 {
39     void *q, **qptr;
40
41     if (*gfl == 0) {
42       *gfl = fl;
43     } else {
44       GC_ASSERT(GC_size(fl) == GC_size(*gfl));
45       /* Concatenate: */
46         for (qptr = &(obj_link(fl)), q = *qptr;
47              (word)q >= HBLKSIZE; qptr = &(obj_link(q)), q = *qptr);
48         GC_ASSERT(0 == q);
49         *qptr = *gfl;
50         *gfl = fl;
51     }
52 }
53
54 /* Recover the contents of the freelist array fl into the global one gfl.*/
55 /* We hold the allocator lock.                                          */
56 static void return_freelists(void **fl, void **gfl)
57 {
58     int i;
59
60     for (i = 1; i < TINY_FREELISTS; ++i) {
61         if ((word)(fl[i]) >= HBLKSIZE) {
62           return_single_freelist(fl[i], gfl+i);
63         }
64         /* Clear fl[i], since the thread structure may hang around.     */
65         /* Do it in a way that is likely to trap if we access it.       */
66         fl[i] = (ptr_t)HBLKSIZE;
67     }
68     /* The 0 granule freelist really contains 1 granule objects.        */
69 #   ifdef GC_GCJ_SUPPORT
70       if (fl[0] == ERROR_FL) return;
71 #   endif
72     if ((word)(fl[0]) >= HBLKSIZE) {
73         return_single_freelist(fl[0], gfl+1);
74     }
75 }
76
77 /* Each thread structure must be initialized.   */
78 /* This call must be made from the new thread.  */
79 void GC_init_thread_local(GC_tlfs p)
80 {
81     int i;
82
83     GC_ASSERT(I_HOLD_LOCK());
84     if (!keys_initialized) {
85         if (0 != GC_key_create(&GC_thread_key, 0)) {
86             ABORT("Failed to create key for local allocator");
87         }
88         keys_initialized = TRUE;
89     }
90     if (0 != GC_setspecific(GC_thread_key, p)) {
91         ABORT("Failed to set thread specific allocation pointers");
92     }
93     for (i = 1; i < TINY_FREELISTS; ++i) {
94         p -> ptrfree_freelists[i] = (void *)1;
95         p -> normal_freelists[i] = (void *)1;
96 #       ifdef GC_GCJ_SUPPORT
97           p -> gcj_freelists[i] = (void *)1;
98 #       endif
99     }   
100     /* Set up the size 0 free lists.    */
101     /* We now handle most of them like regular free lists, to ensure    */
102     /* That explicit deallocation works.  However, allocation of a      */
103     /* size 0 "gcj" object is always an error.                          */
104     p -> ptrfree_freelists[0] = (void *)1;
105     p -> normal_freelists[0] = (void *)1;
106 #   ifdef GC_GCJ_SUPPORT
107         p -> gcj_freelists[0] = ERROR_FL;
108 #   endif
109 }
110
111 #ifdef GC_GCJ_SUPPORT
112   extern void ** GC_gcjobjfreelist;
113 #endif
114
115 /* We hold the allocator lock.  */
116 void GC_destroy_thread_local(GC_tlfs p)
117 {
118     /* We currently only do this from the thread itself or from */
119     /* the fork handler for a child process.                    */
120 #   ifndef HANDLE_FORK
121       GC_ASSERT(GC_getspecific(GC_thread_key) == (void *)p);
122 #   endif
123     return_freelists(p -> ptrfree_freelists, GC_aobjfreelist);
124     return_freelists(p -> normal_freelists, GC_objfreelist);
125 #   ifdef GC_GCJ_SUPPORT
126         return_freelists(p -> gcj_freelists, GC_gcjobjfreelist);
127 #   endif
128 }
129
130 #if defined(GC_ASSERTIONS) && defined(GC_PTHREADS) && !defined(CYGWIN32) \
131     && !defined(GC_WIN32_PTHREADS)
132 # include <pthread.h>
133   extern char * GC_lookup_thread(pthread_t id);
134 #endif
135
136 #if defined(GC_ASSERTIONS) && defined(GC_WIN32_THREADS)
137   extern char * GC_lookup_thread(int id);
138 #endif
139
140 void * GC_malloc(size_t bytes)
141 {
142     size_t granules = ROUNDED_UP_GRANULES(bytes);
143     void *tsd;
144     void *result;
145     void **tiny_fl;
146
147 #   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
148       GC_key_t k = GC_thread_key;
149       if (EXPECT(0 == k, 0)) {
150         /* We haven't yet run GC_init_parallel.  That means     */
151         /* we also aren't locking, so this is fairly cheap.     */
152         return GC_core_malloc(bytes);
153       }
154       tsd = GC_getspecific(k);
155 #   else
156       tsd = GC_getspecific(GC_thread_key);
157 #   endif
158 #   if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
159       if (EXPECT(0 == tsd, 0)) {
160         return GC_core_malloc(bytes);
161       }
162 #   endif
163     GC_ASSERT(GC_is_initialized);
164 #   ifdef GC_ASSERTIONS
165       /* We can't check tsd correctly, since we don't have access to    */
166       /* the right declarations.  But we can check that it's close.     */
167       LOCK();
168       {
169 #       if defined(GC_WIN32_THREADS)
170           char * me = (char *)GC_lookup_thread_inner(GetCurrentThreadId());
171 #       else
172           char * me = GC_lookup_thread(pthread_self());
173 #       endif
174         GC_ASSERT((char *)tsd > me && (char *)tsd < me + 1000);
175       }
176       UNLOCK();
177 #   endif
178     tiny_fl = ((GC_tlfs)tsd) -> normal_freelists;
179     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
180                          NORMAL, GC_core_malloc(bytes), obj_link(result)=0);
181 #   ifdef LOG_ALLOCS
182       GC_err_printf("GC_malloc(%d) = %p : %d\n", bytes, result, GC_gc_no);
183 #   endif
184     return result;
185 }
186
187 void * GC_malloc_atomic(size_t bytes)
188 {
189     size_t granules = ROUNDED_UP_GRANULES(bytes);
190     void *tsd;
191     void *result;
192     void **tiny_fl;
193
194 #   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_WIN32_SPECIFIC)
195       GC_key_t k = GC_thread_key;
196       if (EXPECT(0 == k, 0)) {
197         /* We haven't yet run GC_init_parallel.  That means     */
198         /* we also aren't locking, so this is fairly cheap.     */
199         return GC_core_malloc(bytes);
200       }
201       tsd = GC_getspecific(k);
202 #   else
203       tsd = GC_getspecific(GC_thread_key);
204 #   endif
205 #   if defined(USE_PTHREAD_SPECIFIC) || defined(USE_WIN32_SPECIFIC)
206       if (EXPECT(0 == tsd, 0)) {
207         return GC_core_malloc(bytes);
208       }
209 #   endif
210     GC_ASSERT(GC_is_initialized);
211     tiny_fl = ((GC_tlfs)tsd) -> ptrfree_freelists;
212     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
213                          PTRFREE, GC_core_malloc_atomic(bytes), 0/* no init */);
214     return result;
215 }
216
217 #ifdef GC_GCJ_SUPPORT
218
219 #include "include/gc_gcj.h"
220
221 #ifdef GC_ASSERTIONS
222   extern GC_bool GC_gcj_malloc_initialized;
223 #endif
224
225 extern int GC_gcj_kind;
226
227 /* Gcj-style allocation without locks is extremely tricky.  The         */
228 /* fundamental issue is that we may end up marking a free list, which   */
229 /* has freelist links instead of "vtable" pointers.  That is usually    */
230 /* OK, since the next object on the free list will be cleared, and      */
231 /* will thus be interpreted as containg a zero descriptor.  That's fine */
232 /* if the object has not yet been initialized.  But there are           */
233 /* interesting potential races.                                         */
234 /* In the case of incremental collection, this seems hopeless, since    */
235 /* the marker may run asynchronously, and may pick up the pointer to    */
236 /* the next freelist entry (which it thinks is a vtable pointer), get   */
237 /* suspended for a while, and then see an allocated object instead      */
238 /* of the vtable.  This made be avoidable with either a handshake with  */
239 /* the collector or, probably more easily, by moving the free list      */
240 /* links to the second word of each object.  The latter isn't a         */
241 /* universal win, since on architecture like Itanium, nonzero offsets   */
242 /* are not necessarily free.  And there may be cache fill order issues. */
243 /* For now, we punt with incremental GC.  This probably means that      */
244 /* incremental GC should be enabled before we fork a second thread.     */
245 /* Unlike the other thread local allocation calls, we assume that the   */
246 /* collector has been explicitly initialized.                           */
247 void * GC_gcj_malloc(size_t bytes,
248                      void * ptr_to_struct_containing_descr)
249 {
250   if (GC_EXPECT(GC_incremental, 0)) {
251     return GC_core_gcj_malloc(bytes, ptr_to_struct_containing_descr);
252   } else {
253     size_t granules = ROUNDED_UP_GRANULES(bytes);
254     void *result;
255     void **tiny_fl = ((GC_tlfs)GC_getspecific(GC_thread_key))
256                                         -> gcj_freelists;
257     GC_ASSERT(GC_gcj_malloc_initialized);
258     GC_FAST_MALLOC_GRANS(result, granules, tiny_fl, DIRECT_GRANULES,
259                          GC_gcj_kind,
260                          GC_core_gcj_malloc(bytes,
261                                             ptr_to_struct_containing_descr),
262                          {AO_compiler_barrier();
263                           *(void **)result = ptr_to_struct_containing_descr;});
264         /* This forces the initialization of the "method ptr".          */
265         /* This is necessary to ensure some very subtle properties      */
266         /* required if a GC is run in the middle of such an allocation. */
267         /* Here we implicitly also assume atomicity for the free list.  */
268         /* and method pointer assignments.                              */
269         /* We must update the freelist before we store the pointer.     */
270         /* Otherwise a GC at this point would see a corrupted           */
271         /* free list.                                                   */
272         /* A real memory barrier is not needed, since the               */
273         /* action of stopping this thread will cause prior writes       */
274         /* to complete.                                                 */
275         /* We assert that any concurrent marker will stop us.           */
276         /* Thus it is impossible for a mark procedure to see the        */
277         /* allocation of the next object, but to see this object        */
278         /* still containing a free list pointer.  Otherwise the         */
279         /* marker, by misinterpreting the freelist link as a vtable     */
280         /* pointer, might find a random "mark descriptor" in the next   */
281         /* object.                                                      */
282     return result;
283   }
284 }
285
286 #endif /* GC_GCJ_SUPPORT */
287
288 /* The thread support layer must arrange to mark thread-local   */
289 /* free lists explicitly, since the link field is often         */
290 /* invisible to the marker.  It knows hoe to find all threads;  */
291 /* we take care of an individual thread freelist structure.     */
292 void GC_mark_thread_local_fls_for(GC_tlfs p)
293 {
294     ptr_t q;
295     int j;
296     
297     for (j = 1; j < TINY_FREELISTS; ++j) {
298       q = p -> ptrfree_freelists[j];
299       if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
300       q = p -> normal_freelists[j];
301       if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
302 #     ifdef GC_GCJ_SUPPORT
303         q = p -> gcj_freelists[j];
304         if ((word)q > HBLKSIZE) GC_set_fl_marks(q);
305 #     endif /* GC_GCJ_SUPPORT */
306     }
307 }
308
309 #if defined(GC_ASSERTIONS)
310     /* Check that all thread-local free-lists in p are completely marked.       */
311     void GC_check_tls_for(GC_tlfs p)
312     {
313         ptr_t q;
314         int j;
315         
316         for (j = 1; j < TINY_FREELISTS; ++j) {
317           q = p -> ptrfree_freelists[j];
318           if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
319           q = p -> normal_freelists[j];
320           if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
321 #         ifdef GC_GCJ_SUPPORT
322             q = p -> gcj_freelists[j];
323             if ((word)q > HBLKSIZE) GC_check_fl_marks(q);
324 #         endif /* GC_GCJ_SUPPORT */
325         }
326     }
327 #endif /* GC_ASSERTIONS */
328
329 # else  /* !THREAD_LOCAL_ALLOC  */
330
331 #   define GC_destroy_thread_local(t)
332
333 # endif /* !THREAD_LOCAL_ALLOC */
334