mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-25 18:40:18 -04:00
Re-check cooldown under lock in _maybe_grow_on_pressure
The pre-lock cooldown check used a stale _last_scale_time_usecs read. If another thread (monitor hill-climb, concurrent enqueue) updated the timestamp between the pre-lock check and the lock acquisition, the function would proceed to acquire _scaling_mutex and contend unnecessarily. The CAS that followed would correctly reject the stale value, so this was not a correctness bug, but it caused avoidable lock contention on the hot enqueue path. Add an authoritative cooldown re-check after acquiring _scaling_mutex with a fresh timestamp read. Replace the CAS with a plain store since the _scaling_mutex already serializes writers. Keep the pre-lock speculative check as a fast-path filter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
11fafba6ae
commit
4b5b102e6f
1 changed files with 14 additions and 11 deletions
|
|
@ -669,16 +669,24 @@ ThreadPool::_maybe_grow_on_pressure()
|
|||
if(!_autoscale_enabled)
|
||||
return;
|
||||
|
||||
std::uint64_t now = _now_usecs();
|
||||
std::uint64_t last = _last_scale_time_usecs.load(std::memory_order_relaxed);
|
||||
|
||||
if((now - last) < (std::uint64_t)_scaling_config.cooldown_usecs)
|
||||
return;
|
||||
// Quick check before contending on the lock. Uses a stale read
|
||||
// but the authoritative re-check happens under the lock below.
|
||||
{
|
||||
std::uint64_t now = _now_usecs();
|
||||
std::uint64_t last = _last_scale_time_usecs.load(std::memory_order_relaxed);
|
||||
if((now - last) < (std::uint64_t)_scaling_config.cooldown_usecs)
|
||||
return;
|
||||
}
|
||||
|
||||
// Hold _scaling_mutex so the thread-count check and add are atomic
|
||||
// with respect to other scaling operations.
|
||||
mutex_lockguard(_scaling_mutex);
|
||||
|
||||
std::uint64_t now = _now_usecs();
|
||||
std::uint64_t last = _last_scale_time_usecs.load(std::memory_order_relaxed);
|
||||
if((now - last) < (std::uint64_t)_scaling_config.cooldown_usecs)
|
||||
return;
|
||||
|
||||
std::size_t count;
|
||||
{
|
||||
mutex_lockguard(_threads_mutex);
|
||||
|
|
@ -688,12 +696,7 @@ ThreadPool::_maybe_grow_on_pressure()
|
|||
if(count >= _scaling_config.max_threads)
|
||||
return;
|
||||
|
||||
// CAS to claim the cooldown window — only one caller wins.
|
||||
// Done after the max-threads check so we don't burn the cooldown
|
||||
// when the pool is already at capacity.
|
||||
if(!_last_scale_time_usecs.compare_exchange_strong(last,now,
|
||||
std::memory_order_relaxed))
|
||||
return;
|
||||
_last_scale_time_usecs.store(now,std::memory_order_relaxed);
|
||||
|
||||
_add_thread_scaling_locked({});
|
||||
syslog(LOG_DEBUG,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue