* toplev.c (lang_independent_f_options): Remove
[official-gcc.git] / boehm-gc / hpux_irix_threads.c
blobf6e74365216d3e81725f0cbfecd642ec6b06fd12
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.
6 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
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.
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.
20 * This now also includes an initial attempt at thread support for
21 * HP/UX 11.
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.
28 # if defined(IRIX_THREADS) || defined(HPUX_THREADS)
30 # if defined(HPUX_THREADS)
31 # include <sys/semaphore.h>
32 # endif
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>
43 #undef pthread_create
44 #undef pthread_sigmask
45 #undef pthread_join
47 void GC_thr_init();
49 #if 0
50 void GC_print_sig_mask()
52 sigset_t blocked;
53 int i;
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); }
61 GC_printf0("\n");
63 #endif
65 /* We use the allocation lock to protect thread-related data structures. */
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;
100 GC_thread GC_lookup_thread(pthread_t id);
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.
107 # define SIG_SUSPEND (SIGRTMIN + 6)
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;
114 void GC_suspend_handler(int sig)
116 int dummy;
117 GC_thread me;
118 sigset_t all_sigs;
119 sigset_t old_sigs;
120 int i;
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;
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()); */
143 GC_bool GC_thr_initialized = FALSE;
145 size_t GC_min_stack_sz;
147 size_t GC_page_sz;
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. */
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)
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;
165 while (search_sz < requested_sz) {
166 search_sz *= 2;
167 index++;
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++;
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
187 *stack_size = search_sz;
188 return(result);
191 /* Caller holds allocation lock. */
192 void GC_stack_free(ptr_t stack, size_t size)
194 register int index = 0;
195 register size_t search_sz = GC_min_stack_sz;
197 while (search_sz < size) {
198 search_sz *= 2;
199 index++;
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;
208 # define THREAD_TABLE_SZ 128 /* Must be power of 2 */
209 volatile GC_thread GC_threads[THREAD_TABLE_SZ];
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)
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;
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);
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);
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)
242 int hv = ((word)id) % THREAD_TABLE_SZ;
243 register GC_thread p = GC_threads[hv];
244 register GC_thread prev = 0;
246 while (!pthread_equal(p -> id, id)) {
247 prev = p;
248 p = p -> next;
250 if (prev == 0) {
251 GC_threads[hv] = p -> next;
252 } else {
253 prev -> next = p -> next;
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)
263 int hv = ((word)id) % THREAD_TABLE_SZ;
264 register GC_thread p = GC_threads[hv];
265 register GC_thread prev = 0;
267 while (p != gc_id) {
268 prev = p;
269 p = p -> next;
271 if (prev == 0) {
272 GC_threads[hv] = p -> next;
273 } else {
274 prev -> next = p -> next;
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)
286 int hv = ((word)id) % THREAD_TABLE_SZ;
287 register GC_thread p = GC_threads[hv];
289 while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
290 return(p);
294 /* Caller holds allocation lock. */
295 void GC_stop_world()
297 pthread_t my_thread = pthread_self();
298 register int i;
299 register GC_thread p;
300 register int result;
301 struct timespec timeout;
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;
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");
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;
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);
347 pthread_mutex_unlock(&GC_suspend_lock);
348 /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
351 /* Caller holds allocation lock. */
352 void GC_start_world()
354 GC_thread p;
355 unsigned i;
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;
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);
370 # ifdef MMAP_STACKS
371 --> not really supported yet.
372 int GC_is_thread_stack(ptr_t addr)
374 register int i;
375 register GC_thread p;
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;
386 return 0;
388 # endif
390 /* We hold allocation lock. Should do exactly the right thing if the */
391 /* world is stopped. Should not fail if it isn't. */
392 void GC_push_all_stacks()
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();
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;
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;
420 # ifdef STACK_GROWS_UP
421 GC_push_all_stack(cold, hot);
422 # else
423 GC_push_all_stack(hot, cold);
424 # endif
430 /* We hold the allocation lock. */
431 void GC_thr_init()
433 GC_thread t;
434 struct sigaction act;
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;
456 int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
458 sigset_t fudged_set;
460 if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
461 fudged_set = *set;
462 sigdelset(&fudged_set, SIG_SUSPEND);
463 set = &fudged_set;
465 return(pthread_sigmask(how, set, oset));
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. */
478 void GC_thread_exit_proc(void *arg)
480 GC_thread me;
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;
489 UNLOCK();
492 int GC_pthread_join(pthread_t thread, void **retval)
494 int result;
495 GC_thread thread_gc_id;
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;
513 void * GC_start_routine(void * arg)
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;
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);
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);
585 # else
586 # define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
587 # endif
590 GC_pthread_create(pthread_t *new_thread,
591 const pthread_attr_t *attr,
592 void *(*start_routine)(void *), void *arg)
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. */
605 if (0 == si) return(ENOMEM);
606 if (0 != sem_init(&(si -> registered), 0, 0)) {
607 ABORT("sem_init failed");
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);
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);
629 pthread_attr_setstackaddr(&new_attr, stack);
630 } else {
631 my_flags |= CLIENT_OWNS_STACK;
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);
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");
652 sem_destroy(&(si -> registered));
653 pthread_attr_destroy(&new_attr); /* Not a no-op under HPUX */
654 return(result);
657 #ifndef HPUX_THREADS
658 /* For now we use the pthreads locking primitives on HP/UX */
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. */
664 /* Reasonably fast spin locks. Basically the same implementation */
665 /* as STL alloc.h. */
667 #define SLEEP_THRESHOLD 3
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
679 void GC_lock()
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;
691 if (GC_TRY_LOCK()) {
692 return;
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;
703 if (GC_TRY_LOCK()) {
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.
710 last_spins = i;
711 spin_max = high_spin_max;
712 return;
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;
722 if (i < SLEEP_THRESHOLD) {
723 sched_yield();
724 } else {
725 struct timespec ts;
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);
737 #endif /* !HPUX_THREADS */
739 # else
741 #ifndef LINT
742 int GC_no_Irix_threads;
743 #endif
745 # endif /* IRIX_THREADS */