diff --git a/Userland/Libraries/LibCore/EventLoopImplementationUnix.cpp b/Userland/Libraries/LibCore/EventLoopImplementationUnix.cpp index b10d3c729e0..1fe8843ce77 100644 --- a/Userland/Libraries/LibCore/EventLoopImplementationUnix.cpp +++ b/Userland/Libraries/LibCore/EventLoopImplementationUnix.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,10 @@ namespace { struct ThreadData; class TimeoutSet; -thread_local ThreadData* s_thread_data; +HashMap s_thread_data; +static pthread_rwlock_t s_thread_data_lock_impl; +static pthread_rwlock_t* s_thread_data_lock = nullptr; +thread_local pthread_t s_thread_id; short notification_type_to_poll_events(NotificationType type) { @@ -214,16 +218,41 @@ public: bool should_reload { false }; TimerShouldFireWhenNotVisible fire_when_not_visible { TimerShouldFireWhenNotVisible::No }; WeakPtr owner; + pthread_t owner_thread { 0 }; + Atomic is_being_deleted { false }; }; struct ThreadData { static ThreadData& the() { - if (!s_thread_data) { - // FIXME: Don't leak this. - s_thread_data = new ThreadData; + if (!s_thread_data_lock) { + pthread_rwlock_init(&s_thread_data_lock_impl, nullptr); + s_thread_data_lock = &s_thread_data_lock_impl; } - return *s_thread_data; + + if (s_thread_id == 0) + s_thread_id = pthread_self(); + ThreadData* data = nullptr; + pthread_rwlock_rdlock(&*s_thread_data_lock); + if (!s_thread_data.contains(s_thread_id)) { + // FIXME: Don't leak this. + data = new ThreadData; + pthread_rwlock_unlock(&*s_thread_data_lock); + pthread_rwlock_wrlock(&*s_thread_data_lock); + s_thread_data.set(s_thread_id, data); + } else { + data = s_thread_data.get(s_thread_id).value(); + } + pthread_rwlock_unlock(&*s_thread_data_lock); + return *data; + } + + static ThreadData& for_thread(pthread_t thread_id) + { + pthread_rwlock_rdlock(&*s_thread_data_lock); + auto& result = *s_thread_data.get(thread_id).value(); + pthread_rwlock_unlock(&*s_thread_data_lock); + return result; } ThreadData() @@ -610,6 +639,7 @@ intptr_t EventLoopManagerUnix::register_timer(EventReceiver& object, int millise VERIFY(milliseconds >= 0); auto& thread_data = ThreadData::the(); auto timer = new EventLoopTimer; + timer->owner_thread = s_thread_id; timer->owner = object; timer->interval = Duration::from_milliseconds(milliseconds); timer->reload(MonotonicTime::now_coarse()); @@ -621,11 +651,14 @@ intptr_t EventLoopManagerUnix::register_timer(EventReceiver& object, int millise void EventLoopManagerUnix::unregister_timer(intptr_t timer_id) { - auto& thread_data = ThreadData::the(); auto* timer = bit_cast(timer_id); - if (timer->is_scheduled()) - thread_data.timeouts.unschedule(timer); - delete timer; + auto& thread_data = ThreadData::for_thread(timer->owner_thread); + auto expected = false; + if (timer->is_being_deleted.compare_exchange_strong(expected, true, AK::MemoryOrder::memory_order_acq_rel)) { + if (timer->is_scheduled()) + thread_data.timeouts.unschedule(timer); + delete timer; + } } void EventLoopManagerUnix::register_notifier(Notifier& notifier) @@ -639,11 +672,13 @@ void EventLoopManagerUnix::register_notifier(Notifier& notifier) .events = notification_type_to_poll_events(notifier.type()), .revents = 0, }); + + notifier.set_owner_thread(s_thread_id); } void EventLoopManagerUnix::unregister_notifier(Notifier& notifier) { - auto& thread_data = ThreadData::the(); + auto& thread_data = ThreadData::for_thread(notifier.owner_thread()); auto it = thread_data.notifier_by_ptr.find(¬ifier); VERIFY(it != thread_data.notifier_by_ptr.end()); diff --git a/Userland/Libraries/LibCore/Notifier.h b/Userland/Libraries/LibCore/Notifier.h index eb43bc17177..644c9a7aaf3 100644 --- a/Userland/Libraries/LibCore/Notifier.h +++ b/Userland/Libraries/LibCore/Notifier.h @@ -32,11 +32,15 @@ public: void event(Core::Event&) override; + void set_owner_thread(pthread_t owner_thread) { m_owner_thread = owner_thread; } + pthread_t owner_thread() const { return m_owner_thread; } + private: Notifier(int fd, Type type, EventReceiver* parent = nullptr); int m_fd { -1 }; bool m_is_enabled { false }; + pthread_t m_owner_thread { 0 }; Type m_type { Type::None }; };