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:
parent
5839abb2f9
commit
d0fb5d8b00
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue