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 <kalenik.aliaksandr@gmail.com>
This commit is contained in:
ayeteadoe 2025-11-22 15:02:46 -08:00 committed by Alexander Kalenik
parent 4cb7743915
commit e3c2393d96
2 changed files with 67 additions and 0 deletions

View file

@ -15,6 +15,7 @@
#include <LibCore/System.h>
#include <LibCore/ThreadEventQueue.h>
#include <LibThreading/Mutex.h>
#include <LibThreading/MutexProtected.h>
#include <LibThreading/RWLock.h>
#include <UI/Qt/EventLoopImplementationQt.h>
#include <UI/Qt/EventLoopImplementationQtEventTarget.h>
@ -25,6 +26,10 @@
#include <QSocketNotifier>
#include <QThread>
#include <QTimer>
#if defined(AK_OS_WINDOWS)
# include <AK/Windows.h>
# include <QWinEventNotifier>
#endif
namespace Ladybird {
@ -33,6 +38,9 @@ static thread_local OwnPtr<ThreadData> s_this_thread_data;
static HashMap<pthread_t, ThreadData*> s_thread_data;
static Threading::RWLock s_thread_data_lock;
static thread_local Optional<pthread_t> s_thread_id;
#if defined(AK_OS_WINDOWS)
static Threading::MutexProtected<HashMap<pid_t, QWinEventNotifier*>> 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<void(pid_t)> 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()));

View file

@ -37,6 +37,11 @@ public:
virtual int register_signal(int, Function<void(int)>) override;
virtual void unregister_signal(int) override;
#if defined(AK_OS_WINDOWS)
virtual void register_process(pid_t, ESCAPING Function<void(pid_t)> exit_handler) override;
virtual void unregister_process(pid_t pid) override;
#endif
void set_main_loop_signal_notifiers(Badge<EventLoopImplementationQt>);
private: