summaryrefslogtreecommitdiff
path: root/pthread_stop_world.c
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2022-03-27 23:46:02 +0300
committerIvan Maidanski <ivmai@mail.ru>2022-03-27 23:46:02 +0300
commiteddbd924a208aa4f25717b736cf01aac3c600450 (patch)
treecbdfe07b74c46e90a74588bacdcca6467f77c054 /pthread_stop_world.c
parentb3fa1641bfb784931d39f88181067771fc553674 (diff)
downloadbdwgc-eddbd924a208aa4f25717b736cf01aac3c600450.tar.gz
Allow to use same signal for thread suspend and restart
* include/gc/gc.h [GC_THREADS] (GC_set_thr_restart_signal): Update comment. * pthread_stop_world.c [!NACL && !GC_OPENBSD_UTHREADS] (GC_sig_suspend): Likewise. * pthread_stop_world.c [!NACL && !GC_OPENBSD_UTHREADS] (GC_suspend_handler_inner): If THREAD_RESTARTED bit of GC_stop_count is set then return immediately; initialize self after DISABLE_CANCEL(). * pthread_stop_world.c [!NACL && !GC_OPENBSD_UTHREADS] (GC_restart_handler): Reformat comment. * pthread_stop_world.c [!NACL && !GC_OPENBSD_UTHREADS && GC_ENABLE_SUSPEND_THREAD] (GC_suspend_thread): Declare saved_stop_count local variable; set suspended_ext to true w/o atomic store if thread is self or FINISHED flag is set; move AO_store_release(&t->suspended_ext,TRUE) down to be near raise_signal() call; increment GC_stop_count (by THREAD_RESTARTED) before raise_signal() call and restore the value after sem_wait(). * pthread_stop_world.c [!NACL && !GC_OPENBSD_UTHREADS] (GC_stop_init): Log a message instead of abort if GC_sig_suspend == GC_sig_thr_restart; do not call sigaction(GC_sig_thr_restart) if GC_sig_suspend == GC_sig_thr_restart. * tests/disclaim.c [GC_PTHREADS] (main): Call GC_set_suspend_signal(GC_get_thr_restart_signal()) before GC init.
Diffstat (limited to 'pthread_stop_world.c')
-rw-r--r--pthread_stop_world.c52
1 files changed, 32 insertions, 20 deletions
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
index 11655282..6a3ed809 100644
--- a/pthread_stop_world.c
+++ b/pthread_stop_world.c
@@ -179,6 +179,7 @@ STATIC volatile AO_t GC_stop_count;
/* a non-constant expression (e.g., in case of SIGRTMIN), */
/* actual signal numbers are determined by GC_stop_init() */
/* unless manually set (before GC initialization). */
+ /* Might be set to the same signal number. */
STATIC int GC_sig_suspend = SIGNAL_UNSET;
STATIC int GC_sig_thr_restart = SIGNAL_UNSET;
@@ -318,18 +319,20 @@ GC_INLINE void GC_store_stack_ptr(GC_thread me)
STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED,
void * context GC_ATTR_UNUSED)
{
- pthread_t self = pthread_self();
+ pthread_t self;
GC_thread me;
# ifdef E2K
ptr_t bs_lo;
size_t stack_size;
# endif
IF_CANCEL(int cancel_state;)
- AO_t my_stop_count = ao_load_acquire_async(&GC_stop_count)
- & ~(word)THREAD_RESTARTED;
+ AO_t my_stop_count = ao_load_acquire_async(&GC_stop_count);
/* After the barrier, this thread should see */
/* the actual content of GC_threads. */
+ if ((my_stop_count & THREAD_RESTARTED) != 0)
+ return; /* Restarting the world. */
+
DISABLE_CANCEL(cancel_state);
/* pthread_setcancelstate is not defined to be async-signal-safe. */
/* But the glibc version appears to be in the absence of */
@@ -339,6 +342,8 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED,
/* out of it. In fact, it looks to me like an async-signal-safe */
/* cancellation point is inherently a problem, unless there is */
/* some way to disable cancellation in the handler. */
+
+ self = pthread_self();
# ifdef DEBUG_THREADS
GC_log_printf("Suspending %p\n", (void *)self);
# endif
@@ -550,13 +555,10 @@ STATIC void GC_restart_handler(int sig)
if (sig != GC_sig_thr_restart)
ABORT("Bad signal in restart handler");
- /*
- ** Note: even if we don't do anything useful here,
- ** it would still be necessary to have a signal handler,
- ** rather than ignoring the signals, otherwise
- ** the signals will not be delivered at all, and
- ** will thus not interrupt the sigsuspend() above.
- */
+ /* Note: even if we do not do anything useful here, it would still */
+ /* be necessary to have a signal handler, rather than ignoring the */
+ /* signals, otherwise the signals will not be delivered at all, */
+ /* and will thus not interrupt the sigsuspend() above. */
# ifdef DEBUG_THREADS
GC_log_printf("In GC_restart_handler for %p\n", (void *)pthread_self());
errno = old_errno;
@@ -643,6 +645,7 @@ STATIC void GC_restart_handler(int sig)
GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread) {
GC_thread t;
+ AO_t saved_stop_count;
IF_CANCEL(int cancel_state;)
DCL_LOCK_STATE;
@@ -653,10 +656,8 @@ STATIC void GC_restart_handler(int sig)
return;
}
- /* Set the flag making the change visible to the signal handler. */
- AO_store_release(&t->suspended_ext, TRUE);
-
if (THREAD_EQUAL((pthread_t)thread, pthread_self())) {
+ t -> suspended_ext = TRUE;
UNLOCK();
/* It is safe as "t" cannot become invalid here (no race with */
/* GC_unregister_my_thread). */
@@ -664,6 +665,7 @@ STATIC void GC_restart_handler(int sig)
return;
}
if ((t -> flags & FINISHED) != 0) {
+ t -> suspended_ext = TRUE;
/* Terminated but not joined yet. */
UNLOCK();
return;
@@ -691,6 +693,13 @@ STATIC void GC_restart_handler(int sig)
/* be trying to acquire this lock too, and the suspend handler */
/* execution is deferred until the write fault handler completes. */
+ saved_stop_count = GC_stop_count;
+ GC_ASSERT((saved_stop_count & THREAD_RESTARTED) != 0);
+ AO_store(&GC_stop_count, saved_stop_count + THREAD_RESTARTED);
+
+ /* Set the flag making the change visible to the signal handler. */
+ AO_store_release(&t->suspended_ext, TRUE);
+
/* TODO: Support GC_retry_signals (not needed for TSan) */
switch (raise_signal(t, GC_sig_suspend)) {
/* ESRCH cannot happen as terminated threads are handled above. */
@@ -709,6 +718,8 @@ STATIC void GC_restart_handler(int sig)
}
if (GC_manual_vdb)
GC_release_dirty_lock();
+ AO_store(&GC_stop_count, saved_stop_count); /* restore the counter */
+
RESTORE_CANCEL(cancel_state);
UNLOCK();
}
@@ -1332,8 +1343,6 @@ GC_INNER void GC_stop_init(void)
GC_sig_suspend = SIG_SUSPEND;
if (SIGNAL_UNSET == GC_sig_thr_restart)
GC_sig_thr_restart = SIG_THR_RESTART;
- if (GC_sig_suspend == GC_sig_thr_restart)
- ABORT("Cannot use same signal for thread suspend and resume");
if (sem_init(&GC_suspend_ack_sem, GC_SEM_INIT_PSHARED, 0) != 0)
ABORT("sem_init failed");
@@ -1367,12 +1376,15 @@ GC_INNER void GC_stop_init(void)
ABORT("Cannot set SIG_SUSPEND handler");
}
-# ifndef SUSPEND_HANDLER_NO_CONTEXT
- act.sa_flags &= ~SA_SIGINFO;
-# endif
- act.sa_handler = GC_restart_handler;
- if (sigaction(GC_sig_thr_restart, &act, NULL) != 0) {
+ if (GC_sig_suspend != GC_sig_thr_restart) {
+# ifndef SUSPEND_HANDLER_NO_CONTEXT
+ act.sa_flags &= ~SA_SIGINFO;
+# endif
+ act.sa_handler = GC_restart_handler;
+ if (sigaction(GC_sig_thr_restart, &act, NULL) != 0)
ABORT("Cannot set SIG_THR_RESTART handler");
+ } else {
+ GC_COND_LOG_PRINTF("Using same signal for suspend and restart\n");
}
/* Initialize suspend_handler_mask (excluding GC_sig_thr_restart). */