[BACK]Return to pthread_stop_world.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib2 / asir2000 / gc

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>