Files
ladybird/Libraries/LibCore/EventLoop.cpp
Zaggy1024 2a5a9d2103 LibCore: Add a thread-safe weak link for EventLoop
Since the event loop has a very specifically scoped lifetime, we can't
ensure that it outlives threads that hold a reference to it without
blocking the thread that owns it. In order to make threads use the
event loop safely, we now have an atomically ref-counted
WeakEventLoopReference class that can be passed off to threads to
safely post events/callbacks to it.

Another possibility was to use an RWLock per event loop that each
thread holds a read lock on, while ~EventLoop() uses a write lock to
block and prevent it being destroyed until all its threads exit.
However, media data providers don't receive a signal to exit due to the
GC heap being intentionally leaked, so the process never actually
exits. It would be possible to specifically drop the reference to
PlaybackManager in HTMLMediaElement in order to make those data
providers die on their own, but that doesn't help prevent this problem
in other cases where it may arise.
2025-12-09 16:12:40 -06:00

215 lines
4.9 KiB
C++

/*
* Copyright (c) 2018-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2022, kleines Filmröllchen <malu.bertsch@gmail.com>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Badge.h>
#include <LibCore/Event.h>
#include <LibCore/EventLoop.h>
#include <LibCore/EventLoopImplementation.h>
#include <LibCore/EventReceiver.h>
#include <LibCore/Promise.h>
#include <LibCore/ThreadEventQueue.h>
namespace Core {
namespace {
OwnPtr<Vector<EventLoop&>>& event_loop_stack_uninitialized()
{
thread_local OwnPtr<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 = make<Vector<EventLoop&>>();
return *the_stack;
}
}
EventLoop::EventLoop()
: m_impl(EventLoopManager::the().make_implementation())
{
if (event_loop_stack().is_empty()) {
event_loop_stack().append(*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();
}
}
bool EventLoop::is_running()
{
auto& stack = event_loop_stack_uninitialized();
return stack != nullptr && !stack->is_empty();
}
EventLoop& EventLoop::current()
{
if (event_loop_stack().is_empty())
dbgln("No EventLoop is present, unable to return current one!");
return event_loop_stack().last();
}
NonnullRefPtr<WeakEventLoopReference> EventLoop::current_weak()
{
auto& event_loop = current();
if (!event_loop.m_weak)
event_loop.m_weak = adopt_ref(*new (nothrow) WeakEventLoopReference(event_loop));
return *event_loop.m_weak;
}
void EventLoop::quit(int code)
{
ThreadEventQueue::current().cancel_all_pending_jobs();
m_impl->quit(code);
}
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);
return m_impl->exec();
}
void EventLoop::spin_until(Function<bool()> goal_condition)
{
EventLoopPusher pusher(*this);
while (!goal_condition())
pump();
}
size_t EventLoop::pump(WaitMode mode)
{
return m_impl->pump(mode == WaitMode::WaitForEvents ? EventLoopImplementation::PumpMode::WaitForEvents : EventLoopImplementation::PumpMode::DontWaitForEvents);
}
void EventLoop::add_job(NonnullRefPtr<Promise<NonnullRefPtr<EventReceiver>>> job_promise)
{
ThreadEventQueue::current().add_job(move(job_promise));
}
int EventLoop::register_signal(int signal_number, Function<void(int)> handler)
{
return EventLoopManager::the().register_signal(signal_number, move(handler));
}
void EventLoop::unregister_signal(int handler_id)
{
EventLoopManager::the().unregister_signal(handler_id);
}
intptr_t EventLoop::register_timer(EventReceiver& object, int milliseconds, bool should_reload)
{
return EventLoopManager::the().register_timer(object, milliseconds, should_reload);
}
void EventLoop::unregister_timer(intptr_t timer_id)
{
EventLoopManager::the().unregister_timer(timer_id);
}
void EventLoop::register_notifier(Badge<Notifier>, Notifier& notifier)
{
EventLoopManager::the().register_notifier(notifier);
}
void EventLoop::unregister_notifier(Badge<Notifier>, Notifier& notifier)
{
EventLoopManager::the().unregister_notifier(notifier);
}
void EventLoop::wake()
{
m_impl->wake();
}
void EventLoop::deferred_invoke(Function<void()> invokee)
{
m_impl->deferred_invoke(move(invokee));
}
void deferred_invoke(Function<void()> invokee)
{
EventLoop::current().deferred_invoke(move(invokee));
}
WeakEventLoopReference::WeakEventLoopReference(EventLoop& event_loop)
: m_event_loop(&event_loop)
{
}
void WeakEventLoopReference::revoke()
{
Threading::RWLockLocker<Threading::LockMode::Read> locker { m_lock };
m_event_loop = nullptr;
}
StrongEventLoopReference WeakEventLoopReference::take()
{
return StrongEventLoopReference(*this);
}
StrongEventLoopReference::StrongEventLoopReference(WeakEventLoopReference& event_loop_weak)
{
event_loop_weak.m_lock.lock_read();
m_event_loop_weak = &event_loop_weak;
}
StrongEventLoopReference::~StrongEventLoopReference()
{
m_event_loop_weak->m_lock.unlock();
}
bool StrongEventLoopReference::is_alive() const
{
return m_event_loop_weak->m_event_loop != nullptr;
}
StrongEventLoopReference::operator bool() const
{
return is_alive();
}
EventLoop* StrongEventLoopReference::operator*() const
{
VERIFY(is_alive());
return m_event_loop_weak->m_event_loop;
}
EventLoop* StrongEventLoopReference::operator->() const
{
VERIFY(is_alive());
return m_event_loop_weak->m_event_loop;
}
}