Skip to content

Commit a339c4e

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 1444113 commit a339c4e

File tree

5 files changed

+87
-0
lines changed

5 files changed

+87
-0
lines changed

Libraries/LibCore/EventLoop.cpp

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

145+
void EventLoop::register_process(pid_t pid, ESCAPING Function<void(pid_t)> handler)
146+
{
147+
EventLoopManager::the().register_process(pid, move(handler));
148+
}
149+
150+
void EventLoop::unregister_process(pid_t pid)
151+
{
152+
EventLoopManager::the().unregister_process(pid);
153+
}
154+
145155
void EventLoop::wake()
146156
{
147157
m_impl->wake();

Libraries/LibCore/EventLoop.h

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

93+
// Invokes the specified handler when the process exits
94+
static void register_process(pid_t pid, ESCAPING Function<void(pid_t)> handler);
95+
static void unregister_process(pid_t pid);
96+
9397
static bool is_running();
9498
static EventLoop& current();
9599

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(pid_t, ESCAPING Function<void(pid_t)>) { VERIFY_NOT_REACHED(); }
39+
virtual void unregister_process(pid_t) { 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)> 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->handler(process_id);
232+
continue;
233+
}
213234
VERIFY_NOT_REACHED();
214235
}
215236
} else {
@@ -358,6 +379,51 @@ void EventLoopManagerWindows::unregister_signal([[maybe_unused]] int handler_id)
358379
VERIFY_NOT_REACHED();
359380
}
360381

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

Libraries/LibCore/EventLoopImplementationWindows.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ 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+
// Invokes the specified handler when the process exits
32+
virtual void register_process(pid_t pid, ESCAPING Function<void(pid_t)> handler) override;
33+
virtual void unregister_process(pid_t pid) override;
3034
};
3135

3236
class EventLoopImplementationWindows final : public EventLoopImplementation {

0 commit comments

Comments
 (0)