Index: lib/sanitizer_common/sanitizer_thread_registry.h =================================================================== --- lib/sanitizer_common/sanitizer_thread_registry.h +++ lib/sanitizer_common/sanitizer_thread_registry.h @@ -122,6 +122,7 @@ 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_; Index: lib/sanitizer_common/sanitizer_thread_registry.cc =================================================================== --- lib/sanitizer_common/sanitizer_thread_registry.cc +++ lib/sanitizer_common/sanitizer_thread_registry.cc @@ -338,4 +338,15 @@ 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 Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -1044,6 +1044,35 @@ 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. @@ -2640,6 +2669,10 @@ 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); Index: lib/tsan/rtl/tsan_rtl.h =================================================================== --- lib/tsan/rtl/tsan_rtl.h +++ lib/tsan/rtl/tsan_rtl.h @@ -772,6 +772,7 @@ 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); Index: lib/tsan/rtl/tsan_rtl_thread.cc =================================================================== --- lib/tsan/rtl/tsan_rtl_thread.cc +++ lib/tsan/rtl/tsan_rtl_thread.cc @@ -312,6 +312,12 @@ 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); } Index: test/tsan/Linux/thread_timedjoin.c =================================================================== --- /dev/null +++ test/tsan/Linux/thread_timedjoin.c @@ -0,0 +1,39 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#define _GNU_SOURCE +#include "../test.h" +#include + +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 Index: test/tsan/Linux/thread_tryjoin.c =================================================================== --- /dev/null +++ test/tsan/Linux/thread_tryjoin.c @@ -0,0 +1,41 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#define _GNU_SOURCE +#include "../test.h" +#include + +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