fix: avoid joining thread in deferred deleter

Join thread in DllMain (which we are not aware of)
can result in hang occurring in DeferredDeleter, if the library is
freed before FreeLibraryAndExitThread call from within the worker thread,
the thread gets stuck, thus the main thread is stuck on worker->join().

Related-To: NEO-14121

Signed-off-by: Oskar Hubert Weber <oskar.hubert.weber@intel.com>
This commit is contained in:
Oskar Hubert Weber
2025-04-16 19:33:13 +00:00
committed by Compute-Runtime-Automation
parent ee769f5983
commit 1816c8eac4
8 changed files with 23 additions and 3 deletions

View File

@@ -27,8 +27,11 @@ void DeferredDeleter::stop() {
doWorkInBackground = false;
lock.unlock();
condition.notify_one();
// Wait for the working job to exit
worker->join();
worker->detach();
// Wait for the working job to exit main loop
while (!exitedMainLoop) {
std::this_thread::yield();
}
// Delete working thread
worker.reset();
}
@@ -76,6 +79,7 @@ void DeferredDeleter::ensureThread() {
if (worker != nullptr) {
return;
}
exitedMainLoop = false;
worker = Thread::createFunc(run, reinterpret_cast<void *>(this));
}
@@ -103,6 +107,7 @@ void *DeferredDeleter::run(void *arg) {
lock.lock();
// Check whether working thread should be stopped
} while (!self->shouldStop());
self->exitedMainLoop = true;
lock.unlock();
return nullptr;
}

View File

@@ -40,6 +40,7 @@ class DeferredDeleter : NEO::NonCopyableAndNonMovableClass {
static void *run(void *);
std::atomic<bool> exitedMainLoop = false;
std::atomic<bool> doWorkInBackground = false;
std::atomic<int> elementsToRelease = 0;
std::atomic<int> hostptrsToRelease = 0;

View File

@@ -24,6 +24,10 @@ void ThreadLinux::join() {
pthread_join(threadId, nullptr);
}
void ThreadLinux::detach() {
pthread_detach(threadId);
}
void ThreadLinux::yield() {
sched_yield();
}

View File

@@ -16,6 +16,7 @@ class ThreadLinux : public Thread {
public:
ThreadLinux(pthread_t threadId);
void join() override;
void detach() override;
void yield() override;
~ThreadLinux() override = default;

View File

@@ -15,6 +15,7 @@ class Thread {
public:
static decltype(&Thread::create) createFunc;
virtual void join() = 0;
virtual void detach() = 0;
virtual ~Thread() = default;
virtual void yield() = 0;
};

View File

@@ -21,7 +21,12 @@ void ThreadWin::join() {
thread->join();
}
void ThreadWin::detach() {
thread->detach();
}
void ThreadWin::yield() {
std::this_thread::yield();
}
} // namespace NEO

View File

@@ -16,6 +16,7 @@ class ThreadWin : public Thread {
public:
ThreadWin(std::thread *thread);
void join() override;
void detach() override;
void yield() override;
~ThreadWin() override = default;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2024 Intel Corporation
* Copyright (C) 2018-2025 Intel Corporation
*
* SPDX-License-Identifier: MIT
*
@@ -10,6 +10,7 @@
#include "shared/source/memory_manager/deferrable_allocation_deletion.h"
#include "shared/source/memory_manager/deferred_deleter.h"
#include "shared/source/os_interface/os_context.h"
#include "shared/test/common/helpers/memory_management.h"
#include "shared/test/common/libult/ult_command_stream_receiver.h"
#include "shared/test/common/mocks/mock_allocation_properties.h"
#include "shared/test/common/mocks/mock_device.h"
@@ -36,6 +37,7 @@ struct DeferredDeleterPublic : DeferredDeleter {
struct DeferrableAllocationDeletionTest : ::testing::Test {
void SetUp() override {
MemoryManagement::fastLeaksDetectionMode = MemoryManagement::LeakDetectionMode::TURN_OFF_LEAK_DETECTION;
executionEnvironment = new MockExecutionEnvironment(defaultHwInfo.get(), false, 1u);
executionEnvironment->incRefInternal();
memoryManager = new MockMemoryManager(*executionEnvironment);