From db784e00a24a67ecf550d848e69076d1f2bda38d Mon Sep 17 00:00:00 2001 From: "Antonio S. Musumeci" Date: Thu, 9 Apr 2026 23:42:02 -0400 Subject: [PATCH] Document detached-thread lifetime safety in autoscale self-exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After _try_remove_self_if_above detaches and erases the thread, the destructor's snapshot will not include it — so pthread_join is never called on it, and the ThreadPool object can be destroyed while this thread is still returning. This is safe because the thread only accesses local variables after detach (name is a std::string copy made at function entry), but the reasoning is subtle. Add an explicit safety comment explaining why there is no use-after-free. Co-Authored-By: Claude Opus 4.6 (1M context) --- vendored/libfuse/include/thread_pool.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vendored/libfuse/include/thread_pool.hpp b/vendored/libfuse/include/thread_pool.hpp index 9bdfef56..ab846120 100644 --- a/vendored/libfuse/include/thread_pool.hpp +++ b/vendored/libfuse/include/thread_pool.hpp @@ -416,8 +416,16 @@ ThreadPool::start_routine_autoscale(void *arg_) // Copy state needed for the log message BEFORE detaching, // because _try_remove_self_if_above detaches the thread and - // the destructor won't wait for us - btp may be destroyed + // the destructor won't wait for us — btp may be destroyed // by the time we reach the syslog call. + // + // Safety: after _try_remove_self_if_above returns true the + // thread is detached and removed from _threads. The + // destructor's snapshot won't include it, so pthread_join + // is never called. From this point we touch only locals + // (name is a std::string copy made at function entry), so + // there is no use-after-free even if ~ThreadPool runs + // concurrently on another thread. if(btp->_try_remove_self_if_above(pthread_self(), btp->_scaling_config.min_threads)) {