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