OSDN Git Service

* Makefile.in (local-distclean): Remove leftover built files.
[pf3gnuchains/gcc-fork.git] / boehm-gc / hpux_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 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
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()
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