Annotation of OpenXM_contrib2/asir2000/gc/irix_threads.c, Revision 1.2
1.1 noro 1: /*
1.2 ! noro 2: * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
! 3: * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
! 4: * Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
1.1 noro 5: *
6: * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7: * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
8: *
9: * Permission is hereby granted to use or copy this program
10: * for any purpose, provided the above notices are retained on all copies.
11: * Permission to modify the code and to distribute modified code is granted,
12: * provided the above notices are retained, and a notice that the code was
13: * modified is included with the above copyright notice.
14: */
15: /*
16: * Support code for Irix (>=6.2) Pthreads. This relies on properties
17: * not guaranteed by the Pthread standard. It may or may not be portable
18: * to other implementations.
19: *
1.2 ! noro 20: * This now also includes an initial attempt at thread support for
! 21: * HP/UX 11.
! 22: *
1.1 noro 23: * Note that there is a lot of code duplication between linux_threads.c
1.2 ! noro 24: * and hpux_irix_threads.c; any changes made here may need to be reflected
1.1 noro 25: * there too.
26: */
27:
1.2 ! noro 28: # if defined(GC_IRIX_THREADS) || defined(IRIX_THREADS)
1.1 noro 29:
1.2 ! noro 30: # include "private/gc_priv.h"
1.1 noro 31: # include <pthread.h>
32: # include <semaphore.h>
33: # include <time.h>
34: # include <errno.h>
35: # include <unistd.h>
36: # include <sys/mman.h>
37: # include <sys/time.h>
38:
39: #undef pthread_create
40: #undef pthread_sigmask
41: #undef pthread_join
1.2 ! noro 42: #undef pthread_detach
1.1 noro 43:
44: void GC_thr_init();
45:
46: #if 0
47: void GC_print_sig_mask()
48: {
49: sigset_t blocked;
50: int i;
51:
52: if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
53: ABORT("pthread_sigmask");
54: GC_printf0("Blocked: ");
55: for (i = 1; i <= MAXSIG; i++) {
56: if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
57: }
58: GC_printf0("\n");
59: }
60: #endif
61:
62: /* We use the allocation lock to protect thread-related data structures. */
63:
64: /* The set of all known threads. We intercept thread creation and */
65: /* joins. We never actually create detached threads. We allocate all */
66: /* new thread stacks ourselves. These allow us to maintain this */
67: /* data structure. */
68: /* Protected by GC_thr_lock. */
69: /* Some of this should be declared volatile, but that's incosnsistent */
70: /* with some library routine declarations. */
71: typedef struct GC_Thread_Rep {
72: struct GC_Thread_Rep * next; /* More recently allocated threads */
73: /* with a given pthread id come */
74: /* first. (All but the first are */
75: /* guaranteed to be dead, but we may */
76: /* not yet have registered the join.) */
77: pthread_t id;
78: word stop;
79: # define NOT_STOPPED 0
80: # define PLEASE_STOP 1
81: # define STOPPED 2
82: word flags;
83: # define FINISHED 1 /* Thread has exited. */
84: # define DETACHED 2 /* Thread is intended to be detached. */
85: # define CLIENT_OWNS_STACK 4
86: /* Stack was supplied by client. */
87: ptr_t stack;
88: ptr_t stack_ptr; /* Valid only when stopped. */
89: /* But must be within stack region at */
90: /* all times. */
91: size_t stack_size; /* 0 for original thread. */
92: void * status; /* Used only to avoid premature */
93: /* reclamation of any data it might */
94: /* reference. */
95: } * GC_thread;
96:
97: GC_thread GC_lookup_thread(pthread_t id);
98:
99: /*
100: * The only way to suspend threads given the pthread interface is to send
101: * signals. Unfortunately, this means we have to reserve
102: * a signal, and intercept client calls to change the signal mask.
103: */
104: # define SIG_SUSPEND (SIGRTMIN + 6)
105:
106: pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
107: /* Number of threads stopped so far */
108: pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
109: pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
110:
111: void GC_suspend_handler(int sig)
112: {
113: int dummy;
114: GC_thread me;
115: sigset_t all_sigs;
116: sigset_t old_sigs;
117: int i;
118:
119: if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
120: me = GC_lookup_thread(pthread_self());
121: /* The lookup here is safe, since I'm doing this on behalf */
122: /* of a thread which holds the allocation lock in order */
123: /* to stop the world. Thus concurrent modification of the */
124: /* data structure is impossible. */
125: if (PLEASE_STOP != me -> stop) {
126: /* Misdirected signal. */
127: pthread_mutex_unlock(&GC_suspend_lock);
128: return;
129: }
130: pthread_mutex_lock(&GC_suspend_lock);
131: me -> stack_ptr = (ptr_t)(&dummy);
132: me -> stop = STOPPED;
133: pthread_cond_signal(&GC_suspend_ack_cv);
134: pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
135: pthread_mutex_unlock(&GC_suspend_lock);
136: /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
137: }
138:
139:
140: GC_bool GC_thr_initialized = FALSE;
141:
142: size_t GC_min_stack_sz;
143:
144: size_t GC_page_sz;
145:
146: # define N_FREE_LISTS 25
147: ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
148: /* GC_stack_free_lists[i] is free list for stacks of */
149: /* size GC_min_stack_sz*2**i. */
150: /* Free lists are linked through first word. */
151:
152: /* Return a stack of size at least *stack_size. *stack_size is */
153: /* replaced by the actual stack size. */
154: /* Caller holds allocation lock. */
155: ptr_t GC_stack_alloc(size_t * stack_size)
156: {
157: register size_t requested_sz = *stack_size;
158: register size_t search_sz = GC_min_stack_sz;
159: register int index = 0; /* = log2(search_sz/GC_min_stack_sz) */
160: register ptr_t result;
161:
162: while (search_sz < requested_sz) {
163: search_sz *= 2;
164: index++;
165: }
166: if ((result = GC_stack_free_lists[index]) == 0
167: && (result = GC_stack_free_lists[index+1]) != 0) {
168: /* Try next size up. */
169: search_sz *= 2; index++;
170: }
171: if (result != 0) {
172: GC_stack_free_lists[index] = *(ptr_t *)result;
173: } else {
174: result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz);
175: result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1));
176: /* Protect hottest page to detect overflow. */
1.2 ! noro 177: # ifdef STACK_GROWS_UP
! 178: /* mprotect(result + search_sz, GC_page_sz, PROT_NONE); */
! 179: # else
! 180: /* mprotect(result, GC_page_sz, PROT_NONE); */
! 181: result += GC_page_sz;
! 182: # endif
1.1 noro 183: }
184: *stack_size = search_sz;
185: return(result);
186: }
187:
188: /* Caller holds allocation lock. */
189: void GC_stack_free(ptr_t stack, size_t size)
190: {
191: register int index = 0;
192: register size_t search_sz = GC_min_stack_sz;
193:
194: while (search_sz < size) {
195: search_sz *= 2;
196: index++;
197: }
198: if (search_sz != size) ABORT("Bad stack size");
199: *(ptr_t *)stack = GC_stack_free_lists[index];
200: GC_stack_free_lists[index] = stack;
201: }
202:
203:
204:
205: # define THREAD_TABLE_SZ 128 /* Must be power of 2 */
206: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
207:
1.2 ! noro 208: void GC_push_thread_structures GC_PROTO((void))
! 209: {
! 210: GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
! 211: }
! 212:
1.1 noro 213: /* Add a thread to GC_threads. We assume it wasn't already there. */
214: /* Caller holds allocation lock. */
215: GC_thread GC_new_thread(pthread_t id)
216: {
217: int hv = ((word)id) % THREAD_TABLE_SZ;
218: GC_thread result;
219: static struct GC_Thread_Rep first_thread;
220: static GC_bool first_thread_used = FALSE;
221:
222: if (!first_thread_used) {
223: result = &first_thread;
224: first_thread_used = TRUE;
225: /* Dont acquire allocation lock, since we may already hold it. */
226: } else {
227: result = (struct GC_Thread_Rep *)
1.2 ! noro 228: GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
1.1 noro 229: }
230: if (result == 0) return(0);
231: result -> id = id;
232: result -> next = GC_threads[hv];
233: GC_threads[hv] = result;
234: /* result -> flags = 0; */
235: /* result -> stop = 0; */
236: return(result);
237: }
238:
239: /* Delete a thread from GC_threads. We assume it is there. */
240: /* (The code intentionally traps if it wasn't.) */
241: /* Caller holds allocation lock. */
242: void GC_delete_thread(pthread_t id)
243: {
244: int hv = ((word)id) % THREAD_TABLE_SZ;
245: register GC_thread p = GC_threads[hv];
246: register GC_thread prev = 0;
247:
248: while (!pthread_equal(p -> id, id)) {
249: prev = p;
250: p = p -> next;
251: }
252: if (prev == 0) {
253: GC_threads[hv] = p -> next;
254: } else {
255: prev -> next = p -> next;
256: }
257: }
258:
259: /* If a thread has been joined, but we have not yet */
260: /* been notified, then there may be more than one thread */
261: /* in the table with the same pthread id. */
262: /* This is OK, but we need a way to delete a specific one. */
263: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
264: {
265: int hv = ((word)id) % THREAD_TABLE_SZ;
266: register GC_thread p = GC_threads[hv];
267: register GC_thread prev = 0;
268:
269: while (p != gc_id) {
270: prev = p;
271: p = p -> next;
272: }
273: if (prev == 0) {
274: GC_threads[hv] = p -> next;
275: } else {
276: prev -> next = p -> next;
277: }
278: }
279:
280: /* Return a GC_thread corresponding to a given thread_t. */
281: /* Returns 0 if it's not there. */
282: /* Caller holds allocation lock or otherwise inhibits */
283: /* updates. */
284: /* If there is more than one thread with the given id we */
285: /* return the most recent one. */
286: GC_thread GC_lookup_thread(pthread_t id)
287: {
288: int hv = ((word)id) % THREAD_TABLE_SZ;
289: register GC_thread p = GC_threads[hv];
290:
291: while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
292: return(p);
293: }
294:
295:
296: /* Caller holds allocation lock. */
297: void GC_stop_world()
298: {
299: pthread_t my_thread = pthread_self();
300: register int i;
301: register GC_thread p;
302: register int result;
303: struct timespec timeout;
304:
305: for (i = 0; i < THREAD_TABLE_SZ; i++) {
306: for (p = GC_threads[i]; p != 0; p = p -> next) {
307: if (p -> id != my_thread) {
308: if (p -> flags & FINISHED) {
309: p -> stop = STOPPED;
310: continue;
311: }
312: p -> stop = PLEASE_STOP;
313: result = pthread_kill(p -> id, SIG_SUSPEND);
314: /* GC_printf1("Sent signal to 0x%x\n", p -> id); */
315: switch(result) {
316: case ESRCH:
317: /* Not really there anymore. Possible? */
318: p -> stop = STOPPED;
319: break;
320: case 0:
321: break;
322: default:
323: ABORT("pthread_kill failed");
324: }
325: }
326: }
327: }
328: pthread_mutex_lock(&GC_suspend_lock);
329: for (i = 0; i < THREAD_TABLE_SZ; i++) {
330: for (p = GC_threads[i]; p != 0; p = p -> next) {
331: while (p -> id != my_thread && p -> stop != STOPPED) {
332: clock_gettime(CLOCK_REALTIME, &timeout);
333: timeout.tv_nsec += 50000000; /* 50 msecs */
334: if (timeout.tv_nsec >= 1000000000) {
335: timeout.tv_nsec -= 1000000000;
336: ++timeout.tv_sec;
337: }
338: result = pthread_cond_timedwait(&GC_suspend_ack_cv,
339: &GC_suspend_lock,
340: &timeout);
341: if (result == ETIMEDOUT) {
342: /* Signal was lost or misdirected. Try again. */
343: /* Duplicate signals should be benign. */
344: result = pthread_kill(p -> id, SIG_SUSPEND);
345: }
346: }
347: }
348: }
349: pthread_mutex_unlock(&GC_suspend_lock);
350: /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
351: }
352:
353: /* Caller holds allocation lock. */
354: void GC_start_world()
355: {
356: GC_thread p;
357: unsigned i;
358:
359: /* GC_printf0("World starting\n"); */
360: for (i = 0; i < THREAD_TABLE_SZ; i++) {
361: for (p = GC_threads[i]; p != 0; p = p -> next) {
362: p -> stop = NOT_STOPPED;
363: }
364: }
365: pthread_mutex_lock(&GC_suspend_lock);
366: /* All other threads are at pthread_cond_wait in signal handler. */
367: /* Otherwise we couldn't have acquired the lock. */
368: pthread_mutex_unlock(&GC_suspend_lock);
369: pthread_cond_broadcast(&GC_continue_cv);
370: }
371:
372: # ifdef MMAP_STACKS
373: --> not really supported yet.
374: int GC_is_thread_stack(ptr_t addr)
375: {
376: register int i;
377: register GC_thread p;
378:
379: for (i = 0; i < THREAD_TABLE_SZ; i++) {
380: for (p = GC_threads[i]; p != 0; p = p -> next) {
381: if (p -> stack_size != 0) {
382: if (p -> stack <= addr &&
383: addr < p -> stack + p -> stack_size)
384: return 1;
385: }
386: }
387: }
388: return 0;
389: }
390: # endif
391:
1.2 ! noro 392: /* We hold allocation lock. Should do exactly the right thing if the */
! 393: /* world is stopped. Should not fail if it isn't. */
1.1 noro 394: void GC_push_all_stacks()
395: {
396: register int i;
397: register GC_thread p;
398: register ptr_t sp = GC_approx_sp();
1.2 ! noro 399: register ptr_t hot, cold;
1.1 noro 400: pthread_t me = pthread_self();
401:
402: if (!GC_thr_initialized) GC_thr_init();
403: /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
404: for (i = 0; i < THREAD_TABLE_SZ; i++) {
405: for (p = GC_threads[i]; p != 0; p = p -> next) {
406: if (p -> flags & FINISHED) continue;
407: if (pthread_equal(p -> id, me)) {
1.2 ! noro 408: hot = GC_approx_sp();
1.1 noro 409: } else {
1.2 ! noro 410: hot = p -> stack_ptr;
1.1 noro 411: }
412: if (p -> stack_size != 0) {
1.2 ! noro 413: # ifdef STACK_GROWS_UP
! 414: cold = p -> stack;
! 415: # else
! 416: cold = p -> stack + p -> stack_size;
! 417: # endif
1.1 noro 418: } else {
419: /* The original stack. */
1.2 ! noro 420: cold = GC_stackbottom;
1.1 noro 421: }
1.2 ! noro 422: # ifdef STACK_GROWS_UP
! 423: GC_push_all_stack(cold, hot);
! 424: # else
! 425: GC_push_all_stack(hot, cold);
! 426: # endif
1.1 noro 427: }
428: }
429: }
430:
431:
432: /* We hold the allocation lock. */
433: void GC_thr_init()
434: {
435: GC_thread t;
436: struct sigaction act;
437:
438: if (GC_thr_initialized) return;
439: GC_thr_initialized = TRUE;
440: GC_min_stack_sz = HBLKSIZE;
441: GC_page_sz = sysconf(_SC_PAGESIZE);
442: (void) sigaction(SIG_SUSPEND, 0, &act);
443: if (act.sa_handler != SIG_DFL)
444: ABORT("Previously installed SIG_SUSPEND handler");
445: /* Install handler. */
446: act.sa_handler = GC_suspend_handler;
447: act.sa_flags = SA_RESTART;
448: (void) sigemptyset(&act.sa_mask);
449: if (0 != sigaction(SIG_SUSPEND, &act, 0))
450: ABORT("Failed to install SIG_SUSPEND handler");
451: /* Add the initial thread, so we can stop it. */
452: t = GC_new_thread(pthread_self());
453: t -> stack_size = 0;
454: t -> stack_ptr = (ptr_t)(&t);
455: t -> flags = DETACHED;
456: }
457:
458: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
459: {
460: sigset_t fudged_set;
461:
462: if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
463: fudged_set = *set;
464: sigdelset(&fudged_set, SIG_SUSPEND);
465: set = &fudged_set;
466: }
467: return(pthread_sigmask(how, set, oset));
468: }
469:
470: struct start_info {
471: void *(*start_routine)(void *);
472: void *arg;
473: word flags;
474: ptr_t stack;
475: size_t stack_size;
476: sem_t registered; /* 1 ==> in our thread table, but */
477: /* parent hasn't yet noticed. */
478: };
479:
480: void GC_thread_exit_proc(void *arg)
481: {
482: GC_thread me;
483:
484: LOCK();
485: me = GC_lookup_thread(pthread_self());
486: if (me -> flags & DETACHED) {
487: GC_delete_thread(pthread_self());
488: } else {
489: me -> flags |= FINISHED;
490: }
491: UNLOCK();
492: }
493:
494: int GC_pthread_join(pthread_t thread, void **retval)
495: {
496: int result;
497: GC_thread thread_gc_id;
498:
499: LOCK();
500: thread_gc_id = GC_lookup_thread(thread);
501: /* This is guaranteed to be the intended one, since the thread id */
502: /* cant have been recycled by pthreads. */
503: UNLOCK();
504: result = pthread_join(thread, retval);
505: /* Some versions of the Irix pthreads library can erroneously */
506: /* return EINTR when the call succeeds. */
507: if (EINTR == result) result = 0;
1.2 ! noro 508: if (result == 0) {
! 509: LOCK();
! 510: /* Here the pthread thread id may have been recycled. */
! 511: GC_delete_gc_thread(thread, thread_gc_id);
! 512: UNLOCK();
! 513: }
! 514: return result;
! 515: }
! 516:
! 517: int GC_pthread_detach(pthread_t thread)
! 518: {
! 519: int result;
! 520: GC_thread thread_gc_id;
! 521:
1.1 noro 522: LOCK();
1.2 ! noro 523: thread_gc_id = GC_lookup_thread(thread);
1.1 noro 524: UNLOCK();
1.2 ! noro 525: result = REAL_FUNC(pthread_detach)(thread);
! 526: if (result == 0) {
! 527: LOCK();
! 528: thread_gc_id -> flags |= DETACHED;
! 529: /* Here the pthread thread id may have been recycled. */
! 530: if (thread_gc_id -> flags & FINISHED) {
! 531: GC_delete_gc_thread(thread, thread_gc_id);
! 532: }
! 533: UNLOCK();
! 534: }
1.1 noro 535: return result;
536: }
537:
538: void * GC_start_routine(void * arg)
539: {
540: struct start_info * si = arg;
541: void * result;
542: GC_thread me;
543: pthread_t my_pthread;
544: void *(*start)(void *);
545: void *start_arg;
546:
547: my_pthread = pthread_self();
548: /* If a GC occurs before the thread is registered, that GC will */
549: /* ignore this thread. That's fine, since it will block trying to */
550: /* acquire the allocation lock, and won't yet hold interesting */
551: /* pointers. */
552: LOCK();
553: /* We register the thread here instead of in the parent, so that */
554: /* we don't need to hold the allocation lock during pthread_create. */
555: /* Holding the allocation lock there would make REDIRECT_MALLOC */
556: /* impossible. It probably still doesn't work, but we're a little */
557: /* closer ... */
558: /* This unfortunately means that we have to be careful the parent */
559: /* doesn't try to do a pthread_join before we're registered. */
560: me = GC_new_thread(my_pthread);
561: me -> flags = si -> flags;
562: me -> stack = si -> stack;
563: me -> stack_size = si -> stack_size;
564: me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
565: UNLOCK();
566: start = si -> start_routine;
567: start_arg = si -> arg;
568: sem_post(&(si -> registered));
569: pthread_cleanup_push(GC_thread_exit_proc, 0);
570: result = (*start)(start_arg);
571: me -> status = result;
572: me -> flags |= FINISHED;
573: pthread_cleanup_pop(1);
574: /* This involves acquiring the lock, ensuring that we can't exit */
575: /* while a collection that thinks we're alive is trying to stop */
576: /* us. */
577: return(result);
578: }
579:
1.2 ! noro 580: # define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
! 581:
1.1 noro 582: int
583: GC_pthread_create(pthread_t *new_thread,
584: const pthread_attr_t *attr,
585: void *(*start_routine)(void *), void *arg)
586: {
587: int result;
588: GC_thread t;
589: void * stack;
590: size_t stacksize;
591: pthread_attr_t new_attr;
592: int detachstate;
593: word my_flags = 0;
594: struct start_info * si = GC_malloc(sizeof(struct start_info));
595: /* This is otherwise saved only in an area mmapped by the thread */
596: /* library, which isn't visible to the collector. */
597:
598: if (0 == si) return(ENOMEM);
1.2 ! noro 599: if (0 != sem_init(&(si -> registered), 0, 0)) {
! 600: ABORT("sem_init failed");
! 601: }
1.1 noro 602: si -> start_routine = start_routine;
603: si -> arg = arg;
604: LOCK();
605: if (!GC_thr_initialized) GC_thr_init();
606: if (NULL == attr) {
607: stack = 0;
608: (void) pthread_attr_init(&new_attr);
609: } else {
1.2 ! noro 610: copy_attr(&new_attr, attr);
1.1 noro 611: pthread_attr_getstackaddr(&new_attr, &stack);
612: }
613: pthread_attr_getstacksize(&new_attr, &stacksize);
614: pthread_attr_getdetachstate(&new_attr, &detachstate);
615: if (stacksize < GC_min_stack_sz) ABORT("Stack too small");
616: if (0 == stack) {
617: stack = (void *)GC_stack_alloc(&stacksize);
618: if (0 == stack) {
619: UNLOCK();
620: return(ENOMEM);
621: }
622: pthread_attr_setstackaddr(&new_attr, stack);
623: } else {
624: my_flags |= CLIENT_OWNS_STACK;
625: }
626: if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
627: si -> flags = my_flags;
628: si -> stack = stack;
629: si -> stack_size = stacksize;
630: result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
631: if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) {
632: GC_stack_free(stack, stacksize);
633: }
634: UNLOCK();
635: /* Wait until child has been added to the thread table. */
636: /* This also ensures that we hold onto si until the child is done */
637: /* with it. Thus it doesn't matter whether it is otherwise */
638: /* visible to the collector. */
1.2 ! noro 639: while (0 != sem_wait(&(si -> registered))) {
! 640: if (errno != EINTR) {
! 641: GC_printf1("Sem_wait: errno = %ld\n", (unsigned long) errno);
! 642: ABORT("sem_wait failed");
! 643: }
! 644: }
1.1 noro 645: sem_destroy(&(si -> registered));
1.2 ! noro 646: pthread_attr_destroy(&new_attr); /* Probably unnecessary under Irix */
1.1 noro 647: return(result);
648: }
649:
1.2 ! noro 650: VOLATILE GC_bool GC_collecting = 0;
! 651: /* A hint that we're in the collector and */
1.1 noro 652: /* holding the allocation lock for an */
653: /* extended period. */
654:
655: /* Reasonably fast spin locks. Basically the same implementation */
1.2 ! noro 656: /* as STL alloc.h. */
! 657:
! 658: #define SLEEP_THRESHOLD 3
1.1 noro 659:
660: unsigned long GC_allocate_lock = 0;
1.2 ! noro 661: # define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock,1)
! 662: # define GC_LOCK_TAKEN GC_allocate_lock
1.1 noro 663:
664: void GC_lock()
665: {
666: # define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
667: # define high_spin_max 1000 /* spin cycles for multiprocessor */
668: static unsigned spin_max = low_spin_max;
669: unsigned my_spin_max;
670: static unsigned last_spins = 0;
671: unsigned my_last_spins;
672: volatile unsigned junk;
673: # define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
674: int i;
675:
1.2 ! noro 676: if (GC_TRY_LOCK()) {
1.1 noro 677: return;
678: }
679: junk = 0;
680: my_spin_max = spin_max;
681: my_last_spins = last_spins;
682: for (i = 0; i < my_spin_max; i++) {
683: if (GC_collecting) goto yield;
1.2 ! noro 684: if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
1.1 noro 685: PAUSE;
686: continue;
687: }
1.2 ! noro 688: if (GC_TRY_LOCK()) {
1.1 noro 689: /*
690: * got it!
691: * Spinning worked. Thus we're probably not being scheduled
692: * against the other process with which we were contending.
693: * Thus it makes sense to spin longer the next time.
694: */
695: last_spins = i;
696: spin_max = high_spin_max;
697: return;
698: }
699: }
700: /* We are probably being scheduled against the other process. Sleep. */
701: spin_max = low_spin_max;
702: yield:
703: for (i = 0;; ++i) {
1.2 ! noro 704: if (GC_TRY_LOCK()) {
1.1 noro 705: return;
706: }
707: if (i < SLEEP_THRESHOLD) {
708: sched_yield();
709: } else {
710: struct timespec ts;
711:
712: if (i > 26) i = 26;
713: /* Don't wait for more than about 60msecs, even */
714: /* under extreme contention. */
715: ts.tv_sec = 0;
716: ts.tv_nsec = 1 << i;
717: nanosleep(&ts, 0);
718: }
719: }
720: }
721:
722: # else
723:
724: #ifndef LINT
725: int GC_no_Irix_threads;
726: #endif
727:
728: # endif /* IRIX_THREADS */
729:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>