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