[compiler-rt] r218070 - tsan: more careful handling of signals
Dmitry Vyukov
dvyukov at google.com
Thu Sep 18 12:03:32 PDT 2014
Author: dvyukov
Date: Thu Sep 18 14:03:32 2014
New Revision: 218070
URL: http://llvm.org/viewvc/llvm-project?rev=218070&view=rev
Log:
tsan: more careful handling of signals
On some tests we see that signals are not delivered
when a thread is blocked in epoll_wait. The hypothesis
is that the signal is delivered right before epoll_wait
call. The signal is queued as in_blocking_func is not set
yet, and then the thread just blocks in epoll_wait forever.
So double check pending signals *after* setting
in_blocking_func. This way we either queue a signal
and handle it in the beginning of a blocking func,
or process the signal synchronously if it's delivered
when in_blocking_func is set.
Modified:
compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc
Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc?rev=218070&r1=218069&r2=218070&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc Thu Sep 18 14:03:32 2014
@@ -130,8 +130,8 @@ struct SignalDesc {
struct SignalContext {
int int_signal_send;
- bool in_blocking_func;
- bool have_pending_signals;
+ atomic_uintptr_t in_blocking_func;
+ atomic_uintptr_t have_pending_signals;
SignalDesc pending_signals[kSigCount];
};
@@ -226,22 +226,30 @@ ScopedInterceptor::~ScopedInterceptor()
struct BlockingCall {
explicit BlockingCall(ThreadState *thr)
- : ctx(SigCtx(thr)) {
- ctx->in_blocking_func = true;
+ : thr(thr)
+ , ctx(SigCtx(thr)) {
+ for (;;) {
+ atomic_store(&ctx->in_blocking_func, 1, memory_order_relaxed);
+ if (atomic_load(&ctx->have_pending_signals, memory_order_relaxed) == 0)
+ break;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
+ ProcessPendingSignals(thr);
+ }
+ // When we are in a "blocking call", we process signals asynchronously
+ // (right when they arrive). In this context we do not expect to be
+ // executing any user/runtime code. The known interceptor sequence when
+ // this is not true is: pthread_join -> munmap(stack). It's fine
+ // to ignore munmap in this case -- we handle stack shadow separately.
+ thr->ignore_interceptors++;
}
~BlockingCall() {
- ctx->in_blocking_func = false;
+ thr->ignore_interceptors--;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
}
+ ThreadState *thr;
SignalContext *ctx;
-
- // When we are in a "blocking call", we process signals asynchronously
- // (right when they arrive). In this context we do not expect to be
- // executing any user/runtime code. The known interceptor sequence when
- // this is not true is: pthread_join -> munmap(stack). It's fine
- // to ignore munmap in this case -- we handle stack shadow separately.
- ScopedIgnoreInterceptors ignore_interceptors;
};
TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
@@ -392,7 +400,9 @@ static void SetJmp(ThreadState *thr, upt
buf->shadow_stack_pos = thr->shadow_stack_pos;
SignalContext *sctx = SigCtx(thr);
buf->int_signal_send = sctx ? sctx->int_signal_send : 0;
- buf->in_blocking_func = sctx ? sctx->in_blocking_func : false;
+ buf->in_blocking_func = sctx ?
+ atomic_load(&sctx->in_blocking_func, memory_order_relaxed) :
+ false;
buf->in_signal_handler = atomic_load(&thr->in_signal_handler,
memory_order_relaxed);
}
@@ -410,7 +420,8 @@ static void LongJmp(ThreadState *thr, up
SignalContext *sctx = SigCtx(thr);
if (sctx) {
sctx->int_signal_send = buf->int_signal_send;
- sctx->in_blocking_func = buf->in_blocking_func;
+ atomic_store(&sctx->in_blocking_func, buf->in_blocking_func,
+ memory_order_relaxed);
}
atomic_store(&thr->in_signal_handler, buf->in_signal_handler,
memory_order_relaxed);
@@ -1751,9 +1762,10 @@ static void CallUserSignalHandler(Thread
void ProcessPendingSignals(ThreadState *thr) {
SignalContext *sctx = SigCtx(thr);
- if (sctx == 0 || !sctx->have_pending_signals)
+ if (sctx == 0 ||
+ atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0)
return;
- sctx->have_pending_signals = false;
+ atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed);
atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
// These are too big for stack.
static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
@@ -1797,17 +1809,17 @@ void ALWAYS_INLINE rtl_generic_sighandle
// If we are in blocking function, we can safely process it now
// (but check if we are in a recursive interceptor,
// i.e. pthread_join()->munmap()).
- (sctx && sctx->in_blocking_func)) {
+ (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed))) {
atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
- if (sctx && sctx->in_blocking_func) {
+ if (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed)) {
// We ignore interceptors in blocking functions,
// temporary enbled them again while we are calling user function.
int const i = thr->ignore_interceptors;
thr->ignore_interceptors = 0;
- sctx->in_blocking_func = false;
+ atomic_store(&sctx->in_blocking_func, 0, memory_order_relaxed);
CallUserSignalHandler(thr, sync, true, sigact, sig, info, ctx);
thr->ignore_interceptors = i;
- sctx->in_blocking_func = true;
+ atomic_store(&sctx->in_blocking_func, 1, memory_order_relaxed);
} else {
// Be very conservative with when we do acquire in this case.
// It's unsafe to do acquire in async handlers, because ThreadState
@@ -1831,7 +1843,7 @@ void ALWAYS_INLINE rtl_generic_sighandle
internal_memcpy(&signal->siginfo, info, sizeof(*info));
if (ctx)
internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx));
- sctx->have_pending_signals = true;
+ atomic_store(&sctx->have_pending_signals, 1, memory_order_relaxed);
}
}
More information about the llvm-commits
mailing list