Annotation of OpenXM_contrib2/asir2000/gc/linux_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: * 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_kill_other_threads_np'
122: (was `__pthread_initial_thread_bos' but that disappeared),
123: which is a symbol defined in LinuxThreads, but (hopefully) not in other
124: thread packages.
125: */
126: extern char * __pthread_initial_thread_bos;
127: char **dummy_var_to_force_linux_threads = &__pthread_kill_other_threads_np;
128:
129: #define LINUX_THREADS_STACK_SIZE (2 * 1024 * 1024)
130:
131: static inline ptr_t GC_linux_thread_top_of_stack(void)
132: {
133: char *sp = GC_approx_sp();
134: ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1);
135: #if DEBUG_THREADS
136: GC_printf1("SP = %lx\n", (unsigned long)sp);
137: GC_printf1("TOS = %lx\n", (unsigned long)tos);
138: #endif
139: return tos;
140: }
141:
142: void GC_suspend_handler(int sig)
143: {
144: int dummy;
145: pthread_t my_thread = pthread_self();
146: GC_thread me;
147: sigset_t all_sigs;
148: sigset_t old_sigs;
149: int i;
150: sigset_t mask;
151:
152: if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
153:
154: #if DEBUG_THREADS
155: GC_printf1("Suspending 0x%x\n", my_thread);
156: #endif
157:
158: me = GC_lookup_thread(my_thread);
159: /* The lookup here is safe, since I'm doing this on behalf */
160: /* of a thread which holds the allocation lock in order */
161: /* to stop the world. Thus concurrent modification of the */
162: /* data structure is impossible. */
163: me -> stack_ptr = (ptr_t)(&dummy);
164: me -> stack_end = GC_linux_thread_top_of_stack();
165:
166: /* Tell the thread that wants to stop the world that this */
167: /* thread has been stopped. Note that sem_post() is */
168: /* the only async-signal-safe primitive in LinuxThreads. */
169: sem_post(&GC_suspend_ack_sem);
170:
171: /* Wait until that thread tells us to restart by sending */
172: /* this thread a SIG_RESTART signal. */
173: /* SIG_RESTART should be masked at this point. Thus there */
174: /* is no race. */
175: if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
176: if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed");
177: do {
178: me->signal = 0;
179: sigsuspend(&mask); /* Wait for signal */
180: } while (me->signal != SIG_RESTART);
181:
182: #if DEBUG_THREADS
183: GC_printf1("Continuing 0x%x\n", my_thread);
184: #endif
185: }
186:
187: void GC_restart_handler(int sig)
188: {
189: GC_thread me;
190:
191: if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler");
192:
193: /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */
194: /* The lookup here is safe, since I'm doing this on behalf */
195: /* of a thread which holds the allocation lock in order */
196: /* to stop the world. Thus concurrent modification of the */
197: /* data structure is impossible. */
198: me = GC_lookup_thread(pthread_self());
199: me->signal = SIG_RESTART;
200:
201: /*
202: ** Note: even if we didn't do anything useful here,
203: ** it would still be necessary to have a signal handler,
204: ** rather than ignoring the signals, otherwise
205: ** the signals will not be delivered at all, and
206: ** will thus not interrupt the sigsuspend() above.
207: */
208:
209: #if DEBUG_THREADS
210: GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self());
211: #endif
212: }
213:
214: GC_bool GC_thr_initialized = FALSE;
215:
216: # define THREAD_TABLE_SZ 128 /* Must be power of 2 */
217: volatile GC_thread GC_threads[THREAD_TABLE_SZ];
218:
219: /* Add a thread to GC_threads. We assume it wasn't already there. */
220: /* Caller holds allocation lock. */
221: GC_thread GC_new_thread(pthread_t id)
222: {
223: int hv = ((word)id) % THREAD_TABLE_SZ;
224: GC_thread result;
225: static struct GC_Thread_Rep first_thread;
226: static GC_bool first_thread_used = FALSE;
227:
228: if (!first_thread_used) {
229: result = &first_thread;
230: first_thread_used = TRUE;
231: /* Dont acquire allocation lock, since we may already hold it. */
232: } else {
233: result = (struct GC_Thread_Rep *)
234: GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
235: }
236: if (result == 0) return(0);
237: result -> id = id;
238: result -> next = GC_threads[hv];
239: GC_threads[hv] = result;
240: /* result -> flags = 0; */
241: return(result);
242: }
243:
244: /* Delete a thread from GC_threads. We assume it is there. */
245: /* (The code intentionally traps if it wasn't.) */
246: /* Caller holds allocation lock. */
247: void GC_delete_thread(pthread_t id)
248: {
249: int hv = ((word)id) % THREAD_TABLE_SZ;
250: register GC_thread p = GC_threads[hv];
251: register GC_thread prev = 0;
252:
253: while (!pthread_equal(p -> id, id)) {
254: prev = p;
255: p = p -> next;
256: }
257: if (prev == 0) {
258: GC_threads[hv] = p -> next;
259: } else {
260: prev -> next = p -> next;
261: }
262: }
263:
264: /* If a thread has been joined, but we have not yet */
265: /* been notified, then there may be more than one thread */
266: /* in the table with the same pthread id. */
267: /* This is OK, but we need a way to delete a specific one. */
268: void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
269: {
270: int hv = ((word)id) % THREAD_TABLE_SZ;
271: register GC_thread p = GC_threads[hv];
272: register GC_thread prev = 0;
273:
274: while (p != gc_id) {
275: prev = p;
276: p = p -> next;
277: }
278: if (prev == 0) {
279: GC_threads[hv] = p -> next;
280: } else {
281: prev -> next = p -> next;
282: }
283: }
284:
285: /* Return a GC_thread corresponding to a given thread_t. */
286: /* Returns 0 if it's not there. */
287: /* Caller holds allocation lock or otherwise inhibits */
288: /* updates. */
289: /* If there is more than one thread with the given id we */
290: /* return the most recent one. */
291: GC_thread GC_lookup_thread(pthread_t id)
292: {
293: int hv = ((word)id) % THREAD_TABLE_SZ;
294: register GC_thread p = GC_threads[hv];
295:
296: while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
297: return(p);
298: }
299:
300: /* Caller holds allocation lock. */
301: void GC_stop_world()
302: {
303: pthread_t my_thread = pthread_self();
304: register int i;
305: register GC_thread p;
306: register int n_live_threads = 0;
307: register int result;
308:
309: for (i = 0; i < THREAD_TABLE_SZ; i++) {
310: for (p = GC_threads[i]; p != 0; p = p -> next) {
311: if (p -> id != my_thread) {
312: if (p -> flags & FINISHED) continue;
313: n_live_threads++;
314: #if DEBUG_THREADS
315: GC_printf1("Sending suspend signal to 0x%x\n", p -> id);
316: #endif
317: result = pthread_kill(p -> id, SIG_SUSPEND);
318: switch(result) {
319: case ESRCH:
320: /* Not really there anymore. Possible? */
321: n_live_threads--;
322: break;
323: case 0:
324: break;
325: default:
326: ABORT("pthread_kill failed");
327: }
328: }
329: }
330: }
331: for (i = 0; i < n_live_threads; i++) {
332: sem_wait(&GC_suspend_ack_sem);
333: }
334: #if DEBUG_THREADS
335: GC_printf1("World stopped 0x%x\n", pthread_self());
336: #endif
337: }
338:
339: /* Caller holds allocation lock. */
340: void GC_start_world()
341: {
342: pthread_t my_thread = pthread_self();
343: register int i;
344: register GC_thread p;
345: register int n_live_threads = 0;
346: register int result;
347:
348: # if DEBUG_THREADS
349: GC_printf0("World starting\n");
350: # endif
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) continue;
356: n_live_threads++;
357: #if DEBUG_THREADS
358: GC_printf1("Sending restart signal to 0x%x\n", p -> id);
359: #endif
360: result = pthread_kill(p -> id, SIG_RESTART);
361: switch(result) {
362: case ESRCH:
363: /* Not really there anymore. Possible? */
364: n_live_threads--;
365: break;
366: case 0:
367: break;
368: default:
369: ABORT("pthread_kill failed");
370: }
371: }
372: }
373: }
374: #if DEBUG_THREADS
375: GC_printf0("World started\n");
376: #endif
377: }
378:
379: /* We hold allocation lock. We assume the world is stopped. */
380: void GC_push_all_stacks()
381: {
382: register int i;
383: register GC_thread p;
384: register ptr_t sp = GC_approx_sp();
385: register ptr_t lo, hi;
386: pthread_t me = pthread_self();
387:
388: if (!GC_thr_initialized) GC_thr_init();
389: #if DEBUG_THREADS
390: GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
391: #endif
392: for (i = 0; i < THREAD_TABLE_SZ; i++) {
393: for (p = GC_threads[i]; p != 0; p = p -> next) {
394: if (p -> flags & FINISHED) continue;
395: if (pthread_equal(p -> id, me)) {
396: lo = GC_approx_sp();
397: } else {
398: lo = p -> stack_ptr;
399: }
400: if ((p -> flags & MAIN_THREAD) == 0) {
401: if (pthread_equal(p -> id, me)) {
402: hi = GC_linux_thread_top_of_stack();
403: } else {
404: hi = p -> stack_end;
405: }
406: } else {
407: /* The original stack. */
408: hi = GC_stackbottom;
409: }
410: #if DEBUG_THREADS
411: GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
412: (unsigned long) p -> id,
413: (unsigned long) lo, (unsigned long) hi);
414: #endif
415: GC_push_all_stack(lo, hi);
416: }
417: }
418: }
419:
420:
421: /* We hold the allocation lock. */
422: void GC_thr_init()
423: {
424: GC_thread t;
425: struct sigaction act;
426:
427: if (GC_thr_initialized) return;
428: GC_thr_initialized = TRUE;
429:
430: if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
431: ABORT("sem_init failed");
432:
433: act.sa_flags = SA_RESTART;
434: if (sigfillset(&act.sa_mask) != 0) {
435: ABORT("sigfillset() failed");
436: }
437: /* SIG_RESTART is unmasked by the handler when necessary. */
438: act.sa_handler = GC_suspend_handler;
439: if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
440: ABORT("Cannot set SIG_SUSPEND handler");
441: }
442:
443: act.sa_handler = GC_restart_handler;
444: if (sigaction(SIG_RESTART, &act, NULL) != 0) {
445: ABORT("Cannot set SIG_SUSPEND handler");
446: }
447:
448: /* Add the initial thread, so we can stop it. */
449: t = GC_new_thread(pthread_self());
450: t -> stack_ptr = 0;
451: t -> flags = DETACHED | MAIN_THREAD;
452: }
453:
454: int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
455: {
456: sigset_t fudged_set;
457:
458: if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
459: fudged_set = *set;
460: sigdelset(&fudged_set, SIG_SUSPEND);
461: set = &fudged_set;
462: }
463: return(pthread_sigmask(how, set, oset));
464: }
465:
466: struct start_info {
467: void *(*start_routine)(void *);
468: void *arg;
469: word flags;
470: sem_t registered; /* 1 ==> in our thread table, but */
471: /* parent hasn't yet noticed. */
472: };
473:
474:
475: void GC_thread_exit_proc(void *arg)
476: {
477: GC_thread me;
478: struct start_info * si = arg;
479:
480: LOCK();
481: me = GC_lookup_thread(pthread_self());
482: if (me -> flags & DETACHED) {
483: GC_delete_thread(pthread_self());
484: } else {
485: me -> flags |= FINISHED;
486: }
487: UNLOCK();
488: }
489:
490: int GC_pthread_join(pthread_t thread, void **retval)
491: {
492: int result;
493: GC_thread thread_gc_id;
494:
495: LOCK();
496: thread_gc_id = GC_lookup_thread(thread);
497: /* This is guaranteed to be the intended one, since the thread id */
498: /* cant have been recycled by pthreads. */
499: UNLOCK();
500: result = pthread_join(thread, retval);
501: LOCK();
502: /* Here the pthread thread id may have been recycled. */
503: GC_delete_gc_thread(thread, thread_gc_id);
504: UNLOCK();
505: return result;
506: }
507:
508: void * GC_start_routine(void * arg)
509: {
510: struct start_info * si = arg;
511: void * result;
512: GC_thread me;
513: pthread_t my_pthread;
514: void *(*start)(void *);
515: void *start_arg;
516:
517: my_pthread = pthread_self();
518: LOCK();
519: me = GC_new_thread(my_pthread);
520: me -> flags = si -> flags;
521: me -> stack_ptr = 0;
522: me -> stack_end = 0;
523: UNLOCK();
524: start = si -> start_routine;
525: start_arg = si -> arg;
526: sem_post(&(si -> registered));
527: pthread_cleanup_push(GC_thread_exit_proc, si);
528: # ifdef DEBUG_THREADS
529: GC_printf1("Starting thread 0x%lx\n", pthread_self());
530: GC_printf1("pid = %ld\n", (long) getpid());
531: GC_printf1("sp = 0x%lx\n", (long) &arg);
532: GC_printf1("start_routine = 0x%lx\n", start);
533: # endif
534: result = (*start)(start_arg);
535: #if DEBUG_THREADS
536: GC_printf1("Finishing thread 0x%x\n", pthread_self());
537: #endif
538: me -> status = result;
539: me -> flags |= FINISHED;
540: pthread_cleanup_pop(1);
541: /* Cleanup acquires lock, ensuring that we can't exit */
542: /* while a collection that thinks we're alive is trying to stop */
543: /* us. */
544: return(result);
545: }
546:
547: int
548: GC_pthread_create(pthread_t *new_thread,
549: const pthread_attr_t *attr,
550: void *(*start_routine)(void *), void *arg)
551: {
552: int result;
553: GC_thread t;
554: pthread_t my_new_thread;
555: void * stack;
556: size_t stacksize;
557: pthread_attr_t new_attr;
558: int detachstate;
559: word my_flags = 0;
560: struct start_info * si = GC_malloc(sizeof(struct start_info));
561: /* This is otherwise saved only in an area mmapped by the thread */
562: /* library, which isn't visible to the collector. */
563:
564: if (0 == si) return(ENOMEM);
565: sem_init(&(si -> registered), 0, 0);
566: si -> start_routine = start_routine;
567: si -> arg = arg;
568: LOCK();
569: if (!GC_thr_initialized) GC_thr_init();
570: if (NULL == attr) {
571: stack = 0;
572: (void) pthread_attr_init(&new_attr);
573: } else {
574: new_attr = *attr;
575: }
576: pthread_attr_getdetachstate(&new_attr, &detachstate);
577: if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
578: si -> flags = my_flags;
579: UNLOCK();
580: result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
581: /* Wait until child has been added to the thread table. */
582: /* This also ensures that we hold onto si until the child is done */
583: /* with it. Thus it doesn't matter whether it is otherwise */
584: /* visible to the collector. */
585: if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed");
586: sem_destroy(&(si -> registered));
587: /* pthread_attr_destroy(&new_attr); */
588: /* pthread_attr_destroy(&new_attr); */
589: return(result);
590: }
591:
592: GC_bool GC_collecting = 0;
593: /* A hint that we're in the collector and */
594: /* holding the allocation lock for an */
595: /* extended period. */
596:
597: /* Reasonably fast spin locks. Basically the same implementation */
598: /* as STL alloc.h. This isn't really the right way to do this. */
599: /* but until the POSIX scheduling mess gets straightened out ... */
600:
601: volatile unsigned int GC_allocate_lock = 0;
602:
603:
604: void GC_lock()
605: {
606: # define low_spin_max 30 /* spin cycles if we suspect uniprocessor */
607: # define high_spin_max 1000 /* spin cycles for multiprocessor */
608: static unsigned spin_max = low_spin_max;
609: unsigned my_spin_max;
610: static unsigned last_spins = 0;
611: unsigned my_last_spins;
612: volatile unsigned junk;
613: # define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
614: int i;
615:
616: if (!GC_test_and_set(&GC_allocate_lock)) {
617: return;
618: }
619: junk = 0;
620: my_spin_max = spin_max;
621: my_last_spins = last_spins;
622: for (i = 0; i < my_spin_max; i++) {
623: if (GC_collecting) goto yield;
624: if (i < my_last_spins/2 || GC_allocate_lock) {
625: PAUSE;
626: continue;
627: }
628: if (!GC_test_and_set(&GC_allocate_lock)) {
629: /*
630: * got it!
631: * Spinning worked. Thus we're probably not being scheduled
632: * against the other process with which we were contending.
633: * Thus it makes sense to spin longer the next time.
634: */
635: last_spins = i;
636: spin_max = high_spin_max;
637: return;
638: }
639: }
640: /* We are probably being scheduled against the other process. Sleep. */
641: spin_max = low_spin_max;
642: yield:
643: for (i = 0;; ++i) {
644: if (!GC_test_and_set(&GC_allocate_lock)) {
645: return;
646: }
647: # define SLEEP_THRESHOLD 12
648: /* nanosleep(<= 2ms) just spins under Linux. We */
649: /* want to be careful to avoid that behavior. */
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: # endif /* LINUX_THREADS */
666:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>