LibCore: Add thread-safe weak deferred_invoke()

Add a thread-safe deferred_invoke() API on WeakEventLoopReference that
queues work onto the owning thread's event queue and wakes that thread
via EventLoopManager hooks. This avoids calling wake() from foreign
threads during teardown.

Implement current_thread_handle()/wake_thread() in each backend and
track per-thread data so handles are validated before waking:

- Unix: wake via per-thread wake pipe
- Windows: wake via thread wake event
- macOS: wake via stored CFRunLoopRef
- Qt: wake via event target or QEventLoop::wakeUp()
- Android: wake via stored ALooper
This commit is contained in:
Andreas Kling
2026-01-23 17:16:40 +01:00
committed by Andreas Kling
parent 367296fce6
commit 96ce468b60
Notes: github-actions[bot] 2026-02-06 11:08:22 +00:00
13 changed files with 220 additions and 0 deletions

View File

@@ -10,6 +10,7 @@
#include <AK/Assertions.h>
#include <AK/Diagnostics.h>
#include <AK/HashMap.h>
#include <AK/HashTable.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/Windows.h>
#include <LibCore/EventLoopImplementationWindows.h>
@@ -17,6 +18,7 @@
#include <LibCore/ThreadEventQueue.h>
#include <LibCore/Timer.h>
#include <LibThreading/Mutex.h>
#include <LibThreading/RWLock.h>
struct OwnHandle {
HANDLE handle = NULL;
@@ -62,6 +64,10 @@ constexpr bool IsHashCompatible<HANDLE, OwnHandle> = true;
namespace Core {
struct ThreadData;
static HashTable<ThreadData*> s_thread_data_by_ptr;
static Threading::RWLock s_thread_data_lock;
enum class CompletionType : u8 {
Wake,
Timer,
@@ -110,9 +116,23 @@ struct ThreadData {
return nullptr;
}
static ThreadData* for_handle(EventLoopThreadHandle handle)
{
if (handle == 0)
return nullptr;
auto* ptr = reinterpret_cast<ThreadData*>(handle);
Threading::RWLockLocker<Threading::LockMode::Read> locker(s_thread_data_lock);
if (!s_thread_data_by_ptr.contains(ptr))
return nullptr;
return ptr;
}
ThreadData()
: wake_data(make<EventLoopWake>())
{
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
s_thread_data_by_ptr.set(this);
wake_data->type = CompletionType::Wake;
wake_data->wait_event.handle = CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -129,6 +149,8 @@ struct ThreadData {
{
NTSTATUS status = g_system.NtCancelWaitCompletionPacket(wake_data->wait_packet.handle, TRUE);
VERIFY(NT_SUCCESS(status));
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
s_thread_data_by_ptr.remove(this);
}
OwnHandle iocp;
@@ -355,6 +377,20 @@ void EventLoopManagerWindows::did_post_event()
{
}
EventLoopThreadHandle EventLoopManagerWindows::current_thread_handle()
{
auto* thread_data = ThreadData::the();
return reinterpret_cast<EventLoopThreadHandle>(thread_data);
}
void EventLoopManagerWindows::wake_thread(EventLoopThreadHandle handle)
{
auto* thread_data = ThreadData::for_handle(handle);
if (!thread_data)
return;
SetEvent(thread_data->wake_data->wait_event.handle);
}
NonnullOwnPtr<EventLoopImplementation> EventLoopManagerWindows::make_implementation()
{
return make<EventLoopImplementationWindows>();