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