tsan: add pthread_tryjoin_np and pthread_timedjoin_np interceptors

Add pthread_tryjoin_np() and pthread_timedjoin_np() interceptors on Linux,
so that ThreadSanitizer can handle programs using these functions.

Author: Yuri Per (yuri)
Reviewed in: https://reviews.llvm.org/D54521

llvm-svn: 347383
This commit is contained in:
Dmitry Vyukov 2018-11-21 09:31:21 +00:00
parent 5839abb2f9
commit d0fb5d8b00
7 changed files with 132 additions and 0 deletions

View File

@ -338,4 +338,15 @@ ThreadContextBase *ThreadRegistry::QuarantinePop() {
return tctx;
}
void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {
BlockingMutexLock l(&mtx_);
CHECK_LT(tid, n_contexts_);
ThreadContextBase *tctx = threads_[tid];
CHECK_NE(tctx, 0);
CHECK_NE(tctx->status, ThreadStatusInvalid);
CHECK_NE(tctx->status, ThreadStatusDead);
CHECK_EQ(tctx->user_id, 0);
tctx->user_id = user_id;
}
} // namespace __sanitizer

View File

@ -122,6 +122,7 @@ class ThreadRegistry {
void JoinThread(u32 tid, void *arg);
void FinishThread(u32 tid);
void StartThread(u32 tid, tid_t os_id, bool workerthread, void *arg);
void SetThreadUserId(u32 tid, uptr user_id);
private:
const ThreadContextFactory context_factory_;

View File

@ -1056,6 +1056,35 @@ TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
return res;
}
#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
SCOPED_TSAN_INTERCEPTOR(pthread_tryjoin_np, th, ret);
int tid = ThreadTid(thr, pc, (uptr)th);
ThreadIgnoreBegin(thr, pc);
int res = REAL(pthread_tryjoin_np)(th, ret);
ThreadIgnoreEnd(thr, pc);
if (res == 0)
ThreadJoin(thr, pc, tid);
else
ThreadNotJoined(thr, pc, tid, (uptr)th);
return res;
}
TSAN_INTERCEPTOR(int, pthread_timedjoin_np, void *th, void **ret,
const struct timespec *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_timedjoin_np, th, ret, abstime);
int tid = ThreadTid(thr, pc, (uptr)th);
ThreadIgnoreBegin(thr, pc);
int res = BLOCK_REAL(pthread_timedjoin_np)(th, ret, abstime);
ThreadIgnoreEnd(thr, pc);
if (res == 0)
ThreadJoin(thr, pc, tid);
else
ThreadNotJoined(thr, pc, tid, (uptr)th);
return res;
}
#endif
// Problem:
// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
// pthread_cond_t has different size in the different versions.
@ -2652,6 +2681,10 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_create);
TSAN_INTERCEPT(pthread_join);
TSAN_INTERCEPT(pthread_detach);
#if SANITIZER_LINUX
TSAN_INTERCEPT(pthread_tryjoin_np);
TSAN_INTERCEPT(pthread_timedjoin_np);
#endif
TSAN_INTERCEPT_VER(pthread_cond_init, PTHREAD_ABI_BASE);
TSAN_INTERCEPT_VER(pthread_cond_signal, PTHREAD_ABI_BASE);

View File

@ -772,6 +772,7 @@ void ThreadFinalize(ThreadState *thr);
void ThreadSetName(ThreadState *thr, const char *name);
int ThreadCount(ThreadState *thr);
void ProcessPendingSignals(ThreadState *thr);
void ThreadNotJoined(ThreadState *thr, uptr pc, int tid, uptr uid);
Processor *ProcCreate();
void ProcDestroy(Processor *proc);

View File

@ -312,6 +312,12 @@ void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
ctx->thread_registry->DetachThread(tid, thr);
}
void ThreadNotJoined(ThreadState *thr, uptr pc, int tid, uptr uid) {
CHECK_GT(tid, 0);
CHECK_LT(tid, kMaxTid);
ctx->thread_registry->SetThreadUserId(tid, uid);
}
void ThreadSetName(ThreadState *thr, const char *name) {
ctx->thread_registry->SetThreadName(thr->tid, name);
}

View File

@ -0,0 +1,39 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
#define _GNU_SOURCE
#include "../test.h"
#include <errno.h>
int var;
void *Thread(void *x) {
barrier_wait(&barrier);
var = 1;
return 0;
}
static void check(int res, int expect) {
if (res != expect) {
fprintf(stderr, "Unexpected result of pthread_timedjoin_np: %d\n", res);
exit(1);
}
}
int main() {
barrier_init(&barrier, 2);
pthread_t t;
pthread_create(&t, 0, Thread, 0);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
check(pthread_timedjoin_np(t, 0, &ts), ETIMEDOUT);
barrier_wait(&barrier);
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 10000;
check(pthread_timedjoin_np(t, 0, &ts), 0);
var = 2;
fprintf(stderr, "PASS\n");
return 0;
}
// CHECK-NOT: WARNING: ThreadSanitizer: data race
// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
// CHECK: PASS

View File

@ -0,0 +1,41 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
#define _GNU_SOURCE
#include "../test.h"
#include <errno.h>
int var;
void *Thread(void *x) {
barrier_wait(&barrier);
var = 1;
return 0;
}
static void check(int res) {
if (res != EBUSY) {
fprintf(stderr, "Unexpected result of pthread_tryjoin_np: %d\n", res);
exit(1);
}
}
int main() {
barrier_init(&barrier, 2);
pthread_t t;
pthread_create(&t, 0, Thread, 0);
check(pthread_tryjoin_np(t, 0));
barrier_wait(&barrier);
for (;;) {
int res = pthread_tryjoin_np(t, 0);
if (!res)
break;
check(res);
pthread_yield();
}
var = 2;
fprintf(stderr, "PASS\n");
return 0;
}
// CHECK-NOT: WARNING: ThreadSanitizer: data race
// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
// CHECK: PASS