From 1816c8eac4e8e83a89a00510a2428eb1c4bea005 Mon Sep 17 00:00:00 2001 From: Oskar Hubert Weber Date: Wed, 16 Apr 2025 19:33:13 +0000 Subject: [PATCH] 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 --- shared/source/memory_manager/deferred_deleter.cpp | 9 +++++++-- shared/source/memory_manager/deferred_deleter.h | 1 + shared/source/os_interface/linux/os_thread_linux.cpp | 4 ++++ shared/source/os_interface/linux/os_thread_linux.h | 1 + shared/source/os_interface/os_thread.h | 1 + shared/source/os_interface/windows/os_thread_win.cpp | 5 +++++ shared/source/os_interface/windows/os_thread_win.h | 1 + .../deferrable_allocation_deletion_tests.cpp | 4 +++- 8 files changed, 23 insertions(+), 3 deletions(-) diff --git a/shared/source/memory_manager/deferred_deleter.cpp b/shared/source/memory_manager/deferred_deleter.cpp index 0383e0fd65..b16477d383 100644 --- a/shared/source/memory_manager/deferred_deleter.cpp +++ b/shared/source/memory_manager/deferred_deleter.cpp @@ -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(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; } diff --git a/shared/source/memory_manager/deferred_deleter.h b/shared/source/memory_manager/deferred_deleter.h index 006f57df78..5b53d5a6d1 100644 --- a/shared/source/memory_manager/deferred_deleter.h +++ b/shared/source/memory_manager/deferred_deleter.h @@ -40,6 +40,7 @@ class DeferredDeleter : NEO::NonCopyableAndNonMovableClass { static void *run(void *); + std::atomic exitedMainLoop = false; std::atomic doWorkInBackground = false; std::atomic elementsToRelease = 0; std::atomic hostptrsToRelease = 0; diff --git a/shared/source/os_interface/linux/os_thread_linux.cpp b/shared/source/os_interface/linux/os_thread_linux.cpp index f5bf4c21e7..871750aa9f 100644 --- a/shared/source/os_interface/linux/os_thread_linux.cpp +++ b/shared/source/os_interface/linux/os_thread_linux.cpp @@ -24,6 +24,10 @@ void ThreadLinux::join() { pthread_join(threadId, nullptr); } +void ThreadLinux::detach() { + pthread_detach(threadId); +} + void ThreadLinux::yield() { sched_yield(); } diff --git a/shared/source/os_interface/linux/os_thread_linux.h b/shared/source/os_interface/linux/os_thread_linux.h index b934ba4b8e..8c2551a533 100644 --- a/shared/source/os_interface/linux/os_thread_linux.h +++ b/shared/source/os_interface/linux/os_thread_linux.h @@ -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; diff --git a/shared/source/os_interface/os_thread.h b/shared/source/os_interface/os_thread.h index 845a1e0aa8..9d1eee7d88 100644 --- a/shared/source/os_interface/os_thread.h +++ b/shared/source/os_interface/os_thread.h @@ -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; }; diff --git a/shared/source/os_interface/windows/os_thread_win.cpp b/shared/source/os_interface/windows/os_thread_win.cpp index cf39a745bb..a858fec9e5 100644 --- a/shared/source/os_interface/windows/os_thread_win.cpp +++ b/shared/source/os_interface/windows/os_thread_win.cpp @@ -21,7 +21,12 @@ void ThreadWin::join() { thread->join(); } +void ThreadWin::detach() { + thread->detach(); +} + void ThreadWin::yield() { std::this_thread::yield(); } + } // namespace NEO diff --git a/shared/source/os_interface/windows/os_thread_win.h b/shared/source/os_interface/windows/os_thread_win.h index 2a99670e02..98ce9250ae 100644 --- a/shared/source/os_interface/windows/os_thread_win.h +++ b/shared/source/os_interface/windows/os_thread_win.h @@ -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; diff --git a/shared/test/unit_test/memory_manager/deferrable_allocation_deletion_tests.cpp b/shared/test/unit_test/memory_manager/deferrable_allocation_deletion_tests.cpp index 0fa702e801..5127dba06d 100644 --- a/shared/test/unit_test/memory_manager/deferrable_allocation_deletion_tests.cpp +++ b/shared/test/unit_test/memory_manager/deferrable_allocation_deletion_tests.cpp @@ -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);