Files
ladybird/Libraries/LibWebView/ProcessManager.cpp
ayeteadoe 433ff624b8 LibWebView: Abstract process exit monitoring into ProcessMonitor
There is no direct equivalent to SIGCHILD on Windows. The closest we
can get is monitoring a specific process, given a known pid. On Unix
there is no single solution to be able to do that in LibCore's
EventLoopImplementationUnix. For Linux there's a SYS_pidfd_open syscall
that can integrate into poll(), but on macOS a kqueue would be needed.
Given macOS uses EventLoopImplementationUnix for the headless view
implementation, we currently can't create a fully cross-platform
abstaction at the Event Loop level to match what Windows needs to do.

ProcessMonitor's purpose is to abstract away the Unix vs Windows
behaviour avoiding more inlined ifdef soup in ProcessManager.
2026-04-12 16:08:07 +02:00

138 lines
3.9 KiB
C++

/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/String.h>
#include <LibCore/EventLoop.h>
#include <LibCore/System.h>
#include <LibWebView/ProcessManager.h>
namespace WebView {
ProcessType process_type_from_name(StringView name)
{
if (name == "Browser"sv)
return ProcessType::Browser;
if (name == "WebContent"sv)
return ProcessType::WebContent;
if (name == "WebWorker"sv)
return ProcessType::WebWorker;
if (name == "RequestServer"sv)
return ProcessType::RequestServer;
if (name == "ImageDecoder"sv)
return ProcessType::ImageDecoder;
dbgln("Unknown process type: '{}'", name);
VERIFY_NOT_REACHED();
}
StringView process_name_from_type(ProcessType type)
{
switch (type) {
case ProcessType::Browser:
return "Browser"sv;
case ProcessType::WebContent:
return "WebContent"sv;
case ProcessType::WebWorker:
return "WebWorker"sv;
case ProcessType::RequestServer:
return "RequestServer"sv;
case ProcessType::ImageDecoder:
return "ImageDecoder"sv;
}
VERIFY_NOT_REACHED();
}
ProcessManager::ProcessManager()
: on_process_exited([](Process&&) {})
, m_process_monitor(ProcessMonitor([this](pid_t pid) {
if (auto process = remove_process(pid); process.has_value())
on_process_exited(process.release_value());
}))
{
add_process(Process(WebView::ProcessType::Browser, nullptr, Core::Process::current()));
#ifdef AK_OS_MACH
auto self_send_port = mach_task_self();
auto res = mach_port_mod_refs(mach_task_self(), self_send_port, MACH_PORT_RIGHT_SEND, +1);
VERIFY(res == KERN_SUCCESS);
set_process_mach_port(getpid(), Core::MachPort::adopt_right(self_send_port, Core::MachPort::PortRight::Send));
#endif
}
Optional<Process&> ProcessManager::find_process(pid_t pid)
{
return m_processes.get(pid);
}
void ProcessManager::add_process(WebView::Process&& process)
{
Threading::MutexLocker locker { m_lock };
auto pid = process.pid();
auto result = m_processes.set(pid, move(process));
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
m_statistics.processes.append(make<Core::Platform::ProcessInfo>(pid));
m_process_monitor.add_process(pid);
}
#if defined(AK_OS_MACH)
void ProcessManager::set_process_mach_port(pid_t pid, Core::MachPort&& port)
{
Threading::MutexLocker locker { m_lock };
for (auto const& info : m_statistics.processes) {
if (info->pid == pid) {
info->child_task_port = move(port);
return;
}
}
}
#endif
Optional<Process> ProcessManager::remove_process(pid_t pid)
{
Threading::MutexLocker locker { m_lock };
m_statistics.processes.remove_first_matching([&](auto const& info) {
return (info->pid == pid);
});
return m_processes.take(pid);
}
void ProcessManager::update_all_process_statistics()
{
Threading::MutexLocker locker { m_lock };
(void)update_process_statistics(m_statistics);
}
JsonValue ProcessManager::serialize_json()
{
Threading::MutexLocker locker { m_lock };
JsonArray serialized;
m_statistics.for_each_process([&](auto const& process) {
auto& process_handle = find_process(process.pid).value();
auto type = WebView::process_name_from_type(process_handle.type());
auto const& title = process_handle.title();
auto process_name = title.has_value()
? MUST(String::formatted("{} - {}", type, *title))
: String::from_utf8_without_validation(type.bytes());
JsonObject object;
object.set("name"sv, move(process_name));
object.set("pid"sv, process.pid);
object.set("cpu"sv, process.cpu_percent);
object.set("memory"sv, process.memory_usage_bytes);
serialized.must_append(move(object));
});
return serialized;
}
}