LibCore: Expose register_process and implement for Windows event loop

This is the closest Windows equivalent to integrating process exit
handlers into the event loop.

Linux could also integrate register_process() into the poll() based
Unix event loop via the SYS_pidfd_open syscall; however, macOS requires
a kqueue. So for now register_process will only be used by Windows to
implement WebView::ProcessMonitor.

Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
This commit is contained in:
ayeteadoe
2025-11-22 15:02:26 -08:00
committed by Alexander Kalenik
parent 433ff624b8
commit 4cb7743915
Notes: github-actions[bot] 2026-04-12 14:09:25 +00:00
5 changed files with 95 additions and 0 deletions

View File

@@ -141,6 +141,16 @@ void EventLoop::unregister_notifier(Badge<Notifier>, Notifier& notifier)
EventLoopManager::the().unregister_notifier(notifier);
}
void EventLoop::register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler)
{
EventLoopManager::the().register_process(pid, move(exit_handler));
}
void EventLoop::unregister_process(pid_t pid)
{
EventLoopManager::the().unregister_process(pid);
}
void EventLoop::wake()
{
m_impl->wake();

View File

@@ -88,6 +88,10 @@ public:
static int register_signal(int signo, ESCAPING Function<void(int)> handler);
static void unregister_signal(int handler_id);
// Invokes the specified handler when the process exits
static void register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler);
static void unregister_process(pid_t pid);
static bool is_running();
static EventLoop& current();
static NonnullRefPtr<WeakEventLoopReference> current_weak();

View File

@@ -36,6 +36,9 @@ public:
virtual int register_signal(int signal_number, Function<void(int)> handler) = 0;
virtual void unregister_signal(int handler_id) = 0;
virtual void register_process([[maybe_unused]] pid_t pid, [[maybe_unused]] ESCAPING Function<void(pid_t)> exit_handler) { VERIFY_NOT_REACHED(); }
virtual void unregister_process([[maybe_unused]] pid_t pid) { VERIFY_NOT_REACHED(); }
protected:
EventLoopManager();
};

View File

@@ -17,6 +17,7 @@
#include <LibCore/ThreadEventQueue.h>
#include <LibCore/Timer.h>
#include <LibThreading/Mutex.h>
#include <LibThreading/MutexProtected.h>
struct OwnHandle {
HANDLE handle = NULL;
@@ -66,6 +67,7 @@ enum class CompletionType : u8 {
Wake,
Timer,
Notifer,
Process,
};
struct CompletionPacket {
@@ -101,6 +103,15 @@ struct EventLoopNotifier final : CompletionPacket {
OwnHandle wait_event;
};
struct EventLoopProcess final : CompletionPacket {
~EventLoopProcess() = default;
OwnHandle process;
pid_t pid;
Function<void(pid_t)> exit_handler;
OwnHandle jobobject;
};
struct ThreadData {
static ThreadData* the()
{
@@ -141,6 +152,8 @@ struct ThreadData {
NonnullOwnPtr<EventLoopWake> wake_data;
};
static Threading::MutexProtected<HashMap<pid_t, NonnullOwnPtr<EventLoopProcess>>> s_processes;
EventLoopImplementationWindows::EventLoopImplementationWindows()
: m_wake_event(ThreadData::the()->wake_data->wait_event.handle)
{
@@ -211,6 +224,21 @@ size_t EventLoopImplementationWindows::pump(PumpMode pump_mode)
VERIFY(NT_SUCCESS(status));
continue;
}
if (packet->type == CompletionType::Process) {
auto* process_data = static_cast<EventLoopProcess*>(packet);
pid_t const process_id = process_data->pid;
// NOTE: This may seem like the incorrect parameter, but https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_associate_completion_port
// states that this field represents the event type indicator
DWORD const event_type = entry.dwNumberOfBytesTransferred;
if (reinterpret_cast<intptr_t>(entry.lpOverlapped) == process_id && (event_type == JOB_OBJECT_MSG_EXIT_PROCESS || event_type == JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS)) {
Optional<NonnullOwnPtr<EventLoopProcess>> owned_process = s_processes.with_locked([&](auto& processes) {
return processes.take(process_id);
});
if (owned_process.has_value())
owned_process.release_value()->exit_handler(process_id);
}
continue;
}
VERIFY_NOT_REACHED();
}
} else {
@@ -352,6 +380,53 @@ void EventLoopManagerWindows::unregister_signal([[maybe_unused]] int handler_id)
VERIFY_NOT_REACHED();
}
void EventLoopManagerWindows::register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler)
{
auto* thread_data = ThreadData::the();
VERIFY(thread_data);
s_processes.with_locked([&](auto& processes) {
if (processes.contains(pid))
return;
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
VERIFY(process_handle);
HANDLE job_object_handle = CreateJobObject(nullptr, nullptr);
VERIFY(job_object_handle);
BOOL succeeded = AssignProcessToJobObject(job_object_handle, process_handle);
VERIFY(succeeded);
auto process_data = make<EventLoopProcess>();
process_data->type = CompletionType::Process;
process_data->process.handle = process_handle;
process_data->pid = pid;
process_data->exit_handler = move(exit_handler);
process_data->jobobject.handle = job_object_handle;
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp = { .CompletionKey = process_data.ptr(), .CompletionPort = thread_data->iocp.handle };
succeeded = SetInformationJobObject(job_object_handle, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
VERIFY(succeeded);
processes.set(pid, move(process_data));
});
}
void EventLoopManagerWindows::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_data = maybe_process.release_value();
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp = { .CompletionKey = process_data, .CompletionPort = nullptr };
BOOL succeeded = SetInformationJobObject(process_data->jobobject.handle, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
VERIFY(succeeded);
}
void EventLoopManagerWindows::did_post_event()
{
}

View File

@@ -27,6 +27,9 @@ public:
virtual int register_signal(int signal_number, Function<void(int)> handler) override;
virtual void unregister_signal(int handler_id) override;
virtual void register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler) override;
virtual void unregister_process(pid_t pid) override;
};
class EventLoopImplementationWindows final : public EventLoopImplementation {