Index: compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp +++ compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp @@ -885,19 +885,21 @@ } namespace __tsan { -void DestroyThreadState() { - ThreadState *thr = cur_thread(); - Processor *proc = thr->proc(); - ThreadFinish(thr); - ProcUnwire(proc, thr); - ProcDestroy(proc); +void PlatformThreadFinished(ThreadState *thr) { ThreadSignalContext *sctx = thr->signal_ctx; if (sctx) { - thr->signal_ctx = 0; + thr->signal_ctx = nullptr; UnmapOrDie(sctx, sizeof(*sctx)); } - DTLS_Destroy(); - cur_thread_finalize(); + + if (thr->tctx->thread_type != ThreadType::Fiber) { + CHECK_EQ(thr, cur_thread()); + Processor *proc = thr->proc(); + ProcUnwire(proc, thr); + ProcDestroy(proc); + DTLS_Destroy(); + cur_thread_finalize(); + } } } // namespace __tsan @@ -912,7 +914,7 @@ } return; } - DestroyThreadState(); + ThreadFinish(cur_thread()); } #endif @@ -2551,7 +2553,7 @@ #if SANITIZER_NETBSD TSAN_INTERCEPTOR(void, _lwp_exit) { SCOPED_TSAN_INTERCEPTOR(_lwp_exit); - DestroyThreadState(); + ThreadFinish(cur_thread()); REAL(_lwp_exit)(); } #define TSAN_MAYBE_INTERCEPT__LWP_EXIT TSAN_INTERCEPT(_lwp_exit) @@ -2562,7 +2564,7 @@ #if SANITIZER_FREEBSD TSAN_INTERCEPTOR(void, thr_exit, tid_t *state) { SCOPED_TSAN_INTERCEPTOR(thr_exit, state); - DestroyThreadState(); + ThreadFinish(cur_thread()); REAL(thr_exit(state)); } #define TSAN_MAYBE_INTERCEPT_THR_EXIT TSAN_INTERCEPT(thr_exit) Index: compiler-rt/lib/tsan/rtl/tsan_platform.h =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_platform.h +++ compiler-rt/lib/tsan/rtl/tsan_platform.h @@ -1020,7 +1020,7 @@ void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg); -void DestroyThreadState(); +void PlatformThreadFinished(ThreadState *thr); } // namespace __tsan Index: compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp +++ compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp @@ -460,7 +460,7 @@ #if !SANITIZER_GO #if SANITIZER_ANDROID // On Android, one thread can call intercepted functions after -// DestroyThreadState(), so add a fake thread state for "dead" threads. +// ThreadFinish(), so add a fake thread state for "dead" threads. static ThreadState *dead_thread_state = nullptr; ThreadState *cur_thread() { Index: compiler-rt/lib/tsan/rtl/tsan_platform_mac.cpp =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_platform_mac.cpp +++ compiler-rt/lib/tsan/rtl/tsan_platform_mac.cpp @@ -222,7 +222,7 @@ if (thread == pthread_self()) { ThreadState *thr = cur_thread(); if (thr->tctx) { - DestroyThreadState(); + ThreadFinish(thr); } } } Index: compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp =================================================================== --- compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp +++ compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp @@ -283,6 +283,9 @@ DontNeedShadowFor(thr->tls_addr, thr->tls_size); thr->is_dead = true; ctx->thread_registry->FinishThread(thr->tid); +#if !SANITIZER_GO + PlatformThreadFinished(thr); +#endif } struct ConsumeThreadContext { Index: compiler-rt/test/tsan/fiber_cleanup.cpp =================================================================== --- /dev/null +++ compiler-rt/test/tsan/fiber_cleanup.cpp @@ -0,0 +1,67 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// REQUIRES: linux +#include "test.h" + +#include +#include +#include +#include +#include +#include + +long count_memory_mappings() { + pid_t my_pid = getpid(); + std::ifstream proc_file{"/proc/" + std::to_string(my_pid) + "/maps"}; + + std::string line; + long line_count{0}; + while (std::getline(proc_file, line)) { + line_count++; + } + + return line_count; +} + +void fiber_iteration() { + std::mutex mtx; + + void *orig_fiber = __tsan_get_current_fiber(); + void *fiber = __tsan_create_fiber(0); + + // Running some code on the fiber that triggers handling of pending signals. + __tsan_switch_to_fiber(fiber, 0); + mtx.lock(); + mtx.unlock(); + __tsan_switch_to_fiber(orig_fiber, 0); + + // We expect the fiber to clean up all resources (here the sigcontext) when destroyed. + __tsan_destroy_fiber(fiber); +} + +// Magic-Number for some warmup iterations, +// as tsan maps some memory for the first runs. +const size_t num_warmup = 100; + +int main() { + for (size_t i = 0; i < num_warmup; i++) { + fiber_iteration(); + } + + long memory_mappings_before = count_memory_mappings(); + fiber_iteration(); + fiber_iteration(); + long memory_mappings_after = count_memory_mappings(); + + // Is there a better way to detect a resource leak in the + // ThreadState object? (i.e. a mmap not being freed) + if (memory_mappings_before == memory_mappings_after) { + fprintf(stderr, "PASS\n"); + } else { + fprintf(stderr, "FAILED\n"); + } + + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: +// CHECK: PASS