[compiler-rt] r234394 - tsan: handle async signals while blocked in pthread_cond_wait

Dmitry Vyukov dvyukov at google.com
Wed Apr 8 00:48:52 PDT 2015


Author: dvyukov
Date: Wed Apr  8 02:48:52 2015
New Revision: 234394

URL: http://llvm.org/viewvc/llvm-project?rev=234394&view=rev
Log:
tsan: handle async signals while blocked in pthread_cond_wait

Fixes https://code.google.com/p/thread-sanitizer/issues/detail?id=91


Added:
    compiler-rt/trunk/test/tsan/signal_cond.cc
Modified:
    compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc
    compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc
    compiler-rt/trunk/test/tsan/cond_cancel.c

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=234394&r1=234393&r2=234394&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc Wed Apr  8 02:48:52 2015
@@ -1056,13 +1056,25 @@ static void *init_cond(void *c, bool for
 }
 
 struct CondMutexUnlockCtx {
+  ScopedInterceptor *si;
   ThreadState *thr;
   uptr pc;
   void *m;
 };
 
 static void cond_mutex_unlock(CondMutexUnlockCtx *arg) {
+  // pthread_cond_wait interceptor has enabled async signal delivery
+  // (see BlockingCall below). Disable async signals since we are running
+  // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run
+  // since the thread is cancelled, so we have to manually execute them
+  // (the thread still can run some user code due to pthread_cleanup_push).
+  ThreadSignalContext *ctx = SigCtx(arg->thr);
+  CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1);
+  atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
   MutexLock(arg->thr, arg->pc, (uptr)arg->m);
+  // Undo BlockingCall ctor effects.
+  arg->thr->ignore_interceptors--;
+  arg->si->~ScopedInterceptor();
 }
 
 INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
@@ -1077,12 +1089,17 @@ INTERCEPTOR(int, pthread_cond_wait, void
   SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m);
   MutexUnlock(thr, pc, (uptr)m);
   MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
-  CondMutexUnlockCtx arg = {thr, pc, m};
+  CondMutexUnlockCtx arg = {&si, thr, pc, m};
+  int res = 0;
   // This ensures that we handle mutex lock even in case of pthread_cancel.
   // See test/tsan/cond_cancel.cc.
-  int res = call_pthread_cancel_with_cleanup(
-      (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait),
-      cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg);
+  {
+    // Enable signal delivery while the thread is blocked.
+    BlockingCall bc(thr);
+    res = call_pthread_cancel_with_cleanup(
+        (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait),
+        cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg);
+  }
   if (res == errno_EOWNERDEAD)
     MutexRepair(thr, pc, (uptr)m);
   MutexLock(thr, pc, (uptr)m);
@@ -1094,12 +1111,16 @@ INTERCEPTOR(int, pthread_cond_timedwait,
   SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime);
   MutexUnlock(thr, pc, (uptr)m);
   MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
-  CondMutexUnlockCtx arg = {thr, pc, m};
+  CondMutexUnlockCtx arg = {&si, thr, pc, m};
+  int res = 0;
   // This ensures that we handle mutex lock even in case of pthread_cancel.
   // See test/tsan/cond_cancel.cc.
-  int res = call_pthread_cancel_with_cleanup(
-      REAL(pthread_cond_timedwait), cond, m, abstime,
-      (void(*)(void *arg))cond_mutex_unlock, &arg);
+  {
+    BlockingCall bc(thr);
+    res = call_pthread_cancel_with_cleanup(
+        REAL(pthread_cond_timedwait), cond, m, abstime,
+        (void(*)(void *arg))cond_mutex_unlock, &arg);
+  }
   if (res == errno_EOWNERDEAD)
     MutexRepair(thr, pc, (uptr)m);
   MutexLock(thr, pc, (uptr)m);

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc?rev=234394&r1=234393&r2=234394&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_platform_linux.cc Wed Apr  8 02:48:52 2015
@@ -394,6 +394,8 @@ int ExtractRecvmsgFDs(void *msgp, int *f
   return res;
 }
 
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
 int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
     void *abstime), void *c, void *m, void *abstime,
     void(*cleanup)(void *arg), void *arg) {

Modified: compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc?rev=234394&r1=234393&r2=234394&view=diff
==============================================================================
--- compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc (original)
+++ compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc Wed Apr  8 02:48:52 2015
@@ -76,6 +76,8 @@ void InitializePlatform() {
 }
 
 #ifndef SANITIZER_GO
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
 int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
     void *abstime), void *c, void *m, void *abstime,
     void(*cleanup)(void *arg), void *arg) {

Modified: compiler-rt/trunk/test/tsan/cond_cancel.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/cond_cancel.c?rev=234394&r1=234393&r2=234394&view=diff
==============================================================================
--- compiler-rt/trunk/test/tsan/cond_cancel.c (original)
+++ compiler-rt/trunk/test/tsan/cond_cancel.c Wed Apr  8 02:48:52 2015
@@ -8,9 +8,14 @@ pthread_mutex_t m;
 pthread_cond_t c;
 int x;
 
+static void my_cleanup(void *arg) {
+  printf("my_cleanup\n");
+  pthread_mutex_unlock((pthread_mutex_t*)arg);
+}
+
 void *thr1(void *p) {
   pthread_mutex_lock(&m);
-  pthread_cleanup_push((void(*)(void *arg))pthread_mutex_unlock, &m);
+  pthread_cleanup_push(my_cleanup, &m);
   barrier_wait(&barrier);
   while (x == 0)
     pthread_cond_wait(&c, &m);

Added: compiler-rt/trunk/test/tsan/signal_cond.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/signal_cond.cc?rev=234394&view=auto
==============================================================================
--- compiler-rt/trunk/test/tsan/signal_cond.cc (added)
+++ compiler-rt/trunk/test/tsan/signal_cond.cc Wed Apr  8 02:48:52 2015
@@ -0,0 +1,51 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include "test.h"
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <semaphore.h>
+
+// Test that signals can be delivered to blocked pthread_cond_wait.
+// https://code.google.com/p/thread-sanitizer/issues/detail?id=91
+
+int g_thread_run = 1;
+pthread_mutex_t mutex;
+pthread_cond_t cond;
+sem_t sem;
+
+void sig_handler(int sig) {
+  (void)sig;
+  write(1, "SIGNAL\n", sizeof("SIGNAL\n") - 1);
+  sem_post(&sem);
+}
+
+void* my_thread(void* arg) {
+  pthread_mutex_lock(&mutex);
+  while (g_thread_run)
+    pthread_cond_wait(&cond, &mutex);
+  pthread_mutex_unlock(&mutex);
+  return 0;
+}
+
+int main() {
+  sem_init(&sem, 0, 0);
+  signal(SIGUSR1, &sig_handler);
+  pthread_t thr;
+  pthread_create(&thr, 0, &my_thread, 0);
+  // wait for thread to get inside pthread_cond_wait
+  // (can't use barrier_wait for that)
+  sleep(1);
+  pthread_kill(thr, SIGUSR1);
+  while (sem_wait(&sem) == -1 && errno == EINTR) {
+  }
+  pthread_mutex_lock(&mutex);
+  g_thread_run = 0;
+  pthread_cond_signal(&cond);
+  pthread_mutex_unlock(&mutex);
+  pthread_join(thr, 0);
+  fprintf(stderr, "DONE\n");
+  return 0;
+}
+
+// CHECK: SIGNAL
+// CHECK: DONE





More information about the llvm-commits mailing list