diff --git a/include/envoy/thread_local/thread_local.h b/include/envoy/thread_local/thread_local.h index 8ff2ca99c8c00..f3d846ff5cca5 100644 --- a/include/envoy/thread_local/thread_local.h +++ b/include/envoy/thread_local/thread_local.h @@ -226,6 +226,13 @@ class Instance : public SlotAllocator { * @return Event::Dispatcher& the thread local dispatcher. */ virtual Event::Dispatcher& dispatcher() PURE; + + /** + * Returns whether or not global threading has been shutdown. + * + * @return true if global threading has been shutdown or false if not. + */ + virtual bool isShutdown() const PURE; }; } // namespace ThreadLocal diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index 46aed025cc236..78dda479cf821 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -43,6 +43,9 @@ ThreadLocalStoreImpl::~ThreadLocalStoreImpl() { ASSERT(shutting_down_ || !threading_ever_initialized_); default_scope_.reset(); ASSERT(scopes_.empty()); + ASSERT(scopes_to_cleanup_.empty()); + ASSERT(central_cache_entries_to_cleanup_.empty()); + ASSERT(histograms_to_cleanup_.empty()); } void ThreadLocalStoreImpl::setHistogramSettings(HistogramSettingsConstPtr&& histogram_settings) { @@ -194,11 +197,23 @@ void ThreadLocalStoreImpl::initializeThreading(Event::Dispatcher& main_thread_di tls_cache_ = ThreadLocal::TypedSlot::makeUnique(tls); tls_cache_->set( [](Event::Dispatcher&) -> std::shared_ptr { return std::make_shared(); }); + tls_ = tls; } void ThreadLocalStoreImpl::shutdownThreading() { // This will block both future cache fills as well as cache flushes. shutting_down_ = true; + ASSERT(!tls_.has_value() || tls_->isShutdown()); + + // We can't call runOnAllThreads here as global threading has already been shutdown. It is okay + // to simply clear the scopes and central cache entries here as they will be cleaned up during + // thread local data cleanup in InstanceImpl::shutdownThread(). + { + Thread::LockGuard lock(lock_); + scopes_to_cleanup_.clear(); + central_cache_entries_to_cleanup_.clear(); + } + Thread::LockGuard lock(hist_mutex_); for (ParentHistogramImpl* histogram : histogram_set_) { histogram->setShuttingDown(true); @@ -261,22 +276,29 @@ void ThreadLocalStoreImpl::releaseScopeCrossThread(ScopeImpl* scope) { // // Since this is called from ScopeImpl's destructor, we must bump the // ref-count of the central-cache by copying to a local scoped pointer, and - // keep that reference alive until all the TLS caches are clear. - CentralCacheEntrySharedPtr central_cache = scope->central_cache_; + // keep that reference alive until all the TLS caches are clear. This is done by keeping a + // separate vector of shared_ptrs which will be destructed once all threads have completed. // This can happen from any thread. We post() back to the main thread which will initiate the // cache flush operation. if (!shutting_down_ && main_thread_dispatcher_) { - const uint64_t scope_id = scope->scope_id_; + // Clear scopes in a batch. It's possible that many different scopes will be deleted at + // the same time, before the main thread gets a chance to run cleanScopesFromCaches. If a new + // scope is deleted before that post runs, we add it to our list of scopes to clear, and there + // is no need to issue another post. This greatly reduces the overhead when there are tens of + // thousands of scopes to clear in a short period. i.e.: VHDS updates with tens of thousands of + // VirtualHosts. + bool need_post = scopes_to_cleanup_.empty(); + scopes_to_cleanup_.push_back(scope->scope_id_); + central_cache_entries_to_cleanup_.push_back(scope->central_cache_); lock.release(); - // TODO(jmarantz): consider batching all the scope IDs that should be - // cleared from TLS caches to reduce bursts of runOnAllThreads on a large - // config update. See the pattern below used for histograms. - main_thread_dispatcher_->post([this, central_cache, scope_id]() { - sync_.syncPoint(MainDispatcherCleanupSync); - clearScopeFromCaches(scope_id, central_cache); - }); + if (need_post) { + main_thread_dispatcher_->post([this]() { + sync_.syncPoint(MainDispatcherCleanupSync); + clearScopesFromCaches(); + }); + } } } @@ -284,8 +306,20 @@ void ThreadLocalStoreImpl::releaseHistogramCrossThread(uint64_t histogram_id) { // This can happen from any thread. We post() back to the main thread which will initiate the // cache flush operation. if (!shutting_down_ && main_thread_dispatcher_) { - main_thread_dispatcher_->post( - [this, histogram_id]() { clearHistogramFromCaches(histogram_id); }); + // It's possible that many different histograms will be deleted at the same + // time, before the main thread gets a chance to run + // clearHistogramsFromCaches. If a new histogram is deleted before that + // post runs, we add it to our list of histograms to clear, and there's no + // need to issue another post. + bool need_post = false; + { + Thread::LockGuard lock(hist_mutex_); + need_post = histograms_to_cleanup_.empty(); + histograms_to_cleanup_.push_back(histogram_id); + } + if (need_post) { + main_thread_dispatcher_->post([this]() { clearHistogramsFromCaches(); }); + } } } @@ -294,39 +328,62 @@ ThreadLocalStoreImpl::TlsCache::insertScope(uint64_t scope_id) { return scope_cache_[scope_id]; } -void ThreadLocalStoreImpl::TlsCache::eraseScope(uint64_t scope_id) { scope_cache_.erase(scope_id); } -void ThreadLocalStoreImpl::TlsCache::eraseHistogram(uint64_t histogram_id) { +void ThreadLocalStoreImpl::TlsCache::eraseScopes(const std::vector& scope_ids) { + for (uint64_t scope_id : scope_ids) { + scope_cache_.erase(scope_id); + } +} + +void ThreadLocalStoreImpl::TlsCache::eraseHistograms(const std::vector& histograms) { // This is called for every histogram in every thread, even though the // histogram may not have been cached in each thread yet. So we don't // want to check whether the erase() call erased anything. - tls_histogram_cache_.erase(histogram_id); + for (uint64_t histogram_id : histograms) { + tls_histogram_cache_.erase(histogram_id); + } } -void ThreadLocalStoreImpl::clearScopeFromCaches(uint64_t scope_id, - CentralCacheEntrySharedPtr central_cache) { +void ThreadLocalStoreImpl::clearScopesFromCaches() { // If we are shutting down we no longer perform cache flushes as workers may be shutting down // at the same time. if (!shutting_down_) { // Perform a cache flush on all threads. + + // Capture all the pending scope ids in a local, clearing the list held in + // this. Once this occurs, if a new scope is deleted, a new post will be + // required. + auto scope_ids = std::make_shared>(); + // Capture all the central cache entries for scopes we're deleting. These will be freed after + // all threads have completed. + auto central_caches = std::make_shared>(); + { + Thread::LockGuard lock(lock_); + *scope_ids = std::move(scopes_to_cleanup_); + scopes_to_cleanup_.clear(); + *central_caches = std::move(central_cache_entries_to_cleanup_); + central_cache_entries_to_cleanup_.clear(); + } + tls_cache_->runOnAllThreads( - [scope_id](OptRef tls_cache) { tls_cache->eraseScope(scope_id); }, - [central_cache]() { /* Holds onto central_cache until all tls caches are clear */ }); + [scope_ids](OptRef tls_cache) { tls_cache->eraseScopes(*scope_ids); }, + [central_caches]() { /* Holds onto central_caches until all tls caches are clear */ }); } } -void ThreadLocalStoreImpl::clearHistogramFromCaches(uint64_t histogram_id) { +void ThreadLocalStoreImpl::clearHistogramsFromCaches() { // If we are shutting down we no longer perform cache flushes as workers may be shutting down // at the same time. if (!shutting_down_) { - // Perform a cache flush on all threads. - // - // TODO(jmarantz): If this cross-thread posting proves to be a performance - // bottleneck, - // https://gist.github.com/jmarantz/838cb6de7e74c0970ea6b63eded0139a - // contains a patch that will implement batching together to clear multiple - // histograms. + // Move the histograms pending cleanup into a local variable. Future histogram deletions will be + // batched until the next time this function is called. + auto histograms = std::make_shared>(); + { + Thread::LockGuard lock(hist_mutex_); + histograms->swap(histograms_to_cleanup_); + } + tls_cache_->runOnAllThreads( - [histogram_id](OptRef tls_cache) { tls_cache->eraseHistogram(histogram_id); }); + [histograms](OptRef tls_cache) { tls_cache->eraseHistograms(*histograms); }); } } diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 946a84ed874cf..bc76c13c9aa92 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -445,8 +445,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo struct TlsCache : public ThreadLocal::ThreadLocalObject { TlsCacheEntry& insertScope(uint64_t scope_id); - void eraseScope(uint64_t scope_id); - void eraseHistogram(uint64_t histogram); + void eraseScopes(const std::vector& scope_ids); + void eraseHistograms(const std::vector& histograms); // The TLS scope cache is keyed by scope ID. This is used to avoid complex circular references // during scope destruction. An ID is required vs. using the address of the scope pointer @@ -472,8 +472,8 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo } std::string getTagsForName(const std::string& name, TagVector& tags) const; - void clearScopeFromCaches(uint64_t scope_id, CentralCacheEntrySharedPtr central_cache); - void clearHistogramFromCaches(uint64_t histogram_id); + void clearScopesFromCaches(); + void clearHistogramsFromCaches(); void releaseScopeCrossThread(ScopeImpl* scope); void mergeInternal(PostMergeCb merge_cb); bool rejects(StatName name) const; @@ -499,6 +499,7 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo std::atomic shutting_down_{}; std::atomic merge_in_progress_{}; AllocatorImpl heap_allocator_; + OptRef tls_; NullCounterImpl null_counter_; NullGaugeImpl null_gauge_; @@ -526,6 +527,19 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo std::vector deleted_gauges_ ABSL_GUARDED_BY(lock_); std::vector deleted_histograms_ ABSL_GUARDED_BY(lock_); std::vector deleted_text_readouts_ ABSL_GUARDED_BY(lock_); + + // Scope IDs and central cache entries that are queued for cross-scope release. + // Because there can be a large number of scopes, all of which are released at once, + // (e.g. when a scope is deleted), it is more efficient to batch their cleanup, + // which would otherwise entail a post() per scope per thread. + std::vector scopes_to_cleanup_ ABSL_GUARDED_BY(lock_); + std::vector central_cache_entries_to_cleanup_ ABSL_GUARDED_BY(lock_); + + // Histograms IDs that are queued for cross-scope release. Because there + // can be a large number of histograms, all of which are released at once, + // (e.g. when a scope is deleted), it is likely more efficient to batch their + // cleanup, which would otherwise entail a post() per histogram per thread. + std::vector histograms_to_cleanup_ ABSL_GUARDED_BY(hist_mutex_); }; using ThreadLocalStoreImplPtr = std::unique_ptr; diff --git a/source/common/thread_local/thread_local_impl.h b/source/common/thread_local/thread_local_impl.h index db9cd63e10230..c9df63b1e0b85 100644 --- a/source/common/thread_local/thread_local_impl.h +++ b/source/common/thread_local/thread_local_impl.h @@ -28,6 +28,7 @@ class InstanceImpl : Logger::Loggable, public NonCopyable, pub void shutdownGlobalThreading() override; void shutdownThread() override; Event::Dispatcher& dispatcher() override; + bool isShutdown() const override { return shutdown_; } private: // On destruction returns the slot index to the deferred delete queue (detaches it). This allows diff --git a/test/common/stats/thread_local_store_speed_test.cc b/test/common/stats/thread_local_store_speed_test.cc index 5208fdaed9f5c..20aa7f503a29a 100644 --- a/test/common/stats/thread_local_store_speed_test.cc +++ b/test/common/stats/thread_local_store_speed_test.cc @@ -29,17 +29,16 @@ class ThreadLocalStorePerf { store_.setTagProducer(std::make_unique(stats_config_)); Stats::TestUtil::forEachSampleStat(1000, [this](absl::string_view name) { - stat_names_.push_back(std::make_unique(name, symbol_table_)); + stat_names_.push_back(std::make_unique(name, symbol_table_)); }); } ~ThreadLocalStorePerf() { - for (auto& stat_name_storage : stat_names_) { - stat_name_storage->free(symbol_table_); + if (tls_) { + tls_->shutdownGlobalThreading(); } store_.shutdownThreading(); if (tls_) { - tls_->shutdownGlobalThreading(); tls_->shutdownThread(); } if (dispatcher_) { @@ -72,7 +71,7 @@ class ThreadLocalStorePerf { Stats::ThreadLocalStoreImpl store_; Api::ApiPtr api_; envoy::config::metrics::v3::StatsConfig stats_config_; - std::vector> stat_names_; + std::vector> stat_names_; }; } // namespace Envoy diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 6d8de94d0463d..8043ebc1e4b78 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -69,6 +69,8 @@ class StatsThreadLocalStoreTest : public testing::Test { store_->addSink(sink_); } + ~StatsThreadLocalStoreTest() override { tls_.shutdownGlobalThreading(); } + void resetStoreWithAlloc(Allocator& alloc) { store_ = std::make_unique(alloc); store_->addSink(sink_); @@ -128,6 +130,7 @@ class HistogramTest : public testing::Test { } void TearDown() override { + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -318,6 +321,7 @@ TEST_F(StatsThreadLocalStoreTest, Tls) { EXPECT_EQ(&t1, store_->textReadouts().front().get()); // front() ok when size()==1 EXPECT_EQ(2UL, store_->textReadouts().front().use_count()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); @@ -415,6 +419,7 @@ TEST_F(StatsThreadLocalStoreTest, BasicScope) { Stats::Histogram::Unit::Unspecified)); } + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); scope1->deliverHistogramToSinks(h1, 100); scope1->deliverHistogramToSinks(h2, 200); @@ -460,6 +465,7 @@ TEST_F(StatsThreadLocalStoreTest, HistogramScopeOverlap) { EXPECT_EQ(0, store_->histograms().size()); EXPECT_EQ(0, numTlsHistograms()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); store_->histogramFromString("histogram_after_shutdown", Histogram::Unit::Unspecified); @@ -476,6 +482,7 @@ TEST_F(StatsThreadLocalStoreTest, SanitizePrefix) { Counter& c1 = scope1->counterFromString("c1"); EXPECT_EQ("scope1___foo.c1", c1.name()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -499,13 +506,14 @@ TEST_F(StatsThreadLocalStoreTest, ScopeDelete) { EXPECT_EQ("scope1.c1", c1->name()); EXPECT_CALL(main_thread_dispatcher_, post(_)); - EXPECT_CALL(tls_, runOnAllThreads(_, _)); + EXPECT_CALL(tls_, runOnAllThreads(_, _)).Times(testing::AtLeast(1)); scope1.reset(); EXPECT_EQ(0UL, store_->counters().size()); EXPECT_EQ(1L, c1.use_count()); c1.reset(); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -541,6 +549,7 @@ TEST_F(StatsThreadLocalStoreTest, NestedScopes) { TextReadout& t1 = scope2->textReadoutFromString("some_string"); EXPECT_EQ("scope1.foo.some_string", t1.name()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -605,6 +614,7 @@ TEST_F(StatsThreadLocalStoreTest, OverlappingScopes) { EXPECT_EQ("abc", t2.value()); EXPECT_EQ(1UL, store_->textReadouts().size()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -650,6 +660,7 @@ TEST_F(StatsThreadLocalStoreTest, TextReadoutAllLengths) { t.set(""); EXPECT_EQ("", t.value()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -805,6 +816,7 @@ TEST_F(StatsMatcherTLSTest, TestNoOpStatImpls) { store_->histogramFromString("noop_histogram_2", Stats::Histogram::Unit::Unspecified); EXPECT_EQ(&noop_histogram, &noop_histogram_2); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); } @@ -919,6 +931,7 @@ TEST_F(StatsMatcherTLSTest, TestExclusionRegex) { EXPECT_EQ("", invalid_string_2.value()); // Expected to free lowercase_counter, lowercase_gauge, valid_counter, valid_gauge + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); } @@ -938,6 +951,7 @@ class RememberStatsMatcherTest : public testing::TestWithParam { } ~RememberStatsMatcherTest() override { + tls_.shutdownGlobalThreading(); store_.shutdownThreading(); tls_.shutdownThread(); } @@ -1111,6 +1125,7 @@ TEST_F(StatsThreadLocalStoreTest, RemoveRejectedStats) { EXPECT_CALL(sink_, onHistogramComplete(Ref(histogram), 42)); histogram.recordValue(42); textReadout.set("fortytwo"); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -1127,6 +1142,7 @@ TEST_F(StatsThreadLocalStoreTest, NonHotRestartNoTruncation) { // This works fine, and we can find it by its long name because heap-stats do not // get truncated. EXPECT_NE(nullptr, TestUtility::findCounter(*store_, name_1).get()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -1143,6 +1159,7 @@ class StatsThreadLocalStoreTestNoFixture : public testing::Test { ~StatsThreadLocalStoreTestNoFixture() override { if (threading_enabled_) { + tls_.shutdownGlobalThreading(); store_.shutdownThreading(); tls_.shutdownThread(); } @@ -1189,6 +1206,7 @@ TEST_F(StatsThreadLocalStoreTest, ShuttingDown) { store_->counterFromString("c1"); store_->gaugeFromString("g1", Gauge::ImportMode::Accumulate); store_->textReadoutFromString("t1"); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); store_->counterFromString("c2"); store_->gaugeFromString("g2", Gauge::ImportMode::Accumulate); @@ -1208,6 +1226,7 @@ TEST_F(StatsThreadLocalStoreTest, ShuttingDown) { EXPECT_EQ(2L, TestUtility::findGauge(*store_, "g2").use_count()); EXPECT_EQ(2L, TestUtility::findTextReadout(*store_, "t2").use_count()); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -1222,6 +1241,7 @@ TEST_F(StatsThreadLocalStoreTest, MergeDuringShutDown) { EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 1)); h1.recordValue(1); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); // Validate that merge callback is called during shutdown and there is no ASSERT. @@ -1229,6 +1249,7 @@ TEST_F(StatsThreadLocalStoreTest, MergeDuringShutDown) { store_->mergeHistograms([&merge_called]() -> void { merge_called = true; }); EXPECT_TRUE(merge_called); + tls_.shutdownGlobalThreading(); store_->shutdownThreading(); tls_.shutdownThread(); } @@ -1243,7 +1264,9 @@ TEST(ThreadLocalStoreThreadTest, ConstructDestruct) { store.initializeThreading(*dispatcher, tls); { ScopePtr scope1 = store.createScope("scope1."); } + tls.shutdownGlobalThreading(); store.shutdownThreading(); + tls.shutdownThread(); } // Histogram tests @@ -1502,15 +1525,7 @@ class ThreadLocalRealThreadsTestBase : public ThreadLocalStoreNoMocksTestBase { } ~ThreadLocalRealThreadsTestBase() override { - { - BlockingBarrier blocking_barrier(1); - main_dispatcher_->post(blocking_barrier.run([this]() { - store_->shutdownThreading(); - tls_->shutdownGlobalThreading(); - tls_->shutdownThread(); - })); - } - + shutdownThreading(); for (Event::DispatcherPtr& dispatcher : thread_dispatchers_) { dispatcher->post([&dispatcher]() { dispatcher->exit(); }); } @@ -1527,6 +1542,17 @@ class ThreadLocalRealThreadsTestBase : public ThreadLocalStoreNoMocksTestBase { main_thread_->join(); } + void shutdownThreading() { + BlockingBarrier blocking_barrier(1); + main_dispatcher_->post(blocking_barrier.run([this]() { + if (!tls_->isShutdown()) { + tls_->shutdownGlobalThreading(); + } + store_->shutdownThreading(); + tls_->shutdownThread(); + })); + } + void workerThreadFn(uint32_t thread_index, BlockingBarrier& blocking_barrier) { thread_dispatchers_[thread_index] = api_->allocateDispatcher(absl::StrCat("test_worker_", thread_index)); @@ -1766,8 +1792,7 @@ TEST_F(HistogramThreadTest, ScopeOverlap) { EXPECT_EQ(0, store_->histograms().size()); EXPECT_EQ(0, numTlsHistograms()); - store_->shutdownThreading(); - + shutdownThreading(); store_->histogramFromString("histogram_after_shutdown", Histogram::Unit::Unspecified); } diff --git a/test/mocks/thread_local/mocks.cc b/test/mocks/thread_local/mocks.cc index aff789b21936b..18a8cfb30baeb 100644 --- a/test/mocks/thread_local/mocks.cc +++ b/test/mocks/thread_local/mocks.cc @@ -10,10 +10,10 @@ namespace Envoy { namespace ThreadLocal { MockInstance::MockInstance() { - ON_CALL(*this, allocateSlot()).WillByDefault(Invoke(this, &MockInstance::allocateSlot_)); - ON_CALL(*this, runOnAllThreads(_)).WillByDefault(Invoke(this, &MockInstance::runOnAllThreads1_)); + ON_CALL(*this, allocateSlot()).WillByDefault(Invoke(this, &MockInstance::allocateSlotMock)); + ON_CALL(*this, runOnAllThreads(_)).WillByDefault(Invoke(this, &MockInstance::runOnAllThreads1)); ON_CALL(*this, runOnAllThreads(_, _)) - .WillByDefault(Invoke(this, &MockInstance::runOnAllThreads2_)); + .WillByDefault(Invoke(this, &MockInstance::runOnAllThreads2)); ON_CALL(*this, shutdownThread()).WillByDefault(Invoke(this, &MockInstance::shutdownThread_)); ON_CALL(*this, dispatcher()).WillByDefault(ReturnRef(dispatcher_)); } diff --git a/test/mocks/thread_local/mocks.h b/test/mocks/thread_local/mocks.h index e735c543b0e1b..3fa0f8d3479dd 100644 --- a/test/mocks/thread_local/mocks.h +++ b/test/mocks/thread_local/mocks.h @@ -22,13 +22,14 @@ class MockInstance : public Instance { // Server::ThreadLocal MOCK_METHOD(SlotPtr, allocateSlot, ()); MOCK_METHOD(void, registerThread, (Event::Dispatcher & dispatcher, bool main_thread)); - MOCK_METHOD(void, shutdownGlobalThreading, ()); + void shutdownGlobalThreading() override { shutdown_ = true; } MOCK_METHOD(void, shutdownThread, ()); MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); + bool isShutdown() const override { return shutdown_; } - SlotPtr allocateSlot_() { return SlotPtr{new SlotImpl(*this, current_slot_++)}; } - void runOnAllThreads1_(Event::PostCb cb) { cb(); } - void runOnAllThreads2_(Event::PostCb cb, Event::PostCb main_callback) { + SlotPtr allocateSlotMock() { return SlotPtr{new SlotImpl(*this, current_slot_++)}; } + void runOnAllThreads1(Event::PostCb cb) { cb(); } + void runOnAllThreads2(Event::PostCb cb, Event::PostCb main_callback) { cb(); main_callback(); } diff --git a/test/server/admin/stats_handler_test.cc b/test/server/admin/stats_handler_test.cc index 1059d642edbd9..2f3b02ff75369 100644 --- a/test/server/admin/stats_handler_test.cc +++ b/test/server/admin/stats_handler_test.cc @@ -33,6 +33,12 @@ class AdminStatsTest : public testing::TestWithParamshutdownThreading(); + tls_.shutdownThread(); + } + Stats::SymbolTableImpl symbol_table_; NiceMock main_thread_dispatcher_; NiceMock tls_; @@ -189,7 +195,7 @@ TEST_P(AdminStatsTest, StatsAsJson) { })EOF"; EXPECT_THAT(expected_json, JsonStringEq(actual_json)); - store_->shutdownThreading(); + shutdownThreading(); } TEST_P(AdminStatsTest, UsedOnlyStatsAsJson) { @@ -289,7 +295,7 @@ TEST_P(AdminStatsTest, UsedOnlyStatsAsJson) { })EOF"; EXPECT_THAT(expected_json, JsonStringEq(actual_json)); - store_->shutdownThreading(); + shutdownThreading(); } TEST_P(AdminStatsTest, StatsAsJsonFilterString) { @@ -391,7 +397,7 @@ TEST_P(AdminStatsTest, StatsAsJsonFilterString) { })EOF"; EXPECT_THAT(expected_json, JsonStringEq(actual_json)); - store_->shutdownThreading(); + shutdownThreading(); } TEST_P(AdminStatsTest, UsedOnlyStatsAsJsonFilterString) { @@ -502,7 +508,7 @@ TEST_P(AdminStatsTest, UsedOnlyStatsAsJsonFilterString) { })EOF"; EXPECT_THAT(expected_json, JsonStringEq(actual_json)); - store_->shutdownThreading(); + shutdownThreading(); } INSTANTIATE_TEST_SUITE_P(IpVersions, AdminInstanceTest,