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