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:
parent
367296fce6
commit
96ce468b60
13 changed files with 220 additions and 0 deletions
|
|
@ -36,6 +36,8 @@ Vector<EventLoop&>& event_loop_stack()
|
|||
EventLoop::EventLoop()
|
||||
: m_impl(EventLoopManager::the().make_implementation())
|
||||
{
|
||||
m_thread_event_queue = &ThreadEventQueue::current();
|
||||
m_thread_handle = EventLoopManager::the().current_thread_handle();
|
||||
if (event_loop_stack().is_empty()) {
|
||||
event_loop_stack().append(*this);
|
||||
}
|
||||
|
|
@ -165,12 +167,16 @@ void deferred_invoke(Function<void()> invokee)
|
|||
WeakEventLoopReference::WeakEventLoopReference(EventLoop& event_loop)
|
||||
: m_event_loop(&event_loop)
|
||||
{
|
||||
m_thread_event_queue = event_loop.m_thread_event_queue;
|
||||
m_thread_handle = event_loop.m_thread_handle;
|
||||
}
|
||||
|
||||
void WeakEventLoopReference::revoke()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker { m_lock };
|
||||
m_event_loop = nullptr;
|
||||
m_thread_event_queue = nullptr;
|
||||
m_thread_handle = 0;
|
||||
}
|
||||
|
||||
StrongEventLoopReference WeakEventLoopReference::take()
|
||||
|
|
@ -178,6 +184,20 @@ StrongEventLoopReference WeakEventLoopReference::take()
|
|||
return StrongEventLoopReference(*this);
|
||||
}
|
||||
|
||||
bool WeakEventLoopReference::deferred_invoke(Function<void()> invokee)
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Read> locker { m_lock };
|
||||
if (!m_event_loop || !m_thread_event_queue || m_thread_handle == 0) {
|
||||
// Catch use-after-revoke bugs early in debug builds.
|
||||
VERIFY(m_event_loop != nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_thread_event_queue->deferred_invoke(move(invokee));
|
||||
EventLoopManager::the().wake_thread(m_thread_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
StrongEventLoopReference::StrongEventLoopReference(WeakEventLoopReference& event_loop_weak)
|
||||
{
|
||||
event_loop_weak.m_lock.lock_read();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <AK/Swift.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibCore/Event.h>
|
||||
#include <LibCore/EventLoopImplementation.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibThreading/RWLock.h>
|
||||
|
||||
|
|
@ -49,6 +50,7 @@ class EventLoop {
|
|||
|
||||
private:
|
||||
friend struct EventLoopPusher;
|
||||
friend class WeakEventLoopReference;
|
||||
|
||||
public:
|
||||
enum class WaitMode {
|
||||
|
|
@ -99,6 +101,8 @@ public:
|
|||
private:
|
||||
NonnullOwnPtr<EventLoopImplementation> m_impl;
|
||||
RefPtr<WeakEventLoopReference> m_weak;
|
||||
ThreadEventQueue* m_thread_event_queue { nullptr };
|
||||
EventLoopThreadHandle m_thread_handle { 0 };
|
||||
} SWIFT_UNSAFE_REFERENCE;
|
||||
|
||||
class StrongEventLoopReference;
|
||||
|
|
@ -106,6 +110,7 @@ class StrongEventLoopReference;
|
|||
class WeakEventLoopReference : public AtomicRefCounted<WeakEventLoopReference> {
|
||||
public:
|
||||
StrongEventLoopReference take();
|
||||
bool deferred_invoke(ESCAPING Function<void()>);
|
||||
|
||||
private:
|
||||
friend class EventLoop;
|
||||
|
|
@ -117,6 +122,8 @@ private:
|
|||
|
||||
EventLoop* m_event_loop;
|
||||
Threading::RWLock m_lock;
|
||||
ThreadEventQueue* m_thread_event_queue { nullptr };
|
||||
EventLoopThreadHandle m_thread_handle { 0 };
|
||||
};
|
||||
|
||||
class StrongEventLoopReference {
|
||||
|
|
|
|||
|
|
@ -7,12 +7,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/Forward.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class EventLoopImplementation;
|
||||
class ThreadEventQueue;
|
||||
using EventLoopThreadHandle = FlatPtr;
|
||||
|
||||
class EventLoopManager {
|
||||
public:
|
||||
|
|
@ -30,6 +32,8 @@ public:
|
|||
virtual void unregister_notifier(Notifier&) = 0;
|
||||
|
||||
virtual void did_post_event() = 0;
|
||||
virtual EventLoopThreadHandle current_thread_handle() = 0;
|
||||
virtual void wake_thread(EventLoopThreadHandle) = 0;
|
||||
|
||||
// FIXME: These APIs only exist for obscure use-cases inside SerenityOS. Try to get rid of them.
|
||||
virtual int register_signal(int signal_number, Function<void(int)> handler) = 0;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/BinaryHeap.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/Singleton.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <AK/Time.h>
|
||||
|
|
@ -30,6 +31,7 @@ struct ThreadData;
|
|||
class TimeoutSet;
|
||||
|
||||
HashMap<pthread_t, ThreadData*> s_thread_data;
|
||||
HashTable<ThreadData*> s_thread_data_by_ptr;
|
||||
Threading::RWLock s_thread_data_lock;
|
||||
thread_local pthread_t s_thread_id;
|
||||
thread_local OwnPtr<ThreadData> s_this_thread_data;
|
||||
|
|
@ -232,6 +234,7 @@ struct ThreadData {
|
|||
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data.set(s_thread_id, s_this_thread_data.ptr());
|
||||
s_thread_data_by_ptr.set(s_this_thread_data.ptr());
|
||||
} else {
|
||||
data = s_this_thread_data.ptr();
|
||||
}
|
||||
|
|
@ -244,6 +247,17 @@ struct ThreadData {
|
|||
return s_thread_data.get(thread_id).value_or(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()
|
||||
{
|
||||
pid = getpid();
|
||||
|
|
@ -265,6 +279,7 @@ struct ThreadData {
|
|||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data.remove(s_thread_id);
|
||||
s_thread_data_by_ptr.remove(this);
|
||||
}
|
||||
|
||||
Threading::Mutex mutex;
|
||||
|
|
@ -676,6 +691,20 @@ void EventLoopManagerUnix::did_post_event()
|
|||
{
|
||||
}
|
||||
|
||||
EventLoopThreadHandle EventLoopManagerUnix::current_thread_handle()
|
||||
{
|
||||
return reinterpret_cast<EventLoopThreadHandle>(&ThreadData::the());
|
||||
}
|
||||
|
||||
void EventLoopManagerUnix::wake_thread(EventLoopThreadHandle handle)
|
||||
{
|
||||
auto* thread_data = ThreadData::for_handle(handle);
|
||||
if (!thread_data)
|
||||
return;
|
||||
int wake_event = 0;
|
||||
(void)Core::System::write(thread_data->wake_pipe_fds[1], { &wake_event, sizeof(wake_event) });
|
||||
}
|
||||
|
||||
EventLoopManagerUnix::~EventLoopManagerUnix() = default;
|
||||
|
||||
NonnullOwnPtr<EventLoopImplementation> EventLoopManagerUnix::make_implementation()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ public:
|
|||
virtual void unregister_notifier(Notifier&) override;
|
||||
|
||||
virtual void did_post_event() override;
|
||||
virtual EventLoopThreadHandle current_thread_handle() override;
|
||||
virtual void wake_thread(EventLoopThreadHandle) override;
|
||||
|
||||
virtual int register_signal(int signal_number, Function<void(int)> handler) override;
|
||||
virtual void unregister_signal(int handler_id) override;
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ public:
|
|||
virtual void unregister_notifier(Notifier&) override;
|
||||
|
||||
virtual void did_post_event() override;
|
||||
virtual EventLoopThreadHandle current_thread_handle() override;
|
||||
virtual void wake_thread(EventLoopThreadHandle) override;
|
||||
|
||||
virtual int register_signal(int signal_number, Function<void(int)> handler) override;
|
||||
virtual void unregister_signal(int handler_id) override;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@
|
|||
|
||||
#include "ALooperEventLoopImplementation.h"
|
||||
#include "JNIHelpers.h"
|
||||
#include <AK/HashTable.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/Notifier.h>
|
||||
#include <LibCore/ThreadEventQueue.h>
|
||||
#include <LibThreading/RWLock.h>
|
||||
#include <android/log.h>
|
||||
#include <android/looper.h>
|
||||
#include <fcntl.h>
|
||||
|
|
@ -16,12 +18,34 @@
|
|||
|
||||
namespace Ladybird {
|
||||
|
||||
static Threading::RWLock s_thread_data_lock;
|
||||
static HashTable<EventLoopThreadData*> s_thread_data_by_ptr;
|
||||
|
||||
EventLoopThreadData& EventLoopThreadData::the()
|
||||
{
|
||||
static thread_local EventLoopThreadData s_thread_data { {}, {}, &Core::ThreadEventQueue::current() };
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data_by_ptr.set(&s_thread_data);
|
||||
return s_thread_data;
|
||||
}
|
||||
|
||||
EventLoopThreadData::~EventLoopThreadData()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data_by_ptr.remove(this);
|
||||
}
|
||||
|
||||
EventLoopThreadData* EventLoopThreadData::for_handle(Core::EventLoopThreadHandle handle)
|
||||
{
|
||||
if (handle == 0)
|
||||
return nullptr;
|
||||
auto* ptr = reinterpret_cast<EventLoopThreadData*>(handle);
|
||||
Threading::RWLockLocker<Threading::LockMode::Read> locker(s_thread_data_lock);
|
||||
if (!s_thread_data_by_ptr.contains(ptr))
|
||||
return nullptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static ALooperEventLoopImplementation& current_impl()
|
||||
{
|
||||
return as<ALooperEventLoopImplementation>(Core::EventLoop::current().impl());
|
||||
|
|
@ -127,6 +151,19 @@ void ALooperEventLoopManager::did_post_event()
|
|||
(void)write(m_pipe[1], &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
Core::EventLoopThreadHandle ALooperEventLoopManager::current_thread_handle()
|
||||
{
|
||||
return reinterpret_cast<Core::EventLoopThreadHandle>(&EventLoopThreadData::the());
|
||||
}
|
||||
|
||||
void ALooperEventLoopManager::wake_thread(Core::EventLoopThreadHandle handle)
|
||||
{
|
||||
auto* thread_data = EventLoopThreadData::for_handle(handle);
|
||||
if (!thread_data || !thread_data->looper)
|
||||
return;
|
||||
ALooper_wake(thread_data->looper);
|
||||
}
|
||||
|
||||
int looper_callback(int fd, int events, void* data)
|
||||
{
|
||||
auto& manager = *static_cast<ALooperEventLoopManager*>(data);
|
||||
|
|
@ -146,6 +183,7 @@ ALooperEventLoopImplementation::ALooperEventLoopImplementation()
|
|||
, m_thread_data(&EventLoopThreadData::the())
|
||||
{
|
||||
ALooper_acquire(m_event_loop);
|
||||
m_thread_data->looper = m_event_loop;
|
||||
}
|
||||
|
||||
ALooperEventLoopImplementation::~ALooperEventLoopImplementation()
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ public:
|
|||
virtual void unregister_notifier(Core::Notifier&) override;
|
||||
|
||||
virtual void did_post_event() override;
|
||||
virtual Core::EventLoopThreadHandle current_thread_handle() override;
|
||||
virtual void wake_thread(Core::EventLoopThreadHandle) override;
|
||||
|
||||
Function<void()> on_did_post_event;
|
||||
|
||||
|
|
@ -53,10 +55,13 @@ struct TimerData {
|
|||
|
||||
struct EventLoopThreadData {
|
||||
static EventLoopThreadData& the();
|
||||
static EventLoopThreadData* for_handle(Core::EventLoopThreadHandle);
|
||||
~EventLoopThreadData();
|
||||
|
||||
HashMap<long, TimerData> timers;
|
||||
HashTable<Core::Notifier*> notifiers;
|
||||
Core::ThreadEventQueue* thread_queue = nullptr;
|
||||
ALooper* looper { nullptr };
|
||||
};
|
||||
|
||||
class ALooperEventLoopImplementation : public Core::EventLoopImplementation {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ public:
|
|||
virtual void unregister_notifier(Core::Notifier&) override;
|
||||
|
||||
virtual void did_post_event() override;
|
||||
virtual Core::EventLoopThreadHandle current_thread_handle() override;
|
||||
virtual void wake_thread(Core::EventLoopThreadHandle) override;
|
||||
|
||||
virtual int register_signal(int, Function<void(int)>) override;
|
||||
virtual void unregister_signal(int) override;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/IDAllocator.h>
|
||||
#include <AK/Singleton.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
|
|
@ -27,6 +28,7 @@ namespace Ladybird {
|
|||
struct ThreadData;
|
||||
static thread_local OwnPtr<ThreadData> s_this_thread_data;
|
||||
static HashMap<pthread_t, ThreadData*> s_thread_data;
|
||||
static HashTable<ThreadData*> s_thread_data_by_ptr;
|
||||
static thread_local pthread_t s_thread_id;
|
||||
static Threading::RWLock s_thread_data_lock;
|
||||
|
||||
|
|
@ -37,8 +39,10 @@ struct ThreadData {
|
|||
s_thread_id = pthread_self();
|
||||
if (!s_this_thread_data) {
|
||||
s_this_thread_data = make<ThreadData>();
|
||||
s_this_thread_data->run_loop = CFRunLoopGetCurrent();
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data.set(s_thread_id, s_this_thread_data);
|
||||
s_thread_data_by_ptr.set(s_this_thread_data.ptr());
|
||||
}
|
||||
return *s_this_thread_data;
|
||||
}
|
||||
|
|
@ -49,10 +53,22 @@ struct ThreadData {
|
|||
return s_thread_data.get(thread_id).value_or(nullptr);
|
||||
}
|
||||
|
||||
static ThreadData* for_handle(Core::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()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data.remove(s_thread_id);
|
||||
s_thread_data_by_ptr.remove(this);
|
||||
}
|
||||
|
||||
IDAllocator timer_id_allocator;
|
||||
|
|
@ -63,6 +79,8 @@ struct ThreadData {
|
|||
CFRunLoopRef run_loop { nullptr };
|
||||
};
|
||||
HashMap<Core::Notifier*, NotifierState> notifiers;
|
||||
CFRunLoopRef run_loop { nullptr };
|
||||
CFRunLoopSourceRef deferred_source { nullptr };
|
||||
};
|
||||
|
||||
class SignalHandlers : public RefCounted<SignalHandlers> {
|
||||
|
|
@ -251,6 +269,7 @@ struct EventLoopImplementationMacOS::Impl {
|
|||
EventLoopImplementationMacOS::EventLoopImplementationMacOS()
|
||||
: m_impl(make<Impl>(*this))
|
||||
{
|
||||
ThreadData::the().deferred_source = m_impl->deferred_source;
|
||||
}
|
||||
|
||||
EventLoopImplementationMacOS::~EventLoopImplementationMacOS() = default;
|
||||
|
|
@ -391,6 +410,21 @@ void EventLoopManagerMacOS::did_post_event()
|
|||
CFRunLoopWakeUp(CFRunLoopGetCurrent());
|
||||
}
|
||||
|
||||
Core::EventLoopThreadHandle EventLoopManagerMacOS::current_thread_handle()
|
||||
{
|
||||
return reinterpret_cast<Core::EventLoopThreadHandle>(&ThreadData::the());
|
||||
}
|
||||
|
||||
void EventLoopManagerMacOS::wake_thread(Core::EventLoopThreadHandle handle)
|
||||
{
|
||||
auto* thread_data = ThreadData::for_handle(handle);
|
||||
if (!thread_data || !thread_data->run_loop)
|
||||
return;
|
||||
if (thread_data->deferred_source)
|
||||
CFRunLoopSourceSignal(thread_data->deferred_source);
|
||||
CFRunLoopWakeUp(thread_data->run_loop);
|
||||
}
|
||||
|
||||
static void handle_signal(CFFileDescriptorRef f, CFOptionFlags callback_types, void* info)
|
||||
{
|
||||
VERIFY(callback_types & kCFFileDescriptorReadCallBack);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/IDAllocator.h>
|
||||
#include <AK/Singleton.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
|
|
@ -31,6 +32,7 @@ namespace Ladybird {
|
|||
struct ThreadData;
|
||||
static thread_local OwnPtr<ThreadData> s_this_thread_data;
|
||||
static HashMap<pthread_t, ThreadData*> s_thread_data;
|
||||
static HashTable<ThreadData*> s_thread_data_by_ptr;
|
||||
static Threading::RWLock s_thread_data_lock;
|
||||
static thread_local Optional<pthread_t> s_thread_id;
|
||||
|
||||
|
|
@ -43,6 +45,7 @@ struct ThreadData {
|
|||
s_this_thread_data = make<ThreadData>();
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data.set(s_thread_id.value(), s_this_thread_data.ptr());
|
||||
s_thread_data_by_ptr.set(s_this_thread_data.ptr());
|
||||
}
|
||||
return *s_this_thread_data;
|
||||
}
|
||||
|
|
@ -53,14 +56,28 @@ struct ThreadData {
|
|||
return s_thread_data.get(thread_id).value_or(nullptr);
|
||||
}
|
||||
|
||||
static ThreadData* for_handle(Core::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()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> locker(s_thread_data_lock);
|
||||
s_thread_data.remove(s_thread_id.value());
|
||||
s_thread_data_by_ptr.remove(this);
|
||||
}
|
||||
|
||||
Threading::Mutex mutex;
|
||||
HashMap<Core::Notifier*, NonnullOwnPtr<QSocketNotifier>> notifiers;
|
||||
QEventLoop* event_loop { nullptr };
|
||||
bool is_main_loop { false };
|
||||
};
|
||||
|
||||
class QtEventLoopManagerEvent final : public QEvent {
|
||||
|
|
@ -208,6 +225,7 @@ static void dispatch_signal(int signal_number)
|
|||
EventLoopImplementationQt::EventLoopImplementationQt()
|
||||
: m_event_loop(make<QEventLoop>())
|
||||
{
|
||||
ThreadData::the().event_loop = m_event_loop.ptr();
|
||||
}
|
||||
|
||||
EventLoopImplementationQt::~EventLoopImplementationQt() = default;
|
||||
|
|
@ -258,6 +276,9 @@ void EventLoopImplementationQt::set_main_loop()
|
|||
|
||||
auto& event_loop_manager = static_cast<EventLoopManagerQt&>(Core::EventLoopManager::the());
|
||||
event_loop_manager.set_main_loop_signal_notifiers({});
|
||||
|
||||
auto& thread_data = ThreadData::the();
|
||||
thread_data.is_main_loop = true;
|
||||
}
|
||||
|
||||
static void qt_timer_fired(Core::EventReceiver& object)
|
||||
|
|
@ -381,6 +402,24 @@ void EventLoopManagerQt::did_post_event()
|
|||
QCoreApplication::postEvent(m_main_thread_event_target.ptr(), new QtEventLoopManagerEvent(QtEventLoopManagerEvent::process_event_queue_event_type()));
|
||||
}
|
||||
|
||||
Core::EventLoopThreadHandle EventLoopManagerQt::current_thread_handle()
|
||||
{
|
||||
return reinterpret_cast<Core::EventLoopThreadHandle>(&ThreadData::the());
|
||||
}
|
||||
|
||||
void EventLoopManagerQt::wake_thread(Core::EventLoopThreadHandle handle)
|
||||
{
|
||||
auto* thread_data = ThreadData::for_handle(handle);
|
||||
if (!thread_data)
|
||||
return;
|
||||
if (thread_data->is_main_loop) {
|
||||
QCoreApplication::postEvent(m_main_thread_event_target.ptr(), new QtEventLoopManagerEvent(QtEventLoopManagerEvent::process_event_queue_event_type()));
|
||||
return;
|
||||
}
|
||||
if (thread_data->event_loop)
|
||||
thread_data->event_loop->wakeUp();
|
||||
}
|
||||
|
||||
bool EventLoopManagerQt::event_target_received_event(Badge<EventLoopImplementationQtEventTarget>, QEvent* event)
|
||||
{
|
||||
if (event->type() == QtEventLoopManagerEvent::process_event_queue_event_type()) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ public:
|
|||
virtual void unregister_notifier(Core::Notifier&) override;
|
||||
|
||||
virtual void did_post_event() override;
|
||||
virtual Core::EventLoopThreadHandle current_thread_handle() override;
|
||||
virtual void wake_thread(Core::EventLoopThreadHandle) override;
|
||||
static bool event_target_received_event(Badge<EventLoopImplementationQtEventTarget>, QEvent* event);
|
||||
|
||||
virtual int register_signal(int, Function<void(int)>) override;
|
||||
|
|
|
|||
Loading…
Reference in a new issue