From e3c2393d964e06394fcabeca1e92812e0ce9770e Mon Sep 17 00:00:00 2001 From: ayeteadoe Date: Sat, 22 Nov 2025 15:02:46 -0800 Subject: [PATCH] UI/Qt: Implement register_process for Qt Windows event loop Qt does not use IOCP's in their underlying Windows event loop implementation; however, QWinEventNotifier allows us to register a wait on a process handle that has SYNCHRONIZE access rights. This means an event will be signalled when that process terminates which emits the QWinEventNotifier::activated signal. Co-authored-by: Aliaksandr Kalenik --- UI/Qt/EventLoopImplementationQt.cpp | 62 +++++++++++++++++++++++++++++ UI/Qt/EventLoopImplementationQt.h | 5 +++ 2 files changed, 67 insertions(+) diff --git a/UI/Qt/EventLoopImplementationQt.cpp b/UI/Qt/EventLoopImplementationQt.cpp index 6bab1c3757..767e28272a 100644 --- a/UI/Qt/EventLoopImplementationQt.cpp +++ b/UI/Qt/EventLoopImplementationQt.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,10 @@ #include #include #include +#if defined(AK_OS_WINDOWS) +# include +# include +#endif namespace Ladybird { @@ -33,6 +38,9 @@ static thread_local OwnPtr s_this_thread_data; static HashMap s_thread_data; static Threading::RWLock s_thread_data_lock; static thread_local Optional s_thread_id; +#if defined(AK_OS_WINDOWS) +static Threading::MutexProtected> s_processes; +#endif struct ThreadData { static ThreadData& the() @@ -376,6 +384,60 @@ void EventLoopManagerQt::unregister_signal(int handler_id) info.signal_handlers.remove(remove_signal_number); } +#if defined(AK_OS_WINDOWS) + +void EventLoopManagerQt::register_process(pid_t pid, ESCAPING Function exit_handler) +{ + s_processes.with_locked([&](auto& processes) { + if (processes.contains(pid)) + return; + + HANDLE process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid); + VERIFY(process_handle); + + auto* process = new QWinEventNotifier(process_handle); + QObject::connect(process, &QWinEventNotifier::activated, process, [process, process_id = pid, exit_handler = move(exit_handler), process_handle = process_handle](HANDLE terminated_process_handle) { + if (process_handle != terminated_process_handle) + return; + + s_processes.with_locked([&](auto& processes) { + auto maybe_process = processes.take(process_id); + if (maybe_process.has_value()) + VERIFY(maybe_process.value() == process); + }); + + CloseHandle(process_handle); + process->deleteLater(); + exit_handler(process_id); + }); + + processes.set(pid, process); + }); +} + +void EventLoopManagerQt::unregister_process(pid_t pid) +{ + auto maybe_process = s_processes.with_locked([&](auto& processes) { + return processes.take(pid); + }); + if (!maybe_process.has_value()) + return; + + auto* process = maybe_process.release_value(); + HANDLE process_handle = process->handle(); + if (QThread::currentThread() != process->thread()) { + QMetaObject::invokeMethod(process, [process, process_handle] { + CloseHandle(process_handle); + delete process; }, Qt::QueuedConnection); + return; + } + + CloseHandle(process_handle); + delete process; +} + +#endif + void EventLoopManagerQt::did_post_event() { QCoreApplication::postEvent(m_main_thread_event_target.ptr(), new QtEventLoopManagerEvent(QtEventLoopManagerEvent::process_event_queue_event_type())); diff --git a/UI/Qt/EventLoopImplementationQt.h b/UI/Qt/EventLoopImplementationQt.h index 1b25b16ed5..ad9d9fad51 100644 --- a/UI/Qt/EventLoopImplementationQt.h +++ b/UI/Qt/EventLoopImplementationQt.h @@ -37,6 +37,11 @@ public: virtual int register_signal(int, Function) override; virtual void unregister_signal(int) override; +#if defined(AK_OS_WINDOWS) + virtual void register_process(pid_t, ESCAPING Function exit_handler) override; + virtual void unregister_process(pid_t pid) override; +#endif + void set_main_loop_signal_notifiers(Badge); private: