mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-08 13:37:10 +09:00
LibCore: Make Timers and Notifiers aware of threads
Previously sharing a Timer/Notifier between threads (or just handing its ownership to another thread) lead to a crash as they are thread-specific. This commit makes it so we can handle mutation (i.e. just deletion or unregistering) in a thread-safe and lockfree manner.
This commit is contained in:
parent
5cc90f848f
commit
96c7e83345
Notes:
sideshowbarker
2024-07-16 23:44:30 +09:00
Author: https://github.com/alimpfard
Commit: 96c7e83345
Pull-request: https://github.com/SerenityOS/serenity/pull/24313
Reviewed-by: https://github.com/Zaggy1024
2 changed files with 49 additions and 10 deletions
|
@ -16,6 +16,7 @@
|
|||
#include <LibCore/Socket.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibCore/ThreadEventQueue.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -25,7 +26,10 @@ namespace {
|
|||
struct ThreadData;
|
||||
class TimeoutSet;
|
||||
|
||||
thread_local ThreadData* s_thread_data;
|
||||
HashMap<pthread_t, ThreadData*> 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<EventReceiver> owner;
|
||||
pthread_t owner_thread { 0 };
|
||||
Atomic<bool> 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<EventLoopTimer*>(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());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue