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.
This commit is contained in:
parent
dfb2656d9c
commit
02b205361d
2 changed files with 13 additions and 69 deletions
|
|
@ -9,56 +9,20 @@
|
|||
#include <AK/Assertions.h>
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/EventLoopImplementation.h>
|
||||
#include <LibCore/EventReceiver.h>
|
||||
#include <LibCore/Promise.h>
|
||||
#include <LibCore/ThreadEventQueue.h>
|
||||
#ifndef AK_OS_WINDOWS
|
||||
# include <pthread.h>
|
||||
#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<Vector<EventLoop&>*>(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<EventLoop&>*& event_loop_stack_uninitialized()
|
||||
{
|
||||
thread_local Vector<EventLoop&>* s_event_loop_stack = nullptr;
|
||||
return s_event_loop_stack;
|
||||
}
|
||||
Vector<EventLoop&>& event_loop_stack()
|
||||
{
|
||||
auto& the_stack = event_loop_stack_uninitialized();
|
||||
if (the_stack == nullptr) {
|
||||
the_stack = new Vector<EventLoop&>();
|
||||
#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<EventLoop&>& 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<WeakEventLoopReference> 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<bool()> goal_condition)
|
||||
{
|
||||
EventLoopPusher pusher(*this);
|
||||
VERIFY(current_event_loop() == this);
|
||||
while (!goal_condition())
|
||||
pump();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue