diff --git a/Tests/LibCore/CMakeLists.txt b/Tests/LibCore/CMakeLists.txt index ced5b45211..321b0cef7c 100644 --- a/Tests/LibCore/CMakeLists.txt +++ b/Tests/LibCore/CMakeLists.txt @@ -28,4 +28,5 @@ if(NOT WIN32) set_tests_properties(TestLibCoreMappedFile TestLibCoreStream PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") endif() +target_link_libraries(TestLibCoreEventLoop PRIVATE LibThreading) target_link_libraries(TestLibCoreSharedSingleProducerCircularQueue PRIVATE LibThreading) diff --git a/Tests/LibCore/TestLibCoreEventLoop.cpp b/Tests/LibCore/TestLibCoreEventLoop.cpp index 9300ea76da..6117110435 100644 --- a/Tests/LibCore/TestLibCoreEventLoop.cpp +++ b/Tests/LibCore/TestLibCoreEventLoop.cpp @@ -4,8 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include +#include TEST_CASE(test_poll_for_events) { @@ -13,3 +15,30 @@ TEST_CASE(test_poll_for_events) event_loop.pump(Core::EventLoop::WaitMode::PollForEvents); } + +// Simulate the condition that occurs during exit(): ThreadData (thread-local) is destroyed +// while the EventLoop (normally stack-allocated) is still alive. Another thread holding a +// WeakEventLoopReference can then call wake(), which must handle the closed pipe FD gracefully. +TEST_CASE(wake_after_thread_exit) +{ + Core::EventLoop main_loop; + + IGNORE_USE_IN_ESCAPING_LAMBDA OwnPtr worker_loop; + IGNORE_USE_IN_ESCAPING_LAMBDA RefPtr weak_ref; + + auto thread = Threading::Thread::construct("Worker"sv, [&] { + worker_loop = make(); + weak_ref = Core::EventLoop::current_weak(); + return 0; + }); + thread->start(); + MUST(thread->join()); + + { + auto strong = weak_ref->take(); + if (strong) + strong->wake(); + } + + worker_loop.clear(); +}