Snap for 10147226 from 76dac144917361b32d3f13f1e517f984b6207593 to udc-release Change-Id: I71466cbd1c617492f297f99e475c0fac2eceaf0b
diff --git a/standalone/combined.h b/standalone/combined.h index d5365b6..0066056 100644 --- a/standalone/combined.h +++ b/standalone/combined.h
@@ -239,6 +239,7 @@ } TSDRegistryT *getTSDRegistry() { return &TSDRegistry; } + QuarantineT *getQuarantine() { return &Quarantine; } // The Cache must be provided zero-initialized. void initCache(CacheT *Cache) { Cache->init(&Stats, &Primary); } @@ -254,6 +255,13 @@ TSD->getCache().destroy(&Stats); } + void drainCache(TSD<ThisT> *TSD) { + Quarantine.drainAndRecycle(&TSD->getQuarantineCache(), + QuarantineCallback(*this, TSD->getCache())); + TSD->getCache().drain(); + } + void drainCaches() { TSDRegistry.drainCaches(this); } + ALWAYS_INLINE void *getHeaderTaggedPointer(void *Ptr) { if (!allocatorSupportsMemoryTagging<Params>()) return Ptr; @@ -747,6 +755,8 @@ void releaseToOS(ReleaseToOS ReleaseType) { initThreadMaybe(); + if (ReleaseType == ReleaseToOS::ForceAll) + drainCaches(); Primary.releaseToOS(ReleaseType); Secondary.releaseToOS(); }
diff --git a/standalone/quarantine.h b/standalone/quarantine.h index e65a733..b5f8db0 100644 --- a/standalone/quarantine.h +++ b/standalone/quarantine.h
@@ -192,6 +192,12 @@ uptr getMaxSize() const { return atomic_load_relaxed(&MaxSize); } uptr getCacheSize() const { return atomic_load_relaxed(&MaxCacheSize); } + // This is supposed to be used in test only. + bool isEmpty() { + ScopedLock L(CacheMutex); + return Cache.getSize() == 0U; + } + void put(CacheT *C, Callback Cb, Node *Ptr, uptr Size) { C->enqueue(Cb, Ptr, Size); if (C->getSize() > getCacheSize())
diff --git a/standalone/tests/combined_test.cpp b/standalone/tests/combined_test.cpp index 33a309e..44ba639 100644 --- a/standalone/tests/combined_test.cpp +++ b/standalone/tests/combined_test.cpp
@@ -457,6 +457,28 @@ TSD->unlock(); } +SCUDO_TYPED_TEST(ScudoCombinedTest, ForceCacheDrain) NO_THREAD_SAFETY_ANALYSIS { + auto *Allocator = this->Allocator.get(); + + std::vector<void *> V; + for (scudo::uptr I = 0; I < 64U; I++) + V.push_back(Allocator->allocate( + rand() % (TypeParam::Primary::SizeClassMap::MaxSize / 2U), Origin)); + for (auto P : V) + Allocator->deallocate(P, Origin); + + // `ForceAll` will also drain the caches. + Allocator->releaseToOS(scudo::ReleaseToOS::ForceAll); + + bool UnlockRequired; + auto *TSD = Allocator->getTSDRegistry()->getTSDAndLock(&UnlockRequired); + EXPECT_TRUE(TSD->getCache().isEmpty()); + EXPECT_EQ(TSD->getQuarantineCache().getSize(), 0U); + EXPECT_TRUE(Allocator->getQuarantine()->isEmpty()); + if (UnlockRequired) + TSD->unlock(); +} + SCUDO_TYPED_TEST(ScudoCombinedTest, ThreadedCombined) { std::mutex Mutex; std::condition_variable Cv;
diff --git a/standalone/tsd_exclusive.h b/standalone/tsd_exclusive.h index aca9fc9..2383674 100644 --- a/standalone/tsd_exclusive.h +++ b/standalone/tsd_exclusive.h
@@ -59,6 +59,15 @@ Initialized = false; } + void drainCaches(Allocator *Instance) { + // We don't have a way to iterate all thread local `ThreadTSD`s. Simply + // drain the `ThreadTSD` of current thread and `FallbackTSD`. + Instance->drainCache(&ThreadTSD); + FallbackTSD.lock(); + Instance->drainCache(&FallbackTSD); + FallbackTSD.unlock(); + } + ALWAYS_INLINE void initThreadMaybe(Allocator *Instance, bool MinimalInit) { if (LIKELY(State.InitState != ThreadState::NotInitialized)) return;
diff --git a/standalone/tsd_shared.h b/standalone/tsd_shared.h index e193281..dcb0948 100644 --- a/standalone/tsd_shared.h +++ b/standalone/tsd_shared.h
@@ -54,6 +54,15 @@ Initialized = false; } + void drainCaches(Allocator *Instance) { + ScopedLock L(MutexTSDs); + for (uptr I = 0; I < NumberOfTSDs; ++I) { + TSDs[I].lock(); + Instance->drainCache(&TSDs[I]); + TSDs[I].unlock(); + } + } + ALWAYS_INLINE void initThreadMaybe(Allocator *Instance, UNUSED bool MinimalInit) { if (LIKELY(getCurrentTSD()))