mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
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:
committed by
Alexander Kalenik
parent
433ff624b8
commit
4cb7743915
Notes:
github-actions[bot]
2026-04-12 14:09:25 +00:00
Author: https://github.com/ayeteadoe Commit: https://github.com/LadybirdBrowser/ladybird/commit/4cb7743915b Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6909 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/R-Goc
@@ -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()
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user