fix: do not wait on tag when releasing in-place

Related-To: GSD-11990

If user releases USM without defer policy, do not check whether
allocation is in use when trying to reuse.

Also, when context is being destroyed, use blocking policy to
make sure that tag is updated and driver will notice that
resource is no longer being used by GPU when cleaning up.

Signed-off-by: Szymon Morek <szymon.morek@intel.com>
This commit is contained in:
Szymon Morek
2025-11-25 09:40:48 +00:00
committed by Compute-Runtime-Automation
parent 5790a624e1
commit bb15deca7e
6 changed files with 79 additions and 38 deletions

View File

@@ -42,7 +42,7 @@ namespace L0 {
ze_result_t ContextImp::destroy() {
while (driverHandle->svmAllocsManager->getNumDeferFreeAllocs() > 0) {
this->driverHandle->svmAllocsManager->freeSVMAllocDeferImpl();
this->driverHandle->svmAllocsManager->freeSVMAllocDeferImplBlocking();
}
delete this;

View File

@@ -2826,8 +2826,8 @@ TEST_F(FreeExtTests,
size_t size = 1024;
size_t alignment = 1u;
void *ptr = nullptr;
static_cast<MockMemoryManager *>(driverHandle->getMemoryManager())->deferAllocInUse = true;
auto mockMemoryManager = static_cast<MockMemoryManager *>(driverHandle->getMemoryManager());
mockMemoryManager->deferAllocInUse = true;
ze_host_mem_alloc_desc_t hostDesc = {};
ze_result_t result = context->allocHostMem(&hostDesc,
@@ -2859,10 +2859,12 @@ TEST_F(FreeExtTests,
EXPECT_EQ(ZE_RESULT_SUCCESS, result);
EXPECT_EQ(3u, memManager->numDeferFreeAllocs());
EXPECT_EQ(3u, memManager->deferFreeCallsMade);
static_cast<MockMemoryManager *>(driverHandle->getMemoryManager())->deferAllocInUse = false;
mockMemoryManager->deferAllocInUse = false;
EXPECT_EQ(0u, mockMemoryManager->waitForEnginesCompletionCalled);
context->destroy();
context = nullptr;
EXPECT_EQ(0u, memManager->numDeferFreeAllocs());
EXPECT_EQ(3u, mockMemoryManager->waitForEnginesCompletionCalled);
}
TEST_F(FreeExtTests,

View File

@@ -62,7 +62,7 @@ SVMAllocsManager::SvmAllocationCache::SvmAllocationCache() {
this->enablePerformanceLogging = NEO::debugManager.flags.LogUsmReuse.get();
}
bool SVMAllocsManager::SvmAllocationCache::insert(size_t size, void *ptr, SvmAllocationData *svmData, bool waitForCompletion) {
bool SVMAllocsManager::SvmAllocationCache::insert(size_t size, void *ptr, SvmAllocationData *svmData, CompletionCheckPolicy completionPolicy) {
if (false == sizeAllowed(size) ||
svmData->isInternalAllocation ||
svmData->isImportedAllocation) {
@@ -93,14 +93,14 @@ bool SVMAllocsManager::SvmAllocationCache::insert(size_t size, void *ptr, SvmAll
}
}
if (isSuccess) {
if (waitForCompletion) {
if (completionPolicy == CompletionCheckPolicy::waitOnFree) {
svmAllocsManager->waitForEnginesCompletion(svmData);
}
if (requireUpdatingAllocsForIndirectAccess) {
svmAllocsManager->removeFromAllocsForIndirectAccess(*svmData);
}
svmData->isSavedForReuse = true;
allocations.emplace(std::lower_bound(allocations.begin(), allocations.end(), size), size, ptr, svmData, waitForCompletion);
allocations.emplace(std::lower_bound(allocations.begin(), allocations.end(), size), size, ptr, svmData, completionPolicy == CompletionCheckPolicy::deferred);
empty = false;
if (auto usmReuseCleaner = this->memoryManager->peekExecutionEnvironment().unifiedMemoryReuseCleaner.get()) {
lock.unlock();
@@ -132,7 +132,7 @@ bool SVMAllocsManager::SvmAllocationCache::alignmentAllows(void *ptr, size_t ali
}
bool SVMAllocsManager::SvmAllocationCache::isInUse(SvmCacheAllocationInfo &cacheAllocInfo) {
if (cacheAllocInfo.completed) {
if (!cacheAllocInfo.isInUseCheckRequired) {
return false;
}
if (cacheAllocInfo.svmData->cpuAllocation && memoryManager->allocInUse(*cacheAllocInfo.svmData->cpuAllocation)) {
@@ -700,15 +700,16 @@ bool SVMAllocsManager::freeSVMAlloc(void *ptr, bool blocking) {
}
SvmAllocationData *svmData = getSVMAlloc(ptr);
if (svmData) {
auto completionCheckPolicy = blocking ? SvmAllocationCache::CompletionCheckPolicy::waitOnFree : SvmAllocationCache::CompletionCheckPolicy::notRequired;
if (InternalMemoryType::deviceUnifiedMemory == svmData->memoryType &&
this->usmDeviceAllocationsCache) {
if (this->usmDeviceAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, blocking)) {
if (this->usmDeviceAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, completionCheckPolicy)) {
return true;
}
}
if (InternalMemoryType::hostUnifiedMemory == svmData->memoryType &&
this->usmHostAllocationsCache) {
if (this->usmHostAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, blocking)) {
if (this->usmHostAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, completionCheckPolicy)) {
return true;
}
}
@@ -730,16 +731,16 @@ bool SVMAllocsManager::freeSVMAllocDefer(void *ptr) {
SvmAllocationData *svmData = getSVMAlloc(ptr);
if (svmData) {
constexpr bool waitForCompletion = false;
constexpr auto completionCheckPolicy = SvmAllocationCache::CompletionCheckPolicy::deferred;
if (InternalMemoryType::deviceUnifiedMemory == svmData->memoryType &&
this->usmDeviceAllocationsCache) {
if (this->usmDeviceAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, waitForCompletion)) {
if (this->usmDeviceAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, completionCheckPolicy)) {
return true;
}
}
if (InternalMemoryType::hostUnifiedMemory == svmData->memoryType &&
this->usmHostAllocationsCache) {
if (this->usmHostAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, waitForCompletion)) {
if (this->usmHostAllocationsCache->insert(svmData->gpuAllocations.getDefaultGraphicsAllocation()->getUnderlyingBufferSize(), ptr, svmData, completionCheckPolicy)) {
return true;
}
}
@@ -800,11 +801,11 @@ void SVMAllocsManager::freeSVMAllocImpl(void *ptr, FreePolicyType policy, SvmAll
}
}
void SVMAllocsManager::freeSVMAllocDeferImpl() {
void SVMAllocsManager::freeSVMAllocDeferImpl(FreePolicyType policy) {
std::vector<void *> freedPtr;
for (auto iter = svmDeferFreeAllocs.allocations.begin(); iter != svmDeferFreeAllocs.allocations.end(); ++iter) {
void *ptr = reinterpret_cast<void *>(iter->second.gpuAllocations.getDefaultGraphicsAllocation()->getGpuAddress());
this->freeSVMAllocImpl(ptr, FreePolicyType::defer, this->getSVMAlloc(ptr));
this->freeSVMAllocImpl(ptr, policy, this->getSVMAlloc(ptr));
if (this->getSVMAlloc(ptr) == nullptr) {
freedPtr.push_back(ptr);

View File

@@ -158,8 +158,8 @@ class SVMAllocsManager {
void *allocation;
SvmAllocationData *svmData;
std::chrono::high_resolution_clock::time_point saveTime;
bool completed;
SvmCacheAllocationInfo(size_t allocationSize, void *allocation, SvmAllocationData *svmData, bool completed) : allocationSize(allocationSize), allocation(allocation), svmData(svmData), completed(completed) {
bool isInUseCheckRequired;
SvmCacheAllocationInfo(size_t allocationSize, void *allocation, SvmAllocationData *svmData, bool isInUseCheckRequired) : allocationSize(allocationSize), allocation(allocation), svmData(svmData), isInUseCheckRequired(isInUseCheckRequired) {
saveTime = std::chrono::high_resolution_clock::now();
}
bool operator<(SvmCacheAllocationInfo const &other) const {
@@ -183,6 +183,11 @@ class SVMAllocsManager {
trim,
trimOld
};
enum class CompletionCheckPolicy {
waitOnFree,
deferred,
notRequired,
};
struct SvmAllocationCachePerfInfo {
uint64_t allocationSize;
@@ -199,7 +204,7 @@ class SVMAllocsManager {
SvmAllocationCache();
static bool sizeAllowed(size_t size) { return size <= SvmAllocationCache::maxServicedSize; }
bool insert(size_t size, void *ptr, SvmAllocationData *svmData, bool waitForCompletion);
bool insert(size_t size, void *ptr, SvmAllocationData *svmData, CompletionCheckPolicy completionCheckPolicy);
static bool allocUtilizationAllows(size_t requestedSize, size_t reuseCandidateSize);
static bool alignmentAllows(void *ptr, size_t alignment);
bool isInUse(SvmCacheAllocationInfo &cacheAllocInfo);
@@ -259,8 +264,9 @@ class SVMAllocsManager {
MOCKABLE_VIRTUAL bool freeSVMAlloc(void *ptr, bool blocking);
MOCKABLE_VIRTUAL bool freeSVMAllocDefer(void *ptr);
MOCKABLE_VIRTUAL void freeSVMAllocDeferImpl();
MOCKABLE_VIRTUAL void freeSVMAllocImpl(void *ptr, FreePolicyType policy, SvmAllocationData *svmData);
void freeSVMAllocDeferImpl() { this->freeSVMAllocDeferImpl(FreePolicyType::defer); }
void freeSVMAllocDeferImplBlocking() { this->freeSVMAllocDeferImpl(FreePolicyType::blocking); }
bool freeSVMAlloc(void *ptr) { return freeSVMAlloc(ptr, false); }
void cleanupUSMAllocCaches();
void trimUSMDeviceAllocCache();
@@ -306,6 +312,7 @@ class SVMAllocsManager {
void waitForEnginesCompletion(SvmAllocationData *allocationData);
protected:
void freeSVMAllocDeferImpl(FreePolicyType policy);
void *createZeroCopySvmAllocation(size_t size, const SvmAllocationProperties &svmProperties,
const RootDeviceIndicesContainer &rootDeviceIndices,
const std::map<uint32_t, DeviceBitfield> &subdeviceBitfields);

View File

@@ -138,11 +138,11 @@ TEST(SvmAllocationCacheSimpleTest, givenAllocationsWhenCheckingIsInUseThenReturn
allocationCache.svmAllocsManager = &svmAllocsManager;
{
constexpr bool completed = false;
constexpr bool inUseCheckRequired = true;
memoryManager.deferAllocInUse = false;
MockGraphicsAllocation gpuGfxAllocation;
SvmAllocationData svmAllocData(mockRootDeviceIndex);
SvmCacheAllocationInfo svmCacheAllocInfo(1u, addrToPtr(0xFULL), &svmAllocData, completed);
SvmCacheAllocationInfo svmCacheAllocInfo(1u, addrToPtr(0xFULL), &svmAllocData, inUseCheckRequired);
EXPECT_FALSE(allocationCache.isInUse(svmCacheAllocInfo));
svmAllocData.gpuAllocations.addAllocation(&gpuGfxAllocation);
EXPECT_FALSE(allocationCache.isInUse(svmCacheAllocInfo));
@@ -150,23 +150,23 @@ TEST(SvmAllocationCacheSimpleTest, givenAllocationsWhenCheckingIsInUseThenReturn
EXPECT_TRUE(allocationCache.isInUse(svmCacheAllocInfo));
}
{
constexpr bool completed = false;
constexpr bool inUseCheckRequired = true;
memoryManager.deferAllocInUse = false;
MockGraphicsAllocation cpuGfxAllocation;
SvmAllocationData svmAllocData(mockRootDeviceIndex);
svmAllocData.cpuAllocation = &cpuGfxAllocation;
SvmCacheAllocationInfo svmCacheAllocInfo(1u, addrToPtr(0xFULL), &svmAllocData, completed);
SvmCacheAllocationInfo svmCacheAllocInfo(1u, addrToPtr(0xFULL), &svmAllocData, inUseCheckRequired);
EXPECT_FALSE(allocationCache.isInUse(svmCacheAllocInfo));
memoryManager.deferAllocInUse = true;
EXPECT_TRUE(allocationCache.isInUse(svmCacheAllocInfo));
}
{
constexpr bool completed = true;
constexpr bool inUseCheckRequired = false;
memoryManager.deferAllocInUse = false;
MockGraphicsAllocation cpuGfxAllocation;
SvmAllocationData svmAllocData(mockRootDeviceIndex);
svmAllocData.cpuAllocation = &cpuGfxAllocation;
SvmCacheAllocationInfo svmCacheAllocInfo(1u, addrToPtr(0xFULL), &svmAllocData, completed);
SvmCacheAllocationInfo svmCacheAllocInfo(1u, addrToPtr(0xFULL), &svmAllocData, inUseCheckRequired);
EXPECT_FALSE(allocationCache.isInUse(svmCacheAllocInfo));
memoryManager.deferAllocInUse = true;
EXPECT_FALSE(allocationCache.isInUse(svmCacheAllocInfo));
@@ -189,19 +189,19 @@ TEST(SvmAllocationCacheSimpleTest, givenAllocationsWhenInsertingAllocationThenDo
{
svmAllocData.isImportedAllocation = false;
svmAllocData.isInternalAllocation = false;
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, false));
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::notRequired));
allocationCache.allocations.clear();
}
{
svmAllocData.isImportedAllocation = true;
svmAllocData.isInternalAllocation = false;
EXPECT_FALSE(allocationCache.insert(1u, ptr, &svmAllocData, false));
EXPECT_FALSE(allocationCache.insert(1u, ptr, &svmAllocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::notRequired));
allocationCache.allocations.clear();
}
{
svmAllocData.isImportedAllocation = false;
svmAllocData.isInternalAllocation = true;
EXPECT_FALSE(allocationCache.insert(1u, ptr, &svmAllocData, false));
EXPECT_FALSE(allocationCache.insert(1u, ptr, &svmAllocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::notRequired));
allocationCache.allocations.clear();
}
}
@@ -229,7 +229,7 @@ TEST(SvmAllocationCacheSimpleTest, givenAllocationsWhenGettingAllocationThenUpda
{
allocationCache.requireUpdatingAllocsForIndirectAccess = false;
EXPECT_EQ(1u, svmAllocsManager.internalAllocationsMap.count(1u));
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, false));
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::notRequired));
EXPECT_EQ(1u, svmAllocsManager.internalAllocationsMap.count(1u));
auto reusedPtr = allocationCache.get(1u, unifiedMemoryProperties);
EXPECT_EQ(ptr, reusedPtr);
@@ -248,7 +248,7 @@ TEST(SvmAllocationCacheSimpleTest, givenAllocationsWhenGettingAllocationThenUpda
EXPECT_EQ(1u, svmAllocsManager.internalAllocationsMap.count(1u));
EXPECT_EQ(0u, svmAllocsManager.internalAllocationsMap.count(2u));
allocationCache.requireUpdatingAllocsForIndirectAccess = true;
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, false));
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::notRequired));
EXPECT_EQ(0u, svmAllocsManager.internalAllocationsMap.count(1u));
EXPECT_EQ(0u, svmAllocsManager.internalAllocationsMap.count(2u));
auto reusedPtr = allocationCache.get(1u, unifiedMemoryProperties);
@@ -277,7 +277,7 @@ TEST(SvmAllocationCacheSimpleTest, givenAllocationsInCacheWhenCallingTrimThenUse
SvmAllocationData svmAllocData(mockRootDeviceIndex);
svmAllocData.gpuAllocations.addAllocation(&gpuGfxAllocation);
allocationCache.insert(1u, ptr, &svmAllocData, false);
allocationCache.insert(1u, ptr, &svmAllocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::waitOnFree);
EXPECT_EQ(1u, allocationCache.allocations.size());
svmAllocsManager.freeSVMAllocImplCallBase = false;
allocationCache.trim();
@@ -311,7 +311,7 @@ TEST(SvmAllocationCacheSimpleTest, givenReuseCleanerWhenInsertingAllocationIntoC
UnifiedMemoryProperties unifiedMemoryProperties(InternalMemoryType::hostUnifiedMemory, 1, rootDeviceIndices, deviceBitfields);
EXPECT_FALSE(reuseCleaner->startThreadCalled);
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, false));
EXPECT_TRUE(allocationCache.insert(1u, ptr, &svmAllocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::notRequired));
EXPECT_TRUE(reuseCleaner->startThreadCalled);
EXPECT_FALSE(reuseCleaner->stopThreadCalled);
@@ -1179,7 +1179,7 @@ TEST_F(SvmDeviceAllocationCacheTest, givenAllocationInUsageWhenAllocatingAfterFr
unifiedMemoryProperties.device = device;
auto allocation = svmManager->createUnifiedMemoryAllocation(10u, unifiedMemoryProperties);
EXPECT_NE(allocation, nullptr);
svmManager->freeSVMAlloc(allocation);
svmManager->freeSVMAllocDefer(allocation);
EXPECT_EQ(svmManager->usmDeviceAllocationsCache->allocations.size(), 1u);
MockMemoryManager *mockMemoryManager = reinterpret_cast<MockMemoryManager *>(device->getMemoryManager());
@@ -1189,7 +1189,7 @@ TEST_F(SvmDeviceAllocationCacheTest, givenAllocationInUsageWhenAllocatingAfterFr
auto svmData = svmManager->getSVMAlloc(testedAllocation);
EXPECT_NE(nullptr, svmData);
svmManager->freeSVMAlloc(testedAllocation);
svmManager->freeSVMAllocDefer(testedAllocation);
EXPECT_EQ(svmManager->usmDeviceAllocationsCache->allocations.size(), 2u);
svmManager->cleanupUSMAllocCaches();
@@ -2093,7 +2093,7 @@ TEST_F(SvmHostAllocationCacheTest, givenAllocationInUsageWhenAllocatingAfterFree
UnifiedMemoryProperties unifiedMemoryProperties(InternalMemoryType::hostUnifiedMemory, 1, rootDeviceIndices, deviceBitfields);
auto allocation = svmManager->createHostUnifiedMemoryAllocation(10u, unifiedMemoryProperties);
EXPECT_NE(allocation, nullptr);
svmManager->freeSVMAlloc(allocation);
svmManager->freeSVMAllocDefer(allocation);
EXPECT_EQ(svmManager->usmHostAllocationsCache->allocations.size(), 1u);
memoryManager->deferAllocInUse = true;
@@ -2102,12 +2102,43 @@ TEST_F(SvmHostAllocationCacheTest, givenAllocationInUsageWhenAllocatingAfterFree
auto svmData = svmManager->getSVMAlloc(testedAllocation);
EXPECT_NE(nullptr, svmData);
svmManager->freeSVMAlloc(testedAllocation);
svmManager->freeSVMAllocDefer(testedAllocation);
EXPECT_EQ(svmManager->usmHostAllocationsCache->allocations.size(), 2u);
svmManager->cleanupUSMAllocCaches();
}
TEST_F(SvmHostAllocationCacheTest, givenAllocationInUsageWhenReleaseInPlaceAndAllocatingAfterFreeThenReuseAllocation) {
auto deviceFactory = std::make_unique<UltDeviceFactory>(1, 1);
RootDeviceIndicesContainer rootDeviceIndices = {mockRootDeviceIndex};
std::map<uint32_t, DeviceBitfield> deviceBitfields{{mockRootDeviceIndex, mockDeviceBitfield}};
DebugManagerStateRestore restore;
debugManager.flags.ExperimentalEnableHostAllocationCache.set(1);
auto device = deviceFactory->rootDevices[0];
auto memoryManager = reinterpret_cast<MockMemoryManager *>(device->getMemoryManager());
auto svmManager = std::make_unique<MockSVMAllocsManager>(memoryManager);
memoryManager->usmReuseInfo.init(1 * MemoryConstants::gigaByte, UsmReuseInfo::notLimited);
svmManager->initUsmAllocationsCaches(*device);
EXPECT_NE(nullptr, svmManager->usmHostAllocationsCache);
UnifiedMemoryProperties unifiedMemoryProperties(InternalMemoryType::hostUnifiedMemory, 1, rootDeviceIndices, deviceBitfields);
auto allocation = svmManager->createHostUnifiedMemoryAllocation(10u, unifiedMemoryProperties);
EXPECT_NE(allocation, nullptr);
svmManager->freeSVMAlloc(allocation);
EXPECT_EQ(svmManager->usmHostAllocationsCache->allocations.size(), 1u);
memoryManager->deferAllocInUse = true;
auto testedAllocation = svmManager->createHostUnifiedMemoryAllocation(10u, unifiedMemoryProperties);
EXPECT_EQ(svmManager->usmHostAllocationsCache->allocations.size(), 0u);
auto svmData = svmManager->getSVMAlloc(testedAllocation);
EXPECT_NE(nullptr, svmData);
svmManager->freeSVMAlloc(testedAllocation);
EXPECT_EQ(svmManager->usmHostAllocationsCache->allocations.size(), 1u);
svmManager->cleanupUSMAllocCaches();
}
TEST_F(SvmHostAllocationCacheTest, givenAllocationMarkedCompletedWhenAllocatingAfterFreeThenDoNotCallAllocInUse) {
auto deviceFactory = std::make_unique<UltDeviceFactory>(1, 1);
RootDeviceIndicesContainer rootDeviceIndices = {mockRootDeviceIndex};
@@ -2130,7 +2161,7 @@ TEST_F(SvmHostAllocationCacheTest, givenAllocationMarkedCompletedWhenAllocatingA
EXPECT_EQ(svmManager->usmHostAllocationsCache->allocations.size(), 1u);
auto &svmAllocCacheInfo = svmManager->usmHostAllocationsCache->allocations[0];
EXPECT_TRUE(svmAllocCacheInfo.completed);
EXPECT_FALSE(svmAllocCacheInfo.isInUseCheckRequired);
memoryManager->deferAllocInUse = true;
auto testedAllocation = svmManager->createHostUnifiedMemoryAllocation(10u, unifiedMemoryProperties);

View File

@@ -51,7 +51,7 @@ TEST(UnifiedMemoryReuseCleanerTestsMt, givenUnifiedMemoryReuseCleanerWhenCachesA
cleaner.waitOnConditionVar.store(false);
EXPECT_FALSE(cleaner.waitOnConditionVar.load());
SvmAllocationData allocData{0};
svmAllocCache->insert(svmAllocSize, nullptr, &allocData, false);
svmAllocCache->insert(svmAllocSize, nullptr, &allocData, SVMAllocsManager::SvmAllocationCache::CompletionCheckPolicy::notRequired);
cleaner.waitTillSleep();
EXPECT_TRUE(cleaner.waitOnConditionVar.load());
EXPECT_TRUE(cleaner.isEmpty());