Annotation of OpenXM_contrib/gc/linux_threads.c, Revision 1.1.1.1
1.1 maekawa 1: /*
2: * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
3: * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
4: * Copyright (c) 1998 by Fergus Henderson. 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 LinuxThreads, the clone()-based kernel
17: * thread package for Linux which is included in libc6.
18: *
19: * This code relies on implementation details of LinuxThreads,
20: * (i.e. properties not guaranteed by the Pthread standard):
21: *
22: * - the function GC_linux_thread_top_of_stack(void)
23: * relies on the way LinuxThreads lays out thread stacks
24: * in the address space.
25: *
26: * Note that there is a lot of code duplication between linux_threads.c
27: * and irix_threads.c; any changes made here may need to be reflected
28: * there too.
29: */
30:
31: /* #define DEBUG_THREADS 1 */
32:
33: /* ANSI C requires that a compilation unit contains something */
34: # include "gc_priv.h"
35:
36: # if defined(LINUX_THREADS)
37:
38: # include <pthread.h>
39: # include <time.h>
40: # include <errno.h>
41: # include <unistd.h>
42: # include <sys/mman.h>
43: # include <sys/time.h>
44: # include <semaphore.h>
45:
46: #undef pthread_create
47: #undef pthread_sigmask
48: #undef pthread_join
49:
50: void GC_thr_init();
51:
52: #if 0
53: void GC_print_sig_mask()
54: {
55: sigset_t blocked;
56: int i;
57:
58: if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
59: ABORT("pthread_sigmask");
60: GC_printf0("Blocked: ");
61: for (i = 1; i <= MAXSIG; i++) {
62: if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
63: }
64: GC_printf0("\n");
65: }
66: #endif
67:
68: /* We use the allocation lock to protect thread-related data structures. */
69:
70: /* The set of all known threads. We intercept thread creation and */
71: /* joins. We never actually create detached threads. We allocate all */
72: /* new thread stacks ourselves. These allow us to maintain this */
73: /* data structure. */
74: /* Protected by GC_thr_lock. */
75: /* Some of this should be declared volatile, but that's incosnsistent */
76: /* with some library routine declarations. */
77: typedef struct GC_Thread_Rep {
78: struct GC_Thread_Rep * next; /* More recently allocated threads */
79: /* with a given pthread id come */
80: /* first. (All but the first are */
81: /* guaranteed to be dead, but we may */
82: /* not yet have registered the join.) */
83: pthread_t id;
84: word flags;
85: # define FINISHED 1 /* Thread has exited. */
86: # define DETACHED 2 /* Thread is intended to be detached. */
87: # define MAIN_THREAD 4 /* True for the original thread only. */
88:
89: ptr_t stack_end;
90: ptr_t stack_ptr; /* Valid only when stopped. */
91: int signal;
92: void * status; /* The value returned from the thread. */
93: /* Used only to avoid premature */
94: /* reclamation of any data it might */
95: /* reference. */
96: } * GC_thread;
97:
98: GC_thread GC_lookup_thread(pthread_t id);
99:
100: /*
101: * The only way to suspend threads given the pthread interface is to send
102: * signals. We can't use SIGSTOP directly, because we need to get the
103: * thread to save its stack pointer in the GC thread table before
104: * suspending. So we have to reserve a signal of our own for this.
105: * This means we have to intercept client calls to change the signal mask.
106: * The linuxthreads package already uses SIGUSR1 and SIGUSR2,
107: * so we need to reuse something else. I chose SIGPWR.
108: * (Perhaps SIGUNUSED would be a better choice.)
109: */
110: #define SIG_SUSPEND SIGPWR
111:
112: #define SIG_RESTART SIGXCPU
113:
114: sem_t GC_suspend_ack_sem;
115:
116: /*
117: GC_linux_thread_top_of_stack() relies on implementation details of
118: LinuxThreads, namely that thread stacks are allocated on 2M boundaries
119: and grow to no more than 2M.
120: To make sure that we're using LinuxThreads and not some other thread
121: package, we generate a dummy reference to `__pthread_initial_thread_bos',
122: which is a symbol defined in LinuxThreads, but (hopefully) not in other
123: thread packages.
124: */
125: extern char * __pthread_initial_thread_bos;
126: char **dummy_var_to_force_linux_threads = &__pthread_initial_thread_bos;
127:
128: #define LINUX_THREADS_STACK_SIZE (2 * 1024 * 1024)
129:
130: static inline ptr_t GC_linux_thread_top_of_stack(void)
131: {
132: char *sp = GC_approx_sp();
133: ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1);
134: #if DEBUG_THREADS
135: GC_printf1("SP = %lx\n", (unsigned long)sp);
136: GC_printf1("TOS = %lx\n", (unsigned long)tos);
137: #endif
138: return tos;
139: }
140:
141: void GC_suspend_handler(int sig)
142: {
143: int dummy;
144: pthread_t my_thread = pthread_self();
145: GC_thread me;
146: sigset_t all_sigs;
147: sigset_t old_sigs;
148: int i;
149: sigset_t mask;
150:
151: if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
152:
153: #if DEBUG_THREADS
154: GC_printf1("Suspending 0x%x\n", my_thread);
155: #endif
156:
157: me = GC_lookup_thread(my_thread);
158: /* The lookup here is safe, since I'm doing this on behalf */
159: /* of a thread which holds the allocation lock in order */
160: /* to stop the world. Thus concurrent modification of the */
161: /* data structure is impossible. */
162: me -> stack_ptr = (ptr_t)(&dummy);
163: me -> stack_end = GC_linux_thread_top_of_stack();
164:
165: /* Tell the thread that wants to stop the world that this */
166: /* thread has been stopped. Note that sem_post() is */
167: /* the only async-signal-safe primitive in LinuxThreads. */
168: sem_post(&GC_suspend_ack_sem);
169:
170: /* Wait until that thread tells us to restart by sending */
171: /* this thread a SIG_RESTART signal. */
172: /* SIG_RESTART should be masked at this point. Thus there */
173: /* is no race. */
174: if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
175: if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed");
176: do {
177: me->signal = 0;
178: sigsuspend(&mask); /* Wait for signal */
179: } while (me->signal != SIG_RESTART);
180:
181: #if DEBUG_THREADS
182: GC_printf1("Continuing 0x%x\n", my_thread);
183: #endif
184: }
185:
186: void GC_restart_handler(int sig)
187: {
188: GC_thread me;
189:
190: if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler");
191:
192: /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */
193: /* The lookup here is safe, since I'm doing this on behalf */
194: /* of a thread which holds the allocation lock in order */
195: /* to stop the world. Thus concurrent modification of the */
196: /* data structure is impossible. */
197: me = GC_lookup_thread(pthread_self());
198: me->signal = SIG_RESTART;
199:
200: /*
201: ** Note: even if we didn't do anything useful here,
202: ** it would still be necessary to have a signal handler,
203: ** rather than ignoring the signals, otherwise
204: ** the signals will not be delivered at all, and
205: ** will thus not interrupt the sigsuspend() above.
206: */
207:
208: #if DEBUG_THREADS
209: GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self());
210: #endif
211: }
212:
213: GC_bool GC_thr_initialized = FALSE;
214:
215: # define THREAD_TABLE_SZ 128 /* Must be power of 2 */
216: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
217:
218: /* Add a thread to GC_threads. We assume it wasn't already there. */
219: /* Caller holds allocation lock. */
220: GC_thread GC_new_thread(pthread_t id)
221: {
222: int hv = ((word)id) % THREAD_TABLE_SZ;
223: GC_thread result;
224: static struct GC_Thread_Rep first_thread;
225: static GC_bool first_thread_used = FALSE;
226:
227: if (!first_thread_used) {
228: result = &first_thread;
229: first_thread_used = TRUE;
230: /* Dont acquire allocation lock, since we may already hold it. */
231: } else {
232: result = (struct GC_Thread_Rep *)
233: GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
234: }
235: if (result == 0) return(0);
236: result -> id = id;
237: result -> next = GC_threads[hv];
238: GC_threads[hv] = result;
239: /* result -> flags = 0; */
240: return(result);
241: }
242:
243: /* Delete a thread from GC_threads. We assume it is there. */
244: /* (The code intentionally traps if it wasn't.) */
245: /* Caller holds allocation lock. */
246: void GC_delete_thread(pthread_t id)
247: {
248: int hv = ((word)id) % THREAD_TABLE_SZ;
249: register GC_thread p = GC_threads[hv];
250: register GC_thread prev = 0;
251:
252: while (!pthread_equal(p -> id, id)) {
253: prev = p;
254: p = p -> next;
255: }
256: if (prev == 0) {
257: GC_threads[hv] = p -> next;
258: } else {
259: prev -> next = p -> next;
260: }
261: }
262:
263: /* If a thread has been joined, but we have not yet */
264: /* been notified, then there may be more than one thread */
265: /* in the table with the same pthread id. */
266: /* This is OK, but we need a way to delete a specific one. */
267: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
268: {
269: int hv = ((word)id) % THREAD_TABLE_SZ;
270: register GC_thread p = GC_threads[hv];
271: register GC_thread prev = 0;
272:
273: while (p != gc_id) {
274: prev = p;
275: p = p -> next;
276: }
277: if (prev == 0) {
278: GC_threads[hv] = p -> next;
279: } else {
280: prev -> next = p -> next;
281: }
282: }
283:
284: /* Return a GC_thread corresponding to a given thread_t. */
285: /* Returns 0 if it's not there. */
286: /* Caller holds allocation lock or otherwise inhibits */
287: /* updates. */
288: /* If there is more than one thread with the given id we */
289: /* return the most recent one. */
290: GC_thread GC_lookup_thread(pthread_t id)
291: {
292: int hv = ((word)id) % THREAD_TABLE_SZ;
293: register GC_thread p = GC_threads[hv];
294:
295: while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
296: return(p);
297: }
298:
299: /* Caller holds allocation lock. */
300: void GC_stop_world()
301: {
302: pthread_t my_thread = pthread_self();
303: register int i;
304: register GC_thread p;
305: register int n_live_threads = 0;
306: register int result;
307:
308: for (i = 0; i < THREAD_TABLE_SZ; i++) {
309: for (p = GC_threads[i]; p != 0; p = p -> next) {
310: if (p -> id != my_thread) {
311: if (p -> flags & FINISHED) continue;
312: n_live_threads++;
313: #if DEBUG_THREADS
314: GC_printf1("Sending suspend signal to 0x%x\n", p -> id);
315: #endif
316: result = pthread_kill(p -> id, SIG_SUSPEND);
317: switch(result) {
318: case ESRCH:
319: /* Not really there anymore. Possible? */
320: n_live_threads--;
321: break;
322: case 0:
323: break;
324: default:
325: ABORT("pthread_kill failed");
326: }
327: }
328: }
329: }
330: for (i = 0; i < n_live_threads; i++) {
331: sem_wait(&GC_suspend_ack_sem);
332: }
333: #if DEBUG_THREADS
334: GC_printf1("World stopped 0x%x\n", pthread_self());
335: #endif
336: }
337:
338: /* Caller holds allocation lock. */
339: void GC_start_world()
340: {
341: pthread_t my_thread = pthread_self();
342: register int i;
343: register GC_thread p;
344: register int n_live_threads = 0;
345: register int result;
346:
347: # if DEBUG_THREADS
348: GC_printf0("World starting\n");
349: # endif
350:
351: for (i = 0; i < THREAD_TABLE_SZ; i++) {
352: for (p = GC_threads[i]; p != 0; p = p -> next) {
353: if (p -> id != my_thread) {
354: if (p -> flags & FINISHED) continue;
355: n_live_threads++;
356: #if DEBUG_THREADS
357: GC_printf1("Sending restart signal to 0x%x\n", p -> id);
358: #endif
359: result = pthread_kill(p -> id, SIG_RESTART);
360: switch(result) {
361: case ESRCH:
362: /* Not really there anymore. Possible? */
363: n_live_threads--;
364: break;
365: case 0:
366: break;
367: default:
368: ABORT("pthread_kill failed");
369: }
370: }
371: }
372: }
373: #if DEBUG_THREADS
374: GC_printf0("World started\n");
375: #endif
376: }
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: #if DEBUG_THREADS
389: GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
390: #endif
391: for (i = 0; i < THREAD_TABLE_SZ; i++) {
392: for (p = GC_threads[i]; p != 0; p = p -> next) {
393: if (p -> flags & FINISHED) continue;
394: if (pthread_equal(p -> id, me)) {
395: lo = GC_approx_sp();
396: } else {
397: lo = p -> stack_ptr;
398: }
399: if ((p -> flags & MAIN_THREAD) == 0) {
400: if (pthread_equal(p -> id, me)) {
401: hi = GC_linux_thread_top_of_stack();
402: } else {
403: hi = p -> stack_end;
404: }
405: } else {
406: /* The original stack. */
407: hi = GC_stackbottom;
408: }
409: #if DEBUG_THREADS
410: GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
411: (unsigned long) p -> id,
412: (unsigned long) lo, (unsigned long) hi);
413: #endif
414: GC_push_all_stack(lo, hi);
415: }
416: }
417: }
418:
419:
420: /* We hold the allocation lock. */
421: void GC_thr_init()
422: {
423: GC_thread t;
424: struct sigaction act;
425:
426: if (GC_thr_initialized) return;
427: GC_thr_initialized = TRUE;
428:
429: if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
430: ABORT("sem_init failed");
431:
432: act.sa_flags = SA_RESTART;
433: if (sigfillset(&act.sa_mask) != 0) {
434: ABORT("sigfillset() failed");
435: }
436: /* SIG_RESTART is unmasked by the handler when necessary. */
437: act.sa_handler = GC_suspend_handler;
438: if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
439: ABORT("Cannot set SIG_SUSPEND handler");
440: }
441:
442: act.sa_handler = GC_restart_handler;
443: if (sigaction(SIG_RESTART, &act, NULL) != 0) {
444: ABORT("Cannot set SIG_SUSPEND handler");
445: }
446:
447: /* Add the initial thread, so we can stop it. */
448: t = GC_new_thread(pthread_self());
449: t -> stack_ptr = 0;
450: t -> flags = DETACHED | MAIN_THREAD;
451: }
452:
453: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
454: {
455: sigset_t fudged_set;
456:
457: if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
458: fudged_set = *set;
459: sigdelset(&fudged_set, SIG_SUSPEND);
460: set = &fudged_set;
461: }
462: return(pthread_sigmask(how, set, oset));
463: }
464:
465: struct start_info {
466: void *(*start_routine)(void *);
467: void *arg;
468: word flags;
469: sem_t registered; /* 1 ==> in our thread table, but */
470: /* parent hasn't yet noticed. */
471: };
472:
473:
474: void GC_thread_exit_proc(void *arg)
475: {
476: GC_thread me;
477: struct start_info * si = arg;
478:
479: LOCK();
480: me = GC_lookup_thread(pthread_self());
481: if (me -> flags & DETACHED) {
482: GC_delete_thread(pthread_self());
483: } else {
484: me -> flags |= FINISHED;
485: }
486: UNLOCK();
487: }
488:
489: int GC_pthread_join(pthread_t thread, void **retval)
490: {
491: int result;
492: GC_thread thread_gc_id;
493:
494: LOCK();
495: thread_gc_id = GC_lookup_thread(thread);
496: /* This is guaranteed to be the intended one, since the thread id */
497: /* cant have been recycled by pthreads. */
498: UNLOCK();
499: result = pthread_join(thread, retval);
500: LOCK();
501: /* Here the pthread thread id may have been recycled. */
502: GC_delete_gc_thread(thread, thread_gc_id);
503: UNLOCK();
504: return result;
505: }
506:
507: void * GC_start_routine(void * arg)
508: {
509: struct start_info * si = arg;
510: void * result;
511: GC_thread me;
512: pthread_t my_pthread;
513: void *(*start)(void *);
514: void *start_arg;
515:
516: my_pthread = pthread_self();
517: LOCK();
518: me = GC_new_thread(my_pthread);
519: me -> flags = si -> flags;
520: me -> stack_ptr = 0;
521: me -> stack_end = 0;
522: UNLOCK();
523: start = si -> start_routine;
524: start_arg = si -> arg;
525: sem_post(&(si -> registered));
526: pthread_cleanup_push(GC_thread_exit_proc, si);
527: # ifdef DEBUG_THREADS
528: GC_printf1("Starting thread 0x%lx\n", pthread_self());
529: GC_printf1("pid = %ld\n", (long) getpid());
530: GC_printf1("sp = 0x%lx\n", (long) &arg);
531: GC_printf1("start_routine = 0x%lx\n", start);
532: # endif
533: result = (*start)(start_arg);
534: #if DEBUG_THREADS
535: GC_printf1("Finishing thread 0x%x\n", pthread_self());
536: #endif
537: me -> status = result;
538: me -> flags |= FINISHED;
539: pthread_cleanup_pop(1);
540: /* Cleanup acquires lock, ensuring that we can't exit */
541: /* while a collection that thinks we're alive is trying to stop */
542: /* us. */
543: return(result);
544: }
545:
546: int
547: GC_pthread_create(pthread_t *new_thread,
548: const pthread_attr_t *attr,
549: void *(*start_routine)(void *), void *arg)
550: {
551: int result;
552: GC_thread t;
553: pthread_t my_new_thread;
554: void * stack;
555: size_t stacksize;
556: pthread_attr_t new_attr;
557: int detachstate;
558: word my_flags = 0;
559: struct start_info * si = GC_malloc(sizeof(struct start_info));
560: /* This is otherwise saved only in an area mmapped by the thread */
561: /* library, which isn't visible to the collector. */
562:
563: if (0 == si) return(ENOMEM);
564: sem_init(&(si -> registered), 0, 0);
565: si -> start_routine = start_routine;
566: si -> arg = arg;
567: LOCK();
568: if (!GC_thr_initialized) GC_thr_init();
569: if (NULL == attr) {
570: stack = 0;
571: (void) pthread_attr_init(&new_attr);
572: } else {
573: new_attr = *attr;
574: }
575: pthread_attr_getdetachstate(&new_attr, &detachstate);
576: if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
577: si -> flags = my_flags;
578: UNLOCK();
579: result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
580: /* Wait until child has been added to the thread table. */
581: /* This also ensures that we hold onto si until the child is done */
582: /* with it. Thus it doesn't matter whether it is otherwise */
583: /* visible to the collector. */
584: if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed");
585: sem_destroy(&(si -> registered));
586: /* pthread_attr_destroy(&new_attr); */
587: /* pthread_attr_destroy(&new_attr); */
588: return(result);
589: }
590:
591: GC_bool GC_collecting = 0;
592: /* A hint that we're in the collector and */
593: /* holding the allocation lock for an */
594: /* extended period. */
595:
596: /* Reasonably fast spin locks. Basically the same implementation */
597: /* as STL alloc.h. This isn't really the right way to do this. */
598: /* but until the POSIX scheduling mess gets straightened out ... */
599:
600: volatile unsigned int GC_allocate_lock = 0;
601:
602:
603: void GC_lock()
604: {
605: # define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
606: # define high_spin_max 1000 /* spin cycles for multiprocessor */
607: static unsigned spin_max = low_spin_max;
608: unsigned my_spin_max;
609: static unsigned last_spins = 0;
610: unsigned my_last_spins;
611: volatile unsigned junk;
612: # define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
613: int i;
614:
615: if (!GC_test_and_set(&GC_allocate_lock)) {
616: return;
617: }
618: junk = 0;
619: my_spin_max = spin_max;
620: my_last_spins = last_spins;
621: for (i = 0; i < my_spin_max; i++) {
622: if (GC_collecting) goto yield;
623: if (i < my_last_spins/2 || GC_allocate_lock) {
624: PAUSE;
625: continue;
626: }
627: if (!GC_test_and_set(&GC_allocate_lock)) {
628: /*
629: * got it!
630: * Spinning worked. Thus we're probably not being scheduled
631: * against the other process with which we were contending.
632: * Thus it makes sense to spin longer the next time.
633: */
634: last_spins = i;
635: spin_max = high_spin_max;
636: return;
637: }
638: }
639: /* We are probably being scheduled against the other process. Sleep. */
640: spin_max = low_spin_max;
641: yield:
642: for (i = 0;; ++i) {
643: if (!GC_test_and_set(&GC_allocate_lock)) {
644: return;
645: }
646: # define SLEEP_THRESHOLD 12
647: /* nanosleep(<= 2ms) just spins under Linux. We */
648: /* want to be careful to avoid that behavior. */
649: if (i < SLEEP_THRESHOLD) {
650: sched_yield();
651: } else {
652: struct timespec ts;
653:
654: if (i > 26) i = 26;
655: /* Don't wait for more than about 60msecs, even */
656: /* under extreme contention. */
657: ts.tv_sec = 0;
658: ts.tv_nsec = 1 << i;
659: nanosleep(&ts, 0);
660: }
661: }
662: }
663:
664: # endif /* LINUX_THREADS */
665:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>