Annotation of OpenXM_contrib2/asir2000/gc/pthread_stop_world.c, Revision 1.1
1.1 ! noro 1: #include "private/pthread_support.h"
! 2:
! 3: #if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
! 4: && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
! 5: && !defined(GC_DARWIN_THREADS)
! 6:
! 7: #include <signal.h>
! 8: #include <semaphore.h>
! 9: #include <errno.h>
! 10: #include <unistd.h>
! 11:
! 12: #if DEBUG_THREADS
! 13:
! 14: #ifndef NSIG
! 15: # if defined(MAXSIG)
! 16: # define NSIG (MAXSIG+1)
! 17: # elif defined(_NSIG)
! 18: # define NSIG _NSIG
! 19: # elif defined(__SIGRTMAX)
! 20: # define NSIG (__SIGRTMAX+1)
! 21: # else
! 22: --> please fix it
! 23: # endif
! 24: #endif
! 25:
! 26: void GC_print_sig_mask()
! 27: {
! 28: sigset_t blocked;
! 29: int i;
! 30:
! 31: if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
! 32: ABORT("pthread_sigmask");
! 33: GC_printf0("Blocked: ");
! 34: for (i = 1; i < NSIG; i++) {
! 35: if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
! 36: }
! 37: GC_printf0("\n");
! 38: }
! 39:
! 40: #endif
! 41:
! 42: word GC_stop_count; /* Incremented at the beginning of GC_stop_world. */
! 43:
! 44: #ifdef GC_OSF1_THREADS
! 45: GC_bool GC_retry_signals = TRUE;
! 46: #else
! 47: GC_bool GC_retry_signals = FALSE;
! 48: #endif
! 49:
! 50: /*
! 51: * We use signals to stop threads during GC.
! 52: *
! 53: * Suspended threads wait in signal handler for SIG_THR_RESTART.
! 54: * That's more portable than semaphores or condition variables.
! 55: * (We do use sem_post from a signal handler, but that should be portable.)
! 56: *
! 57: * The thread suspension signal SIG_SUSPEND is now defined in gc_priv.h.
! 58: * Note that we can't just stop a thread; we need it to save its stack
! 59: * pointer(s) and acknowledge.
! 60: */
! 61:
! 62: #ifndef SIG_THR_RESTART
! 63: # if defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS)
! 64: # ifdef _SIGRTMIN
! 65: # define SIG_THR_RESTART _SIGRTMIN + 5
! 66: # else
! 67: # define SIG_THR_RESTART SIGRTMIN + 5
! 68: # endif
! 69: # else
! 70: # define SIG_THR_RESTART SIGXCPU
! 71: # endif
! 72: #endif
! 73:
! 74: sem_t GC_suspend_ack_sem;
! 75:
! 76: void GC_suspend_handler(int sig)
! 77: {
! 78: int dummy;
! 79: pthread_t my_thread = pthread_self();
! 80: GC_thread me;
! 81: sigset_t mask;
! 82: # ifdef PARALLEL_MARK
! 83: word my_mark_no = GC_mark_no;
! 84: /* Marker can't proceed until we acknowledge. Thus this is */
! 85: /* guaranteed to be the mark_no correspending to our */
! 86: /* suspension, i.e. the marker can't have incremented it yet. */
! 87: # endif
! 88: word my_stop_count = GC_stop_count;
! 89:
! 90: if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
! 91:
! 92: #if DEBUG_THREADS
! 93: GC_printf1("Suspending 0x%lx\n", my_thread);
! 94: #endif
! 95:
! 96: me = GC_lookup_thread(my_thread);
! 97: /* The lookup here is safe, since I'm doing this on behalf */
! 98: /* of a thread which holds the allocation lock in order */
! 99: /* to stop the world. Thus concurrent modification of the */
! 100: /* data structure is impossible. */
! 101: if (me -> stop_info.last_stop_count == my_stop_count) {
! 102: /* Duplicate signal. OK if we are retrying. */
! 103: if (!GC_retry_signals) {
! 104: WARN("Duplicate suspend signal in thread %lx\n",
! 105: pthread_self());
! 106: }
! 107: return;
! 108: }
! 109: # ifdef SPARC
! 110: me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack();
! 111: # else
! 112: me -> stop_info.stack_ptr = (ptr_t)(&dummy);
! 113: # endif
! 114: # ifdef IA64
! 115: me -> backing_store_ptr = (ptr_t)GC_save_regs_in_stack();
! 116: # endif
! 117:
! 118: /* Tell the thread that wants to stop the world that this */
! 119: /* thread has been stopped. Note that sem_post() is */
! 120: /* the only async-signal-safe primitive in LinuxThreads. */
! 121: sem_post(&GC_suspend_ack_sem);
! 122: me -> stop_info.last_stop_count = my_stop_count;
! 123:
! 124: /* Wait until that thread tells us to restart by sending */
! 125: /* this thread a SIG_THR_RESTART signal. */
! 126: /* SIG_THR_RESTART should be masked at this point. Thus there */
! 127: /* is no race. */
! 128: if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
! 129: if (sigdelset(&mask, SIG_THR_RESTART) != 0) ABORT("sigdelset() failed");
! 130: # ifdef NO_SIGNALS
! 131: if (sigdelset(&mask, SIGINT) != 0) ABORT("sigdelset() failed");
! 132: if (sigdelset(&mask, SIGQUIT) != 0) ABORT("sigdelset() failed");
! 133: if (sigdelset(&mask, SIGTERM) != 0) ABORT("sigdelset() failed");
! 134: if (sigdelset(&mask, SIGABRT) != 0) ABORT("sigdelset() failed");
! 135: # endif
! 136: do {
! 137: me->stop_info.signal = 0;
! 138: sigsuspend(&mask); /* Wait for signal */
! 139: } while (me->stop_info.signal != SIG_THR_RESTART);
! 140: /* If the RESTART signal gets lost, we can still lose. That should be */
! 141: /* less likely than losing the SUSPEND signal, since we don't do much */
! 142: /* between the sem_post and sigsuspend. */
! 143: /* We'd need more handshaking to work around that, since we don't want */
! 144: /* to accidentally leave a RESTART signal pending, thus causing us to */
! 145: /* continue prematurely in a future round. */
! 146:
! 147: #if DEBUG_THREADS
! 148: GC_printf1("Continuing 0x%lx\n", my_thread);
! 149: #endif
! 150: }
! 151:
! 152: void GC_restart_handler(int sig)
! 153: {
! 154: pthread_t my_thread = pthread_self();
! 155: GC_thread me;
! 156:
! 157: if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler");
! 158:
! 159: /* Let the GC_suspend_handler() know that we got a SIG_THR_RESTART. */
! 160: /* The lookup here is safe, since I'm doing this on behalf */
! 161: /* of a thread which holds the allocation lock in order */
! 162: /* to stop the world. Thus concurrent modification of the */
! 163: /* data structure is impossible. */
! 164: me = GC_lookup_thread(my_thread);
! 165: me->stop_info.signal = SIG_THR_RESTART;
! 166:
! 167: /*
! 168: ** Note: even if we didn't do anything useful here,
! 169: ** it would still be necessary to have a signal handler,
! 170: ** rather than ignoring the signals, otherwise
! 171: ** the signals will not be delivered at all, and
! 172: ** will thus not interrupt the sigsuspend() above.
! 173: */
! 174:
! 175: #if DEBUG_THREADS
! 176: GC_printf1("In GC_restart_handler for 0x%lx\n", pthread_self());
! 177: #endif
! 178: }
! 179:
! 180: # ifdef IA64
! 181: # define IF_IA64(x) x
! 182: # else
! 183: # define IF_IA64(x)
! 184: # endif
! 185: /* We hold allocation lock. Should do exactly the right thing if the */
! 186: /* world is stopped. Should not fail if it isn't. */
! 187: void GC_push_all_stacks()
! 188: {
! 189: int i;
! 190: GC_thread p;
! 191: ptr_t lo, hi;
! 192: /* On IA64, we also need to scan the register backing store. */
! 193: IF_IA64(ptr_t bs_lo; ptr_t bs_hi;)
! 194: pthread_t me = pthread_self();
! 195:
! 196: if (!GC_thr_initialized) GC_thr_init();
! 197: #if DEBUG_THREADS
! 198: GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
! 199: #endif
! 200: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 201: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 202: if (p -> flags & FINISHED) continue;
! 203: if (pthread_equal(p -> id, me)) {
! 204: # ifdef SPARC
! 205: lo = (ptr_t)GC_save_regs_in_stack();
! 206: # else
! 207: lo = GC_approx_sp();
! 208: # endif
! 209: IF_IA64(bs_hi = (ptr_t)GC_save_regs_in_stack();)
! 210: } else {
! 211: lo = p -> stop_info.stack_ptr;
! 212: IF_IA64(bs_hi = p -> backing_store_ptr;)
! 213: }
! 214: if ((p -> flags & MAIN_THREAD) == 0) {
! 215: hi = p -> stack_end;
! 216: IF_IA64(bs_lo = p -> backing_store_end);
! 217: } else {
! 218: /* The original stack. */
! 219: hi = GC_stackbottom;
! 220: IF_IA64(bs_lo = BACKING_STORE_BASE;)
! 221: }
! 222: #if DEBUG_THREADS
! 223: GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
! 224: (unsigned long) p -> id,
! 225: (unsigned long) lo, (unsigned long) hi);
! 226: #endif
! 227: if (0 == lo) ABORT("GC_push_all_stacks: sp not set!\n");
! 228: # ifdef STACK_GROWS_UP
! 229: /* We got them backwards! */
! 230: GC_push_all_stack(hi, lo);
! 231: # else
! 232: GC_push_all_stack(lo, hi);
! 233: # endif
! 234: # ifdef IA64
! 235: if (pthread_equal(p -> id, me)) {
! 236: GC_push_all_eager(bs_lo, bs_hi);
! 237: } else {
! 238: GC_push_all_stack(bs_lo, bs_hi);
! 239: }
! 240: # endif
! 241: }
! 242: }
! 243: }
! 244:
! 245: /* There seems to be a very rare thread stopping problem. To help us */
! 246: /* debug that, we save the ids of the stopping thread. */
! 247: pthread_t GC_stopping_thread;
! 248: int GC_stopping_pid;
! 249:
! 250: /* We hold the allocation lock. Suspend all threads that might */
! 251: /* still be running. Return the number of suspend signals that */
! 252: /* were sent. */
! 253: int GC_suspend_all()
! 254: {
! 255: int n_live_threads = 0;
! 256: int i;
! 257: GC_thread p;
! 258: int result;
! 259: pthread_t my_thread = pthread_self();
! 260:
! 261: GC_stopping_thread = my_thread; /* debugging only. */
! 262: GC_stopping_pid = getpid(); /* debugging only. */
! 263: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 264: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 265: if (p -> id != my_thread) {
! 266: if (p -> flags & FINISHED) continue;
! 267: if (p -> stop_info.last_stop_count == GC_stop_count) continue;
! 268: if (p -> thread_blocked) /* Will wait */ continue;
! 269: n_live_threads++;
! 270: #if DEBUG_THREADS
! 271: GC_printf1("Sending suspend signal to 0x%lx\n", p -> id);
! 272: #endif
! 273:
! 274: result = pthread_kill(p -> id, SIG_SUSPEND);
! 275: switch(result) {
! 276: case ESRCH:
! 277: /* Not really there anymore. Possible? */
! 278: n_live_threads--;
! 279: break;
! 280: case 0:
! 281: break;
! 282: default:
! 283: ABORT("pthread_kill failed");
! 284: }
! 285: }
! 286: }
! 287: }
! 288: return n_live_threads;
! 289: }
! 290:
! 291: /* Caller holds allocation lock. */
! 292: void GC_stop_world()
! 293: {
! 294: int i;
! 295: int n_live_threads;
! 296: int code;
! 297:
! 298: #if DEBUG_THREADS
! 299: GC_printf1("Stopping the world from 0x%lx\n", pthread_self());
! 300: #endif
! 301:
! 302: /* Make sure all free list construction has stopped before we start. */
! 303: /* No new construction can start, since free list construction is */
! 304: /* required to acquire and release the GC lock before it starts, */
! 305: /* and we have the lock. */
! 306: # ifdef PARALLEL_MARK
! 307: GC_acquire_mark_lock();
! 308: GC_ASSERT(GC_fl_builder_count == 0);
! 309: /* We should have previously waited for it to become zero. */
! 310: # endif /* PARALLEL_MARK */
! 311: ++GC_stop_count;
! 312: n_live_threads = GC_suspend_all();
! 313:
! 314: if (GC_retry_signals) {
! 315: unsigned long wait_usecs = 0; /* Total wait since retry. */
! 316: # define WAIT_UNIT 3000
! 317: # define RETRY_INTERVAL 100000
! 318: for (;;) {
! 319: int ack_count;
! 320:
! 321: sem_getvalue(&GC_suspend_ack_sem, &ack_count);
! 322: if (ack_count == n_live_threads) break;
! 323: if (wait_usecs > RETRY_INTERVAL) {
! 324: int newly_sent = GC_suspend_all();
! 325:
! 326: # ifdef CONDPRINT
! 327: if (GC_print_stats) {
! 328: GC_printf1("Resent %ld signals after timeout\n",
! 329: newly_sent);
! 330: }
! 331: # endif
! 332: sem_getvalue(&GC_suspend_ack_sem, &ack_count);
! 333: if (newly_sent < n_live_threads - ack_count) {
! 334: WARN("Lost some threads during GC_stop_world?!\n",0);
! 335: n_live_threads = ack_count + newly_sent;
! 336: }
! 337: wait_usecs = 0;
! 338: }
! 339: usleep(WAIT_UNIT);
! 340: wait_usecs += WAIT_UNIT;
! 341: }
! 342: }
! 343: for (i = 0; i < n_live_threads; i++) {
! 344: if (0 != (code = sem_wait(&GC_suspend_ack_sem))) {
! 345: GC_err_printf1("Sem_wait returned %ld\n", (unsigned long)code);
! 346: ABORT("sem_wait for handler failed");
! 347: }
! 348: }
! 349: # ifdef PARALLEL_MARK
! 350: GC_release_mark_lock();
! 351: # endif
! 352: #if DEBUG_THREADS
! 353: GC_printf1("World stopped from 0x%lx\n", pthread_self());
! 354: #endif
! 355: GC_stopping_thread = 0; /* debugging only */
! 356: }
! 357:
! 358: /* Caller holds allocation lock, and has held it continuously since */
! 359: /* the world stopped. */
! 360: void GC_start_world()
! 361: {
! 362: pthread_t my_thread = pthread_self();
! 363: register int i;
! 364: register GC_thread p;
! 365: register int n_live_threads = 0;
! 366: register int result;
! 367:
! 368: # if DEBUG_THREADS
! 369: GC_printf0("World starting\n");
! 370: # endif
! 371:
! 372: for (i = 0; i < THREAD_TABLE_SZ; i++) {
! 373: for (p = GC_threads[i]; p != 0; p = p -> next) {
! 374: if (p -> id != my_thread) {
! 375: if (p -> flags & FINISHED) continue;
! 376: if (p -> thread_blocked) continue;
! 377: n_live_threads++;
! 378: #if DEBUG_THREADS
! 379: GC_printf1("Sending restart signal to 0x%lx\n", p -> id);
! 380: #endif
! 381:
! 382: result = pthread_kill(p -> id, SIG_THR_RESTART);
! 383: switch(result) {
! 384: case ESRCH:
! 385: /* Not really there anymore. Possible? */
! 386: n_live_threads--;
! 387: break;
! 388: case 0:
! 389: break;
! 390: default:
! 391: ABORT("pthread_kill failed");
! 392: }
! 393: }
! 394: }
! 395: }
! 396: #if DEBUG_THREADS
! 397: GC_printf0("World started\n");
! 398: #endif
! 399: }
! 400:
! 401: void GC_stop_init() {
! 402: struct sigaction act;
! 403:
! 404: if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
! 405: ABORT("sem_init failed");
! 406:
! 407: act.sa_flags = SA_RESTART;
! 408: if (sigfillset(&act.sa_mask) != 0) {
! 409: ABORT("sigfillset() failed");
! 410: }
! 411: # ifdef NO_SIGNALS
! 412: if (sigdelset(&act.sa_mask, SIGINT) != 0
! 413: || sigdelset(&act.sa_mask, SIGQUIT != 0)
! 414: || sigdelset(&act.sa_mask, SIGABRT != 0)
! 415: || sigdelset(&act.sa_mask, SIGTERM != 0)) {
! 416: ABORT("sigdelset() failed");
! 417: }
! 418: # endif
! 419:
! 420: /* SIG_THR_RESTART is unmasked by the handler when necessary. */
! 421: act.sa_handler = GC_suspend_handler;
! 422: if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
! 423: ABORT("Cannot set SIG_SUSPEND handler");
! 424: }
! 425:
! 426: act.sa_handler = GC_restart_handler;
! 427: if (sigaction(SIG_THR_RESTART, &act, NULL) != 0) {
! 428: ABORT("Cannot set SIG_THR_RESTART handler");
! 429: }
! 430:
! 431: /* Check for GC_RETRY_SIGNALS. */
! 432: if (0 != GETENV("GC_RETRY_SIGNALS")) {
! 433: GC_retry_signals = TRUE;
! 434: }
! 435: if (0 != GETENV("GC_NO_RETRY_SIGNALS")) {
! 436: GC_retry_signals = FALSE;
! 437: }
! 438: # ifdef CONDPRINT
! 439: if (GC_print_stats && GC_retry_signals) {
! 440: GC_printf0("Will retry suspend signal if necessary.\n");
! 441: }
! 442: # endif
! 443: }
! 444:
! 445: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>