From 02b205361dd239e134f434e484b609d1fa5f1938 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 4 Jun 2026 18:58:06 +0200 Subject: [PATCH] LibCore: Remove nested EventLoop support Replace the per-thread EventLoop stack with a single thread-local pointer. Constructing an EventLoop now requires that the current thread does not already have one, and exec() and spin_until() operate only on that current loop instead of pushing a temporary nested loop. LibLine was the last user that needed nested Core event loops, so the stack allocation and pthread cleanup machinery can go away. --- Libraries/LibCore/EventLoop.cpp | 75 ++++++--------------------------- Libraries/LibCore/EventLoop.h | 7 +-- 2 files changed, 13 insertions(+), 69 deletions(-) diff --git a/Libraries/LibCore/EventLoop.cpp b/Libraries/LibCore/EventLoop.cpp index 5f14fdf43d..a076a9d9f3 100644 --- a/Libraries/LibCore/EventLoop.cpp +++ b/Libraries/LibCore/EventLoop.cpp @@ -9,56 +9,20 @@ #include #include #include -#include #include #include #include #include #include -#ifndef AK_OS_WINDOWS -# include -#endif namespace Core { namespace { -#ifndef AK_OS_WINDOWS -static pthread_key_t s_event_loop_stack_key; -static pthread_once_t s_event_loop_stack_key_once = PTHREAD_ONCE_INIT; - -static void destroy_event_loop_stack(void* value) +EventLoop*& current_event_loop() { - delete static_cast*>(value); -} - -static void initialize_event_loop_stack_key() -{ - VERIFY(pthread_key_create(&s_event_loop_stack_key, destroy_event_loop_stack) == 0); -} - -static void ensure_event_loop_stack_key() -{ - VERIFY(pthread_once(&s_event_loop_stack_key_once, initialize_event_loop_stack_key) == 0); -} -#endif - -Vector*& event_loop_stack_uninitialized() -{ - thread_local Vector* s_event_loop_stack = nullptr; - return s_event_loop_stack; -} -Vector& event_loop_stack() -{ - auto& the_stack = event_loop_stack_uninitialized(); - if (the_stack == nullptr) { - the_stack = new Vector(); -#ifndef AK_OS_WINDOWS - ensure_event_loop_stack_key(); - VERIFY(pthread_setspecific(s_event_loop_stack_key, the_stack) == 0); -#endif - } - return *the_stack; + thread_local EventLoop* s_current_event_loop = nullptr; + return s_current_event_loop; } } @@ -66,31 +30,28 @@ Vector& event_loop_stack() EventLoop::EventLoop() : m_impl(EventLoopManager::the().make_implementation()) { - if (event_loop_stack().is_empty()) { - event_loop_stack().append(*this); - } + VERIFY(!current_event_loop()); + current_event_loop() = this; } EventLoop::~EventLoop() { if (m_weak) m_weak->revoke(); - if (!event_loop_stack().is_empty() && &event_loop_stack().last() == this) { - event_loop_stack().take_last(); - } + if (current_event_loop() == this) + current_event_loop() = nullptr; } bool EventLoop::is_running() { - auto& stack = event_loop_stack_uninitialized(); - return stack != nullptr && !stack->is_empty(); + return current_event_loop() != nullptr; } EventLoop& EventLoop::current() { - if (event_loop_stack().is_empty()) + if (!current_event_loop()) dbgln("No EventLoop is present, unable to return current one!"); - return event_loop_stack().last(); + return *current_event_loop(); } NonnullRefPtr EventLoop::current_weak() @@ -111,27 +72,15 @@ bool EventLoop::was_exit_requested() return m_impl->was_exit_requested(); } -struct EventLoopPusher { -public: - EventLoopPusher(EventLoop& event_loop) - { - event_loop_stack().append(event_loop); - } - ~EventLoopPusher() - { - event_loop_stack().take_last(); - } -}; - int EventLoop::exec() { - EventLoopPusher pusher(*this); + VERIFY(current_event_loop() == this); return m_impl->exec(); } void EventLoop::spin_until(Function goal_condition) { - EventLoopPusher pusher(*this); + VERIFY(current_event_loop() == this); while (!goal_condition()) pump(); } diff --git a/Libraries/LibCore/EventLoop.h b/Libraries/LibCore/EventLoop.h index 1f02b889df..bb684b8b09 100644 --- a/Libraries/LibCore/EventLoop.h +++ b/Libraries/LibCore/EventLoop.h @@ -31,10 +31,7 @@ class WeakEventLoopReference; // Event loops, through select(), allow programs to "go to sleep" for most of their runtime until some event happens. // EventLoop is too expensive to use in realtime scenarios (read: audio) where even the time required by a single select() system call is too large and unpredictable. // -// There is at most one running event loop per thread. -// Another event loop can be started while another event loop is already running; that new event loop will take over for the other event loop. -// This is mainly used in LibGUI, where each modal window stacks another event loop until it is closed. -// However, that means you need to be careful with storing the current event loop, as it might already be gone at the time of use. +// There is at most one event loop per thread. // Event loops currently handle these kinds of events: // - Deferred invocations caused by various objects. These are just a generic way of telling the EventLoop to run some function as soon as possible at a later point. // - Timers, which repeatedly (or once after a delay) run a function on the EventLoop. Note that timers are not super accurate. @@ -48,8 +45,6 @@ class CORE_API EventLoop { AK_MAKE_NONCOPYABLE(EventLoop); private: - friend struct EventLoopPusher; - public: enum class WaitMode { WaitForEvents,