Files
ladybird/Libraries/LibCore/ThreadEventQueue.cpp
R-Goc 02bb892d7a LibThreading/LibSync: Split out sync primitives
This commit splits out synchronization primitives from LibThreading into
LibSync. This is because LibThreading depends on LibCore, while LibCore
needs the synchronization primitives from LibThreading. This worked
while they were header only, but when I tried to add an implementation
file it ran into the circular dependency. To abstract away the pthread
implementation using cpp files is necessary so the synchronization
primitives were moved to a separate library.
2026-05-08 18:58:35 -05:00

141 lines
3.7 KiB
C++

/*
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Vector.h>
#include <LibCore/EventLoopImplementation.h>
#include <LibCore/EventReceiver.h>
#include <LibCore/Promise.h>
#include <LibCore/ThreadEventQueue.h>
#include <LibSync/Mutex.h>
#include <LibSync/Once.h>
#include <errno.h>
#include <pthread.h>
namespace Core {
struct ThreadEventQueue::Private {
struct QueuedEvent {
AK_MAKE_NONCOPYABLE(QueuedEvent);
AK_MAKE_DEFAULT_MOVABLE(QueuedEvent);
public:
QueuedEvent(RefPtr<EventReceiver> const& receiver, Event::Type event_type)
: receiver(receiver)
, event_type(event_type)
{
}
QueuedEvent(Function<void()>&& invokee)
: m_invokee(move(invokee))
, event_type(Event::Type::DeferredInvoke)
{
}
~QueuedEvent() = default;
WeakPtr<EventReceiver> receiver;
Function<void()> m_invokee;
u8 event_type { Event::Type::Invalid };
};
Sync::Mutex mutex;
Vector<QueuedEvent> queued_events;
};
static pthread_key_t s_current_thread_event_queue_key;
static Sync::OnceFlag s_current_thread_event_queue_key_once {};
ThreadEventQueue* ThreadEventQueue::current_or_null()
{
Sync::call_once(s_current_thread_event_queue_key_once, [] {
pthread_key_create(&s_current_thread_event_queue_key, [](void* value) {
if (value)
delete static_cast<ThreadEventQueue*>(value);
});
});
return static_cast<ThreadEventQueue*>(pthread_getspecific(s_current_thread_event_queue_key));
}
ThreadEventQueue& ThreadEventQueue::current()
{
auto* ptr = current_or_null();
if (!ptr) {
ptr = new ThreadEventQueue;
pthread_setspecific(s_current_thread_event_queue_key, ptr);
}
return *ptr;
}
ThreadEventQueue::ThreadEventQueue()
: m_private(make<Private>())
{
}
ThreadEventQueue::~ThreadEventQueue() = default;
void ThreadEventQueue::post_event(Core::EventReceiver* receiver, Core::Event::Type event_type)
{
{
Sync::MutexLocker lock(m_private->mutex);
m_private->queued_events.empend(receiver, event_type);
}
Core::EventLoopManager::the().did_post_event();
}
void ThreadEventQueue::deferred_invoke(Function<void()>&& invokee)
{
{
Sync::MutexLocker lock(m_private->mutex);
m_private->queued_events.empend(move(invokee));
}
Core::EventLoopManager::the().did_post_event();
}
size_t ThreadEventQueue::process()
{
decltype(m_private->queued_events) events;
{
Sync::MutexLocker locker(m_private->mutex);
events = move(m_private->queued_events);
}
for (auto& queued_event : events) {
if (auto receiver = queued_event.receiver.strong_ref()) {
switch (queued_event.event_type) {
case Event::Type::Timer: {
TimerEvent timer_event;
receiver->dispatch_event(timer_event);
break;
}
case Event::Type::NotifierActivation: {
NotifierActivationEvent notifier_activation_event;
receiver->dispatch_event(notifier_activation_event);
break;
}
default:
VERIFY_NOT_REACHED();
}
} else {
if (queued_event.event_type == Event::Type::DeferredInvoke) {
queued_event.m_invokee();
} else {
// Receiver gone, drop the event.
}
}
}
return events.size();
}
bool ThreadEventQueue::has_pending_events() const
{
Sync::MutexLocker locker(m_private->mutex);
return !m_private->queued_events.is_empty();
}
}