Skip to content

Commit 465dfce

Browse files
committed
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.
1 parent a2e52db commit 465dfce

File tree

5 files changed

+86
-0
lines changed

5 files changed

+86
-0
lines changed

Libraries/LibCore/EventLoop.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,16 @@ void EventLoop::unregister_notifier(Badge<Notifier>, Notifier& notifier)
137137
EventLoopManager::the().unregister_notifier(notifier);
138138
}
139139

140+
void EventLoop::register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler)
141+
{
142+
EventLoopManager::the().register_process(pid, move(exit_handler));
143+
}
144+
145+
void EventLoop::unregister_process(pid_t pid)
146+
{
147+
EventLoopManager::the().unregister_process(pid);
148+
}
149+
140150
void EventLoop::wake()
141151
{
142152
m_impl->wake();

Libraries/LibCore/EventLoop.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ class EventLoop {
8787
static int register_signal(int signo, ESCAPING Function<void(int)> handler);
8888
static void unregister_signal(int handler_id);
8989

90+
// Invokes the specified handler when the process exits
91+
static void register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler);
92+
static void unregister_process(pid_t pid);
93+
9094
static bool is_running();
9195
static EventLoop& current();
9296

Libraries/LibCore/EventLoopImplementation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class EventLoopManager {
3535
virtual int register_signal(int signal_number, Function<void(int)> handler) = 0;
3636
virtual void unregister_signal(int handler_id) = 0;
3737

38+
virtual void register_process([[maybe_unused]] pid_t pid, [[maybe_unused]] ESCAPING Function<void(pid_t)> exit_handler) { VERIFY_NOT_REACHED(); }
39+
virtual void unregister_process([[maybe_unused]] pid_t pid) { VERIFY_NOT_REACHED(); }
40+
3841
protected:
3942
EventLoopManager();
4043
};

Libraries/LibCore/EventLoopImplementationWindows.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ enum class CompletionType : u8 {
6666
Wake,
6767
Timer,
6868
Notifer,
69+
Process,
6970
};
7071

7172
struct CompletionPacket {
@@ -101,6 +102,15 @@ struct EventLoopNotifier final : CompletionPacket {
101102
OwnHandle wait_event;
102103
};
103104

105+
struct EventLoopProcess final : CompletionPacket {
106+
~EventLoopProcess() = default;
107+
108+
OwnHandle process;
109+
pid_t pid;
110+
Function<void(pid_t)> exit_handler;
111+
OwnHandle jobobject;
112+
};
113+
104114
struct ThreadData {
105115
static ThreadData* the()
106116
{
@@ -136,6 +146,7 @@ struct ThreadData {
136146
// These are only used to register and unregister. The event loop doesn't access these.
137147
HashMap<intptr_t, NonnullOwnPtr<EventLoopTimer>> timers;
138148
HashMap<Notifier*, NonnullOwnPtr<EventLoopNotifier>> notifiers;
149+
HashMap<pid_t, NonnullOwnPtr<EventLoopProcess>> processes;
139150

140151
// The wake completion packet is posted to the thread's event loop to wake it.
141152
NonnullOwnPtr<EventLoopWake> wake_data;
@@ -210,6 +221,16 @@ size_t EventLoopImplementationWindows::pump(PumpMode pump_mode)
210221
VERIFY(NT_SUCCESS(status));
211222
continue;
212223
}
224+
if (packet->type == CompletionType::Process) {
225+
auto* process_data = static_cast<EventLoopProcess*>(packet);
226+
pid_t const process_id = process_data->pid;
227+
// 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
228+
// states that this field represents the event type indicator
229+
DWORD const event_type = entry.dwNumberOfBytesTransferred;
230+
if (reinterpret_cast<intptr_t>(entry.lpOverlapped) == process_id && (event_type == JOB_OBJECT_MSG_EXIT_PROCESS || event_type == JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS))
231+
process_data->exit_handler(process_id);
232+
continue;
233+
}
213234
VERIFY_NOT_REACHED();
214235
}
215236
} else {
@@ -351,6 +372,51 @@ void EventLoopManagerWindows::unregister_signal([[maybe_unused]] int handler_id)
351372
VERIFY_NOT_REACHED();
352373
}
353374

375+
void EventLoopManagerWindows::register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler)
376+
{
377+
auto* thread_data = ThreadData::the();
378+
VERIFY(thread_data);
379+
380+
auto& processes = thread_data->processes;
381+
if (processes.contains(pid))
382+
return;
383+
384+
HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
385+
VERIFY(process_handle);
386+
387+
HANDLE job_object_handle = CreateJobObject(nullptr, nullptr);
388+
VERIFY(job_object_handle);
389+
390+
BOOL succeeded = AssignProcessToJobObject(job_object_handle, process_handle);
391+
VERIFY(succeeded);
392+
393+
auto process_data = make<EventLoopProcess>();
394+
process_data->type = CompletionType::Process;
395+
process_data->process.handle = process_handle;
396+
process_data->pid = pid;
397+
process_data->exit_handler = move(exit_handler);
398+
process_data->jobobject.handle = job_object_handle;
399+
400+
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp = { .CompletionKey = process_data.ptr(), .CompletionPort = thread_data->iocp.handle };
401+
succeeded = SetInformationJobObject(job_object_handle, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
402+
VERIFY(succeeded);
403+
404+
processes.set(pid, move(process_data));
405+
}
406+
407+
void EventLoopManagerWindows::unregister_process(pid_t pid)
408+
{
409+
if (auto* thread_data = ThreadData::the()) {
410+
auto maybe_process = thread_data->processes.take(pid);
411+
if (!maybe_process.has_value())
412+
return;
413+
auto process_data = maybe_process.release_value();
414+
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp = { .CompletionKey = process_data, .CompletionPort = nullptr };
415+
BOOL succeeded = SetInformationJobObject(process_data->jobobject.handle, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
416+
VERIFY(succeeded);
417+
}
418+
}
419+
354420
void EventLoopManagerWindows::did_post_event()
355421
{
356422
}

Libraries/LibCore/EventLoopImplementationWindows.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class EventLoopManagerWindows final : public EventLoopManager {
2727

2828
virtual int register_signal(int signal_number, Function<void(int)> handler) override;
2929
virtual void unregister_signal(int handler_id) override;
30+
31+
virtual void register_process(pid_t pid, ESCAPING Function<void(pid_t)> exit_handler) override;
32+
virtual void unregister_process(pid_t pid) override;
3033
};
3134

3235
class EventLoopImplementationWindows final : public EventLoopImplementation {

0 commit comments

Comments
 (0)