7 |
#include <semaphore.h> |
#include <semaphore.h> |
8 |
#include <errno.h> |
#include <errno.h> |
9 |
#include <unistd.h> |
#include <unistd.h> |
10 |
|
#include <sys/time.h> |
11 |
|
#ifndef HPUX |
12 |
|
# include <sys/select.h> |
13 |
|
/* Doesn't exist on HP/UX 11.11. */ |
14 |
|
#endif |
15 |
|
|
16 |
#if DEBUG_THREADS |
#if DEBUG_THREADS |
17 |
|
|
71 |
|
|
72 |
static sigset_t suspend_handler_mask; |
static sigset_t suspend_handler_mask; |
73 |
|
|
74 |
word GC_stop_count; /* Incremented at the beginning of GC_stop_world. */ |
volatile sig_atomic_t GC_stop_count; |
75 |
|
/* Incremented at the beginning of GC_stop_world. */ |
76 |
|
|
77 |
|
volatile sig_atomic_t GC_world_is_stopped = FALSE; |
78 |
|
/* FALSE ==> it is safe for threads to restart, i.e. */ |
79 |
|
/* they will see another suspend signal before they */ |
80 |
|
/* are expected to stop (unless they have voluntarily */ |
81 |
|
/* stopped). */ |
82 |
|
|
83 |
|
void GC_brief_async_signal_safe_sleep() |
84 |
|
{ |
85 |
|
struct timeval tv; |
86 |
|
tv.tv_sec = 0; |
87 |
|
tv.tv_usec = 1000 * TIME_LIMIT / 2; |
88 |
|
select(0, 0, 0, 0, &tv); |
89 |
|
} |
90 |
|
|
91 |
#ifdef GC_OSF1_THREADS |
#ifdef GC_OSF1_THREADS |
92 |
GC_bool GC_retry_signals = TRUE; |
GC_bool GC_retry_signals = TRUE; |
195 |
/* this thread a SIG_THR_RESTART signal. */ |
/* this thread a SIG_THR_RESTART signal. */ |
196 |
/* SIG_THR_RESTART should be masked at this point. Thus there */ |
/* SIG_THR_RESTART should be masked at this point. Thus there */ |
197 |
/* is no race. */ |
/* is no race. */ |
198 |
do { |
/* We do not continue until we receive a SIG_THR_RESTART, */ |
199 |
me->stop_info.signal = 0; |
/* but we do not take that as authoritative. (We may be */ |
200 |
sigsuspend(&suspend_handler_mask); /* Wait for signal */ |
/* accidentally restarted by one of the user signals we */ |
201 |
} while (me->stop_info.signal != SIG_THR_RESTART); |
/* don't block.) After we receive the signal, we use a */ |
202 |
|
/* primitive and expensive mechanism to wait until it's */ |
203 |
|
/* really safe to proceed. Under normal circumstances, */ |
204 |
|
/* this code should not be executed. */ |
205 |
|
sigsuspend(&suspend_handler_mask); /* Wait for signal */ |
206 |
|
while (GC_world_is_stopped && GC_stop_count == my_stop_count) { |
207 |
|
GC_brief_async_signal_safe_sleep(); |
208 |
|
# if DEBUG_THREADS |
209 |
|
GC_err_printf0("Sleeping in signal handler"); |
210 |
|
# endif |
211 |
|
} |
212 |
/* If the RESTART signal gets lost, we can still lose. That should be */ |
/* If the RESTART signal gets lost, we can still lose. That should be */ |
213 |
/* less likely than losing the SUSPEND signal, since we don't do much */ |
/* less likely than losing the SUSPEND signal, since we don't do much */ |
214 |
/* between the sem_post and sigsuspend. */ |
/* between the sem_post and sigsuspend. */ |
215 |
/* We'd need more handshaking to work around that, since we don't want */ |
/* We'd need more handshaking to work around that. */ |
216 |
/* to accidentally leave a RESTART signal pending, thus causing us to */ |
/* Simply dropping the sigsuspend call should be safe, but is unlikely */ |
217 |
/* continue prematurely in a future round. */ |
/* to be efficient. */ |
218 |
|
|
219 |
#if DEBUG_THREADS |
#if DEBUG_THREADS |
220 |
GC_printf1("Continuing 0x%lx\n", my_thread); |
GC_printf1("Continuing 0x%lx\n", my_thread); |
224 |
void GC_restart_handler(int sig) |
void GC_restart_handler(int sig) |
225 |
{ |
{ |
226 |
pthread_t my_thread = pthread_self(); |
pthread_t my_thread = pthread_self(); |
|
GC_thread me; |
|
227 |
|
|
228 |
if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler"); |
if (sig != SIG_THR_RESTART) ABORT("Bad signal in suspend_handler"); |
229 |
|
|
|
/* Let the GC_suspend_handler() know that we got a SIG_THR_RESTART. */ |
|
|
/* The lookup here is safe, since I'm doing this on behalf */ |
|
|
/* of a thread which holds the allocation lock in order */ |
|
|
/* to stop the world. Thus concurrent modification of the */ |
|
|
/* data structure is impossible. */ |
|
|
me = GC_lookup_thread(my_thread); |
|
|
me->stop_info.signal = SIG_THR_RESTART; |
|
|
|
|
230 |
/* |
/* |
231 |
** Note: even if we didn't do anything useful here, |
** Note: even if we don't do anything useful here, |
232 |
** it would still be necessary to have a signal handler, |
** it would still be necessary to have a signal handler, |
233 |
** rather than ignoring the signals, otherwise |
** rather than ignoring the signals, otherwise |
234 |
** the signals will not be delivered at all, and |
** the signals will not be delivered at all, and |
381 |
/* We should have previously waited for it to become zero. */ |
/* We should have previously waited for it to become zero. */ |
382 |
# endif /* PARALLEL_MARK */ |
# endif /* PARALLEL_MARK */ |
383 |
++GC_stop_count; |
++GC_stop_count; |
384 |
|
GC_world_is_stopped = TRUE; |
385 |
n_live_threads = GC_suspend_all(); |
n_live_threads = GC_suspend_all(); |
386 |
|
|
387 |
if (GC_retry_signals) { |
if (GC_retry_signals) { |
444 |
GC_printf0("World starting\n"); |
GC_printf0("World starting\n"); |
445 |
# endif |
# endif |
446 |
|
|
447 |
|
GC_world_is_stopped = FALSE; |
448 |
for (i = 0; i < THREAD_TABLE_SZ; i++) { |
for (i = 0; i < THREAD_TABLE_SZ; i++) { |
449 |
for (p = GC_threads[i]; p != 0; p = p -> next) { |
for (p = GC_threads[i]; p != 0; p = p -> next) { |
450 |
if (p -> id != my_thread) { |
if (p -> id != my_thread) { |
454 |
#if DEBUG_THREADS |
#if DEBUG_THREADS |
455 |
GC_printf1("Sending restart signal to 0x%lx\n", p -> id); |
GC_printf1("Sending restart signal to 0x%lx\n", p -> id); |
456 |
#endif |
#endif |
457 |
|
result = pthread_kill(p -> id, SIG_THR_RESTART); |
|
result = pthread_kill(p -> id, SIG_THR_RESTART); |
|
458 |
switch(result) { |
switch(result) { |
459 |
case ESRCH: |
case ESRCH: |
460 |
/* Not really there anymore. Possible? */ |
/* Not really there anymore. Possible? */ |