root/gc/pthread_stop_world.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GC_print_sig_mask
  2. GC_remove_allowed_signals
  3. GC_suspend_handler
  4. GC_suspend_handler
  5. GC_suspend_handler_inner
  6. GC_restart_handler
  7. GC_push_all_stacks
  8. GC_suspend_all
  9. GC_stop_world
  10. GC_start_world
  11. GC_stop_init

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

/* [<][>][^][v][top][bottom][index][help] */