From 4d35726fc6dc3eb870eadbb0d3efd5e4df81259b Mon Sep 17 00:00:00 2001 From: "Antonio S. Musumeci" Date: Fri, 10 Apr 2026 12:40:43 -0400 Subject: [PATCH] Assert try_enqueue does not move-from callable on failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit enqueue_work_autoscale relies on BoundedQueue::try_enqueue leaving the callable untouched when the semaphore check fails. This is correct today because tryWait() is evaluated before std::forward moves the item, but the coupling is implicit — a future BoundedQueue change that reorders these operations would silently introduce a use-after- move bug on the blocking fallback path. Add a debug assert after the failed try_enqueue to verify the Func is still valid before using it on the blocking path. Strengthen the comment to explicitly name the invariant being relied upon. Co-Authored-By: Claude Opus 4.6 (1M context) --- vendored/libfuse/include/thread_pool.hpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/vendored/libfuse/include/thread_pool.hpp b/vendored/libfuse/include/thread_pool.hpp index 1a7ce704..4b8aba02 100644 --- a/vendored/libfuse/include/thread_pool.hpp +++ b/vendored/libfuse/include/thread_pool.hpp @@ -24,6 +24,7 @@ #include "mutex.hpp" #include +#include #include #include #include @@ -955,8 +956,11 @@ ThreadPool::enqueue_work_autoscale(ThreadPool::PToken &ptok_, // Materialize into a Func up front so we own the value across // the try/block-enqueue boundary without a double std::forward. // - // Safe: BoundedQueue::try_enqueue checks the semaphore before - // forwarding, so f is not moved-from on failure. + // INVARIANT: BoundedQueue::try_enqueue checks the semaphore + // (tryWait) *before* forwarding the item into the inner queue. + // On failure the item is never moved-from, so f remains valid. + // The assert below guards against future BoundedQueue changes + // that could violate this ordering. Func f(std::forward(func_)); if(_queue.try_enqueue(std::move(f))) @@ -965,6 +969,9 @@ ThreadPool::enqueue_work_autoscale(ThreadPool::PToken &ptok_, return; } + assert(static_cast(f) && + "BoundedQueue::try_enqueue moved the callable despite returning false"); + _maybe_grow_on_pressure(); // Block-enqueue (will succeed once a worker frees a slot) @@ -978,7 +985,7 @@ inline void ThreadPool::enqueue_work_autoscale(FuncType &&func_) { - // See ptok_ overload for safety note on try_enqueue + move. + // See ptok_ overload for invariant and safety note. Func f(std::forward(func_)); if(_queue.try_enqueue(std::move(f))) @@ -987,6 +994,9 @@ ThreadPool::enqueue_work_autoscale(FuncType &&func_) return; } + assert(static_cast(f) && + "BoundedQueue::try_enqueue moved the callable despite returning false"); + _maybe_grow_on_pressure(); // Block-enqueue (will succeed once a worker frees a slot)