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