From 3885c19481fdff1c905e988d5d4cf7a546af4902 Mon Sep 17 00:00:00 2001 From: medjed02 Date: Tue, 10 Nov 2026 19:16:19 +0300 Subject: [PATCH] Stop the pool optimizer in stopWorkers() stopWorkers() stops every background worker except the pool optimizer. shutDown() relies on it before saving the cache state, so with the pool optimizer enabled the optimizer thread can still resize pools while the state is being serialized; the destructor has the same window until the PoolOptimizer member itself is destroyed. Test: a counting PoolOptimizeStrategy is no longer called after stopWorkers() returns. --- cachelib/allocator/CacheAllocator.h | 1 + .../tests/SimplePoolOptimizationTest.cpp | 4 +++ .../tests/SimplePoolOptimizationTest.h | 31 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/cachelib/allocator/CacheAllocator.h b/cachelib/allocator/CacheAllocator.h index b485a5fbab..4e98b3e613 100644 --- a/cachelib/allocator/CacheAllocator.h +++ b/cachelib/allocator/CacheAllocator.h @@ -6359,6 +6359,7 @@ folly::IOBufQueue CacheAllocator::saveStateToIOBuf() { template bool CacheAllocator::stopWorkers(std::chrono::seconds timeout) { bool success = true; + success &= stopPoolOptimizer(timeout); success &= stopPoolRebalancer(timeout); success &= stopPoolResizer(timeout); success &= stopMemMonitor(timeout); diff --git a/cachelib/allocator/tests/SimplePoolOptimizationTest.cpp b/cachelib/allocator/tests/SimplePoolOptimizationTest.cpp index 63b567f0ed..e01c3d59c4 100644 --- a/cachelib/allocator/tests/SimplePoolOptimizationTest.cpp +++ b/cachelib/allocator/tests/SimplePoolOptimizationTest.cpp @@ -29,6 +29,10 @@ TYPED_TEST(SimplePoolOptimizationTest, PoolOptimizerBasic) { this->testPoolOptimizerBasic(); } +TYPED_TEST(SimplePoolOptimizationTest, StopWorkersStopsPoolOptimizer) { + this->testStopWorkersStopsPoolOptimizer(); +} + } // namespace tests } // namespace cachelib } // namespace facebook diff --git a/cachelib/allocator/tests/SimplePoolOptimizationTest.h b/cachelib/allocator/tests/SimplePoolOptimizationTest.h index 2313ef4f6e..13c77dd851 100644 --- a/cachelib/allocator/tests/SimplePoolOptimizationTest.h +++ b/cachelib/allocator/tests/SimplePoolOptimizationTest.h @@ -19,7 +19,10 @@ #include #include +#include +#include #include +#include #include "cachelib/allocator/CCacheAllocator.h" #include "cachelib/allocator/PoolOptimizer.h" @@ -85,6 +88,34 @@ struct SimplePoolOptimizeStrategy : public PoolOptimizeStrategy { template class SimplePoolOptimizationTest : public testing::Test { public: + void testStopWorkersStopsPoolOptimizer() { + struct CountingStrategy : public PoolOptimizeStrategy { + std::atomic calls{0}; + + protected: + PoolOptimizeContext pickVictimAndReceiverRegularPoolsImpl( + const CacheBase&) override { + ++calls; + return kNoOpContext; + } + }; + auto strategy = std::make_shared(); + typename AllocatorT::Config config; + config.setCacheSize(30 * Slab::kSize); + config.enablePoolOptimizer(strategy, std::chrono::seconds{1}, + std::chrono::seconds{1}, 1); + AllocatorT alloc(config); + for (int i = 0; i < 100 && strategy->calls == 0; ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds{100}); + } + ASSERT_GT(strategy->calls.load(), 0); + + ASSERT_TRUE(alloc.stopWorkers()); + const int calls = strategy->calls.load(); + std::this_thread::sleep_for(std::chrono::seconds{2}); + EXPECT_EQ(calls, strategy->calls.load()); + } + void testPoolOptimizerBasic() { typename AllocatorT::Config config; config.setCacheSize(30 * Slab::kSize);