Boehm now uses a config.h instead of -D defines to force a recompilation
[cacao.git] / mm / boehm-gc / aix_irix_threads.c
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-2003 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 and for AIX pthreads.
17  * This relies on properties
18  * not guaranteed by the Pthread standard.  It may or may not be portable
19  * to other implementations.
20  *
21  * Note that there is a lot of code duplication between this file and
22  * (pthread_support.c, pthread_stop_world.c).  They should be merged.
23  * Pthread_support.c should be directly usable.
24  *
25  * Please avoid adding new ports here; use the generic pthread support
26  * as a base instead.
27  */
28
29 #include "config.h"
30
31 # if defined(GC_IRIX_THREADS) || defined(GC_AIX_THREADS)
32
33 # include "private/gc_priv.h"
34 # include <pthread.h>
35 # include <assert.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 #if defined(GC_IRIX_THREADS) && !defined(MUTEX_RECURSIVE_NP)
48 #define MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
49 #endif
50
51 void GC_thr_init();
52
53 #if 0
54 void GC_print_sig_mask()
55 {
56     sigset_t blocked;
57     int i;
58
59     if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
60         ABORT("pthread_sigmask");
61     GC_printf0("Blocked: ");
62     for (i = 1; i <= MAXSIG; i++) {
63         if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
64     }
65     GC_printf0("\n");
66 }
67 #endif
68
69 /* We use the allocation lock to protect thread-related data structures. */
70
71 /* The set of all known threads.  We intercept thread creation and      */
72 /* joins.  We never actually create detached threads.  We allocate all  */
73 /* new thread stacks ourselves.  These allow us to maintain this        */
74 /* data structure.                                                      */
75 /* Protected by GC_thr_lock.                                            */
76 /* Some of this should be declared volatile, but that's incosnsistent   */
77 /* with some library routine declarations.                              */
78 typedef struct GC_Thread_Rep {
79     struct GC_Thread_Rep * next;  /* More recently allocated threads    */
80                                   /* with a given pthread id come       */
81                                   /* first.  (All but the first are     */
82                                   /* guaranteed to be dead, but we may  */
83                                   /* not yet have registered the join.) */
84     pthread_t id;
85     word stop;
86 #       define NOT_STOPPED 0
87 #       define PLEASE_STOP 1
88 #       define STOPPED 2
89     word flags;
90 #       define FINISHED 1       /* Thread has exited.   */
91 #       define DETACHED 2       /* Thread is intended to be detached.   */
92     ptr_t stack_cold;           /* cold end of the stack                */
93     ptr_t stack_hot;            /* Valid only when stopped. */
94                                 /* But must be within stack region at   */
95                                 /* all times.                           */
96     void * status;              /* Used only to avoid premature         */
97                                 /* reclamation of any data it might     */
98                                 /* reference.                           */
99 } * GC_thread;
100
101 GC_thread GC_lookup_thread(pthread_t id);
102
103 /*
104  * The only way to suspend threads given the pthread interface is to send
105  * signals.  Unfortunately, this means we have to reserve
106  * a signal, and intercept client calls to change the signal mask.
107  */
108 #if 0 /* DOB: 6.1 */
109 # if defined(GC_AIX_THREADS)
110 #   define SIG_SUSPEND SIGUSR1
111 # else
112 #   define SIG_SUSPEND (SIGRTMIN + 6)
113 # endif
114 #endif
115
116 pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
117                                 /* Number of threads stopped so far     */
118 pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER;
119 pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER;
120
121 int cacao_suspendhandler(void *);
122
123 void GC_suspend_handler(int sig, siginfo_t *info, void *uctx)
124 {
125     int dummy;
126     GC_thread me;
127     sigset_t all_sigs;
128     sigset_t old_sigs;
129     int i;
130
131     if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
132
133         if (cacao_suspendhandler(uctx))
134                 return;
135         
136     me = GC_lookup_thread(pthread_self());
137     /* The lookup here is safe, since I'm doing this on behalf  */
138     /* of a thread which holds the allocation lock in order     */
139     /* to stop the world.  Thus concurrent modification of the  */
140     /* data structure is impossible.                            */
141     if (PLEASE_STOP != me -> stop) {
142         /* Misdirected signal.  */
143         pthread_mutex_unlock(&GC_suspend_lock);
144         return;
145     }
146     pthread_mutex_lock(&GC_suspend_lock);
147     me -> stack_hot = (ptr_t)(&dummy);
148     me -> stop = STOPPED;
149     pthread_cond_signal(&GC_suspend_ack_cv);
150     pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock);
151     pthread_mutex_unlock(&GC_suspend_lock);
152     /* GC_printf1("Continuing 0x%x\n", pthread_self()); */
153 }
154
155
156 GC_bool GC_thr_initialized = FALSE;
157
158
159 # define THREAD_TABLE_SZ 128    /* Must be power of 2   */
160 volatile GC_thread GC_threads[THREAD_TABLE_SZ];
161
162 void GC_push_thread_structures GC_PROTO((void))
163 {
164     GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
165 }
166
167 /* Add a thread to GC_threads.  We assume it wasn't already there.      */
168 /* Caller holds allocation lock.                                        */
169 GC_thread GC_new_thread(pthread_t id)
170 {
171     int hv = ((word)id) % THREAD_TABLE_SZ;
172     GC_thread result;
173     static struct GC_Thread_Rep first_thread;
174     static GC_bool first_thread_used = FALSE;
175     
176     GC_ASSERT(I_HOLD_LOCK());
177     if (!first_thread_used) {
178         result = &first_thread;
179         first_thread_used = TRUE;
180         /* Dont acquire allocation lock, since we may already hold it. */
181     } else {
182         result = (struct GC_Thread_Rep *)
183                  GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
184     }
185     if (result == 0) return(0);
186     result -> id = id;
187     result -> next = GC_threads[hv];
188     GC_threads[hv] = result;
189     /* result -> flags = 0;     */
190     /* result -> stop = 0;      */
191     return(result);
192 }
193
194 /* Delete a thread from GC_threads.  We assume it is there.     */
195 /* (The code intentionally traps if it wasn't.)                 */
196 /* Caller holds allocation lock.                                */
197 /* We explicitly pass in the GC_thread we're looking for, since */
198 /* if a thread has been joined, but we have not yet             */
199 /* been notified, then there may be more than one thread        */
200 /* in the table with the same pthread id.                       */
201 /* This is OK, but we need a way to delete a specific one.      */
202 void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
203 {
204     int hv = ((word)id) % THREAD_TABLE_SZ;
205     register GC_thread p = GC_threads[hv];
206     register GC_thread prev = 0;
207
208     GC_ASSERT(I_HOLD_LOCK());
209     while (p != gc_id) {
210         prev = p;
211         p = p -> next;
212     }
213     if (prev == 0) {
214         GC_threads[hv] = p -> next;
215     } else {
216         prev -> next = p -> next;
217     }
218 }
219
220 /* Return a GC_thread corresponding to a given thread_t.        */
221 /* Returns 0 if it's not there.                                 */
222 /* Caller holds  allocation lock or otherwise inhibits          */
223 /* updates.                                                     */
224 /* If there is more than one thread with the given id we        */
225 /* return the most recent one.                                  */
226 GC_thread GC_lookup_thread(pthread_t id)
227 {
228     int hv = ((word)id) % THREAD_TABLE_SZ;
229     register GC_thread p = GC_threads[hv];
230     
231     /* I either hold the lock, or i'm being called from the stop-the-world
232      * handler. */
233 #if defined(GC_AIX_THREADS)
234     GC_ASSERT(I_HOLD_LOCK()); /* no stop-the-world handler needed on AIX */
235 #endif
236     while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
237     return(p);
238 }
239
240 void lock_stopworld(int);
241 void unlock_stopworld();
242
243 #if defined(GC_AIX_THREADS)
244 void GC_stop_world()
245 {
246     pthread_t my_thread = pthread_self();
247     register int i;
248     register GC_thread p;
249     register int result;
250     struct timespec timeout;
251
252     GC_ASSERT(I_HOLD_LOCK());
253     for (i = 0; i < THREAD_TABLE_SZ; i++) {
254       for (p = GC_threads[i]; p != 0; p = p -> next) {
255         if (p -> id != my_thread) {
256           pthread_suspend_np(p->id);
257         }
258       }
259     }
260     /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
261 }
262
263 void GC_start_world()
264 {
265     GC_thread p;
266     unsigned i;
267     pthread_t my_thread = pthread_self();
268
269     /* GC_printf0("World starting\n"); */
270     GC_ASSERT(I_HOLD_LOCK());
271     for (i = 0; i < THREAD_TABLE_SZ; i++) {
272       for (p = GC_threads[i]; p != 0; p = p -> next) {
273         if (p -> id != my_thread) {
274           pthread_continue_np(p->id);
275         }
276       }
277     }
278 }
279
280 #else /* GC_AIX_THREADS */
281
282 /* Caller holds allocation lock.        */
283 void GC_stop_world()
284 {
285     pthread_t my_thread = pthread_self();
286     register int i;
287     register GC_thread p;
288     register int result;
289     struct timespec timeout;
290
291         lock_stopworld(1);
292     
293     GC_ASSERT(I_HOLD_LOCK());
294     for (i = 0; i < THREAD_TABLE_SZ; i++) {
295       for (p = GC_threads[i]; p != 0; p = p -> next) {
296         if (p -> id != my_thread) {
297             if (p -> flags & FINISHED) {
298                 p -> stop = STOPPED;
299                 continue;
300             }
301             p -> stop = PLEASE_STOP;
302             result = pthread_kill(p -> id, SIG_SUSPEND);
303             /* GC_printf1("Sent signal to 0x%x\n", p -> id); */
304             switch(result) {
305                 case ESRCH:
306                     /* Not really there anymore.  Possible? */
307                     p -> stop = STOPPED;
308                     break;
309                 case 0:
310                     break;
311                 default:
312                     ABORT("pthread_kill failed");
313             }
314         }
315       }
316     }
317     pthread_mutex_lock(&GC_suspend_lock);
318     for (i = 0; i < THREAD_TABLE_SZ; i++) {
319       for (p = GC_threads[i]; p != 0; p = p -> next) {
320         while (p -> id != my_thread && p -> stop != STOPPED) {
321             clock_gettime(CLOCK_REALTIME, &timeout);
322             timeout.tv_nsec += 50000000; /* 50 msecs */
323             if (timeout.tv_nsec >= 1000000000) {
324                 timeout.tv_nsec -= 1000000000;
325                 ++timeout.tv_sec;
326             }
327             result = pthread_cond_timedwait(&GC_suspend_ack_cv,
328                                             &GC_suspend_lock,
329                                             &timeout);
330             if (result == ETIMEDOUT) {
331                 /* Signal was lost or misdirected.  Try again.      */
332                 /* Duplicate signals should be benign.              */
333                 result = pthread_kill(p -> id, SIG_SUSPEND);
334             }
335         }
336       }
337     }
338     pthread_mutex_unlock(&GC_suspend_lock);
339     /* GC_printf1("World stopped 0x%x\n", pthread_self()); */
340 }
341
342 /* Caller holds allocation lock.        */
343 void GC_start_world()
344 {
345     GC_thread p;
346     unsigned i;
347
348     /* GC_printf0("World starting\n"); */
349     GC_ASSERT(I_HOLD_LOCK());
350     for (i = 0; i < THREAD_TABLE_SZ; i++) {
351       for (p = GC_threads[i]; p != 0; p = p -> next) {
352         p -> stop = NOT_STOPPED;
353       }
354     }
355     pthread_mutex_lock(&GC_suspend_lock);
356     /* All other threads are at pthread_cond_wait in signal handler.    */
357     /* Otherwise we couldn't have acquired the lock.                    */
358     pthread_mutex_unlock(&GC_suspend_lock);
359     pthread_cond_broadcast(&GC_continue_cv);
360
361         unlock_stopworld();
362 }
363
364 #endif /* GC_AIX_THREADS */
365
366
367 /* We hold allocation lock.  Should do exactly the right thing if the   */
368 /* world is stopped.  Should not fail if it isn't.                      */
369 void GC_push_all_stacks()
370 {
371     register int i;
372     register GC_thread p;
373     register ptr_t hot, cold;
374     pthread_t me = pthread_self();
375     
376     /* GC_init() should have been called before GC_push_all_stacks is
377      * invoked, and GC_init calls GC_thr_init(), which sets
378      * GC_thr_initialized. */
379     GC_ASSERT(GC_thr_initialized);
380
381     /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */
382     GC_ASSERT(I_HOLD_LOCK());
383     for (i = 0; i < THREAD_TABLE_SZ; i++) {
384       for (p = GC_threads[i]; p != 0; p = p -> next) {
385         if (p -> flags & FINISHED) continue;
386         cold = p->stack_cold;
387         if (!cold) cold=GC_stackbottom; /* 0 indicates 'original stack' */
388         if (pthread_equal(p -> id, me)) {
389             hot = GC_approx_sp();
390         } else {
391 #        ifdef GC_AIX_THREADS
392           /* AIX doesn't use signals to suspend, so we need to get an */
393           /* accurate hot stack pointer.                              */
394           /* See http://publib16.boulder.ibm.com/pseries/en_US/libs/basetrf1/pthread_getthrds_np.htm */
395           pthread_t id = p -> id;
396           struct __pthrdsinfo pinfo;
397           int regbuf[64];
398           int val = sizeof(regbuf);
399           int retval = pthread_getthrds_np(&id, PTHRDSINFO_QUERY_ALL, &pinfo,
400                                            sizeof(pinfo), regbuf, &val);
401           if (retval != 0) {
402             printf("ERROR: pthread_getthrds_np() failed in GC\n");
403             abort();
404           }
405           /* according to the AIX ABI, 
406              "the lowest possible valid stack address is 288 bytes (144 + 144)
407              less than the current value of the stack pointer.  Functions may
408              use this stack space as volatile storage which is not preserved
409              across function calls."
410              ftp://ftp.penguinppc64.org/pub/people/amodra/PPC-elf64abi.txt.gz
411           */
412           hot = (ptr_t)(unsigned long)pinfo.__pi_ustk-288;
413           cold = (ptr_t)pinfo.__pi_stackend; /* more precise */
414           /* push the registers too, because they won't be on stack */
415           GC_push_all_eager((ptr_t)&pinfo.__pi_context,
416                             (ptr_t)((&pinfo.__pi_context)+1));
417           GC_push_all_eager((ptr_t)regbuf, ((ptr_t)regbuf)+val);
418 #        else
419               hot = p -> stack_hot;
420 #        endif
421         }
422 #       ifdef STACK_GROWS_UP
423           GC_push_all_stack(cold, hot);
424 #       else
425  /* printf("thread 0x%x: hot=0x%08x cold=0x%08x\n", p -> id, hot, cold); */
426           GC_push_all_stack(hot, cold);
427 #       endif
428       }
429     }
430 }
431
432
433 /* We hold the allocation lock. */
434 void GC_thr_init()
435 {
436     GC_thread t;
437     struct sigaction act;
438
439     if (GC_thr_initialized) return;
440     GC_ASSERT(I_HOLD_LOCK());
441     GC_thr_initialized = TRUE;
442 #ifndef GC_AIX_THREADS
443     (void) sigaction(SIG_SUSPEND, 0, &act);
444     if (act.sa_handler != SIG_DFL)
445         ABORT("Previously installed SIG_SUSPEND handler");
446     /* Install handler. */
447         act.sa_handler = GC_suspend_handler;
448         act.sa_flags = SA_RESTART | SA_SIGINFO;
449         (void) sigemptyset(&act.sa_mask);
450         if (0 != sigaction(SIG_SUSPEND, &act, 0))
451             ABORT("Failed to install SIG_SUSPEND handler");
452 #endif
453     /* Add the initial thread, so we can stop it.       */
454       t = GC_new_thread(pthread_self());
455       /* use '0' to indicate GC_stackbottom, since GC_init() has not
456        * completed by the time we are called (from GC_init_inner()) */
457       t -> stack_cold = 0; /* the original stack. */
458       t -> stack_hot = (ptr_t)(&t);
459       t -> flags = DETACHED;
460 }
461
462 int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
463 {
464     sigset_t fudged_set;
465     
466 #ifdef GC_AIX_THREADS
467     return(pthread_sigmask(how, set, oset));
468 #endif
469
470     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
471         fudged_set = *set;
472         sigdelset(&fudged_set, SIG_SUSPEND);
473         set = &fudged_set;
474     }
475     return(pthread_sigmask(how, set, oset));
476 }
477
478 struct start_info {
479     void *(*start_routine)(void *);
480     void *arg;
481     word flags;
482     pthread_mutex_t registeredlock;
483     pthread_cond_t registered;     
484     int volatile registereddone;
485 };
486
487 void GC_thread_exit_proc(void *arg)
488 {
489     GC_thread me;
490
491     LOCK();
492     me = GC_lookup_thread(pthread_self());
493     me -> flags |= FINISHED;
494     /* reclaim DETACHED thread right away; otherwise wait until join() */
495     if (me -> flags & DETACHED) {
496         GC_delete_gc_thread(pthread_self(), me);
497     }
498     UNLOCK();
499 }
500
501 int GC_pthread_join(pthread_t thread, void **retval)
502 {
503     int result;
504     GC_thread thread_gc_id;
505     
506     LOCK();
507     thread_gc_id = GC_lookup_thread(thread);
508     /* This is guaranteed to be the intended one, since the thread id   */
509     /* cant have been recycled by pthreads.                             */
510     UNLOCK();
511     GC_ASSERT(!(thread_gc_id->flags & DETACHED));
512     result = pthread_join(thread, retval);
513     /* Some versions of the Irix pthreads library can erroneously       */
514     /* return EINTR when the call succeeds.                             */
515         if (EINTR == result) result = 0;
516     GC_ASSERT(thread_gc_id->flags & FINISHED);
517     LOCK();
518     /* Here the pthread thread id may have been recycled. */
519     GC_delete_gc_thread(thread, thread_gc_id);
520     UNLOCK();
521     return result;
522 }
523
524 void * GC_start_routine(void * arg)
525 {
526     int dummy;
527     struct start_info * si = arg;
528     void * result;
529     GC_thread me;
530     pthread_t my_pthread;
531     void *(*start)(void *);
532     void *start_arg;
533
534     my_pthread = pthread_self();
535     /* If a GC occurs before the thread is registered, that GC will     */
536     /* ignore this thread.  That's fine, since it will block trying to  */
537     /* acquire the allocation lock, and won't yet hold interesting      */
538     /* pointers.                                                        */
539     LOCK();
540     /* We register the thread here instead of in the parent, so that    */
541     /* we don't need to hold the allocation lock during pthread_create. */
542     /* Holding the allocation lock there would make REDIRECT_MALLOC     */
543     /* impossible.  It probably still doesn't work, but we're a little  */
544     /* closer ...                                                       */
545     /* This unfortunately means that we have to be careful the parent   */
546     /* doesn't try to do a pthread_join before we're registered.        */
547     me = GC_new_thread(my_pthread);
548     me -> flags = si -> flags;
549     me -> stack_cold = (ptr_t) &dummy; /* this now the 'start of stack' */
550     me -> stack_hot = me->stack_cold;/* this field should always be sensible */
551     UNLOCK();
552     start = si -> start_routine;
553     start_arg = si -> arg;
554
555     pthread_mutex_lock(&(si->registeredlock));
556     si->registereddone = 1;
557     pthread_cond_signal(&(si->registered));
558     pthread_mutex_unlock(&(si->registeredlock));
559     /* si went away as soon as we did this unlock */
560
561     pthread_cleanup_push(GC_thread_exit_proc, 0);
562     result = (*start)(start_arg);
563     me -> status = result;
564     pthread_cleanup_pop(1);
565         /* This involves acquiring the lock, ensuring that we can't exit */
566         /* while a collection that thinks we're alive is trying to stop  */
567         /* us.                                                           */
568     return(result);
569 }
570
571 int
572 GC_pthread_create(pthread_t *new_thread,
573                   const pthread_attr_t *attr,
574                   void *(*start_routine)(void *), void *arg)
575 {
576     int result;
577     GC_thread t;
578     int detachstate;
579     word my_flags = 0;
580     struct start_info * si;
581         /* This is otherwise saved only in an area mmapped by the thread */
582         /* library, which isn't visible to the collector.                */
583
584     LOCK();
585     /* GC_INTERNAL_MALLOC implicitly calls GC_init() if required */
586     si = (struct start_info *)GC_INTERNAL_MALLOC(sizeof(struct start_info),
587                                                  NORMAL);
588     GC_ASSERT(GC_thr_initialized); /* initialized by GC_init() */
589     UNLOCK();
590     if (0 == si) return(ENOMEM);
591     pthread_mutex_init(&(si->registeredlock), NULL);
592     pthread_cond_init(&(si->registered),NULL);
593     pthread_mutex_lock(&(si->registeredlock));
594     si -> start_routine = start_routine;
595     si -> arg = arg;
596
597     pthread_attr_getdetachstate(attr, &detachstate);
598     if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
599     si -> flags = my_flags;
600     result = pthread_create(new_thread, attr, GC_start_routine, si); 
601
602     /* Wait until child has been added to the thread table.             */
603     /* This also ensures that we hold onto si until the child is done   */
604     /* with it.  Thus it doesn't matter whether it is otherwise         */
605     /* visible to the collector.                                        */
606
607     if (0 == result) {
608       si->registereddone = 0;
609       while (!si->registereddone) 
610         pthread_cond_wait(&(si->registered), &(si->registeredlock));
611     }
612     pthread_mutex_unlock(&(si->registeredlock));
613
614     pthread_cond_destroy(&(si->registered));
615     pthread_mutex_destroy(&(si->registeredlock));
616     LOCK();
617     GC_INTERNAL_FREE(si);
618     UNLOCK();
619
620     return(result);
621 }
622
623 /* For now we use the pthreads locking primitives on HP/UX */
624
625 VOLATILE GC_bool GC_collecting = 0; /* A hint that we're in the collector and       */
626                         /* holding the allocation lock for an           */
627                         /* extended period.                             */
628
629 /* Reasonably fast spin locks.  Basically the same implementation */
630 /* as STL alloc.h.                                                */
631
632 #define SLEEP_THRESHOLD 3
633
634 volatile unsigned int GC_allocate_lock = 0;
635 #define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
636 #define GC_LOCK_TAKEN GC_allocate_lock
637
638 void GC_lock()
639 {
640 #   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
641 #   define high_spin_max 1000 /* spin cycles for multiprocessor */
642     static unsigned spin_max = low_spin_max;
643     unsigned my_spin_max;
644     static unsigned last_spins = 0;
645     unsigned my_last_spins;
646     volatile unsigned junk;
647 #   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
648     int i;
649
650     if (GC_TRY_LOCK()) {
651         return;
652     }
653     junk = 0;
654     my_spin_max = spin_max;
655     my_last_spins = last_spins;
656     for (i = 0; i < my_spin_max; i++) {
657         if (GC_collecting) goto yield;
658         if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
659             PAUSE; 
660             continue;
661         }
662         if (GC_TRY_LOCK()) {
663             /*
664              * got it!
665              * Spinning worked.  Thus we're probably not being scheduled
666              * against the other process with which we were contending.
667              * Thus it makes sense to spin longer the next time.
668              */
669             last_spins = i;
670             spin_max = high_spin_max;
671             return;
672         }
673     }
674     /* We are probably being scheduled against the other process.  Sleep. */
675     spin_max = low_spin_max;
676 yield:
677     for (i = 0;; ++i) {
678         if (GC_TRY_LOCK()) {
679             return;
680         }
681         if (i < SLEEP_THRESHOLD) {
682             sched_yield();
683         } else {
684             struct timespec ts;
685         
686             if (i > 26) i = 26;
687                         /* Don't wait for more than about 60msecs, even */
688                         /* under extreme contention.                    */
689             ts.tv_sec = 0;
690             ts.tv_nsec = 1 << i;
691             nanosleep(&ts, 0);
692         }
693     }
694 }
695
696 /* Added for cacao */
697 int GC_signum1()
698 {
699     return SIG_SUSPEND;
700 }
701 /* cacao END */
702
703 # else  /* !GC_IRIX_THREADS && !GC_AIX_THREADS */
704
705 #ifndef LINT
706   int GC_no_Irix_threads;
707 #endif
708
709 # endif /* IRIX_THREADS */
710