Skip to content

Commit

Permalink
LibWebView+LibCore: Manage process lifecycle using a SIGCHLD handler
Browse files Browse the repository at this point in the history
This large commit also refactors LibWebView's process handling to use
a top-level Application class that uses a new WebView::Process class to
encapsulate the IPC-centric nature of each helper process.
  • Loading branch information
ADKaster committed Jun 30, 2024
1 parent f027530 commit 9616eac
Show file tree
Hide file tree
Showing 21 changed files with 349 additions and 165 deletions.
6 changes: 3 additions & 3 deletions Ladybird/AppKit/UI/TaskManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <AK/String.h>
#include <LibCore/Timer.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/Application.h>

#import <UI/LadybirdWebView.h>
#import <UI/TaskManager.h>
Expand Down Expand Up @@ -77,8 +77,8 @@ - (instancetype)init

- (void)updateStatistics
{
WebView::ProcessManager::the().update_all_processes();
[self.web_view loadHTML:WebView::ProcessManager::the().generate_html()];
WebView::Application::the().update_process_statistics();
[self.web_view loadHTML:WebView::Application::the().generate_process_statistics_html()];
}

@end
17 changes: 7 additions & 10 deletions Ladybird/AppKit/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
#include <Ladybird/Types.h>
#include <Ladybird/Utilities.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibMain/Main.h>
#include <LibWebView/Application.h>
#include <LibWebView/ChromeProcess.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/Database.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/URL.h>
#include <LibWebView/ViewImplementation.h>
#include <LibWebView/WebContentClient.h>
Expand Down Expand Up @@ -77,7 +76,7 @@ static void open_urls_from_client(Vector<ByteString> const& raw_urls, NewWindow
Application* application = [Application sharedApplication];

Core::EventLoopManager::install(*new Ladybird::CFEventLoopManager);
Core::EventLoop event_loop;
WebView::Application web_view_app(arguments.argc, arguments.argv);

platform_init();

Expand Down Expand Up @@ -118,16 +117,14 @@ static void open_urls_from_client(Vector<ByteString> const& raw_urls, NewWindow
open_urls_from_client(raw_urls, NewWindow::Yes);
};

WebView::ProcessManager::initialize();

auto mach_port_server = make<Ladybird::MachPortServer>();
set_mach_server_name(mach_port_server->server_port_name());
mach_port_server->on_receive_child_mach_port = [](auto pid, auto port) {
WebView::ProcessManager::the().add_process(pid, move(port));
mach_port_server->on_receive_child_mach_port = [&web_view_app](auto pid, auto port) {
web_view_app.set_process_mach_port(pid, move(port));
};
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id);
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
if (auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id); view.has_value())
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
};

auto database = TRY(WebView::Database::create());
Expand Down Expand Up @@ -158,5 +155,5 @@ static void open_urls_from_client(Vector<ByteString> const& raw_urls, NewWindow

[NSApp setDelegate:delegate];

return event_loop.exec();
return web_view_app.exec();
}
4 changes: 2 additions & 2 deletions Ladybird/HelperProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "Utilities.h"
#include <AK/Enumerate.h>
#include <LibCore/Process.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/Application.h>

template<typename ClientType, typename... ClientArguments>
static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
Expand Down Expand Up @@ -45,7 +45,7 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
if constexpr (requires { process.client->set_pid(pid_t {}); })
process.client->set_pid(process.process.pid());

WebView::ProcessManager::the().add_process(WebView::process_type_from_name(server_name), process.process.pid());
WebView::Application::the().add_child_process(WebView::Process { WebView::process_type_from_name(server_name), process.client, move(process.process) });

if (enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::Yes) {
dbgln();
Expand Down
7 changes: 3 additions & 4 deletions Ladybird/Qt/TaskManagerWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

#include "TaskManagerWindow.h"
#include <LibWebView/ProcessManager.h>
#include <LibWebView/Application.h>
#include <QVBoxLayout>

namespace Ladybird {
Expand Down Expand Up @@ -41,9 +41,8 @@ void TaskManagerWindow::hideEvent(QHideEvent*)

void TaskManagerWindow::update_statistics()
{

WebView::ProcessManager::the().update_all_processes();
m_web_view->load_html(WebView::ProcessManager::the().generate_html());
WebView::Application::the().update_process_statistics();
m_web_view->load_html(WebView::Application::the().generate_process_statistics_html());
}

}
17 changes: 8 additions & 9 deletions Ladybird/Qt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <LibCore/System.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibMain/Main.h>
#include <LibWebView/Application.h>
#include <LibWebView/ChromeProcess.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/Database.h>
Expand Down Expand Up @@ -76,8 +77,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Ladybird::Application app(arguments.argc, arguments.argv);

Core::EventLoopManager::install(*new Ladybird::EventLoopManagerQt);
Core::EventLoop event_loop;
static_cast<Ladybird::EventLoopImplementationQt&>(event_loop.impl()).set_main_loop();
WebView::Application webview_app(arguments.argc, arguments.argv);
static_cast<Ladybird::EventLoopImplementationQt&>(Core::EventLoop::current().impl()).set_main_loop();

TRY(handle_attached_debugger());

Expand Down Expand Up @@ -142,17 +143,15 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
window.view().load(file_url);
};

WebView::ProcessManager::initialize();

#if defined(AK_OS_MACOS)
auto mach_port_server = make<Ladybird::MachPortServer>();
set_mach_server_name(mach_port_server->server_port_name());
mach_port_server->on_receive_child_mach_port = [](auto pid, auto port) {
WebView::ProcessManager::the().add_process(pid, move(port));
mach_port_server->on_receive_child_mach_port = [&webview_app](auto pid, auto port) {
webview_app.set_process_mach_port(pid, move(port));
};
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id);
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
if (auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id); view.has_value())
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
};
#endif

Expand Down Expand Up @@ -204,5 +203,5 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)

window.show();

return event_loop.exec();
return webview_app.exec();
}
1 change: 1 addition & 0 deletions Userland/Libraries/LibCore/Forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class MimeData;
class NetworkJob;
class NetworkResponse;
class Notifier;
class Process;
class ProcessStatisticsReader;
class Resource;
class ResourceImplementation;
Expand Down
8 changes: 0 additions & 8 deletions Userland/Libraries/LibCore/Platform/ProcessInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ struct ProcessInfo {
{
}

virtual ~ProcessInfo() = default;

pid_t pid { 0 };

u64 memory_usage_bytes { 0 };
Expand All @@ -30,12 +28,6 @@ struct ProcessInfo {
u64 time_spent_in_process { 0 };

#if defined(AK_OS_MACH)
ProcessInfo(pid_t pid, Core::MachPort&& port)
: pid(pid)
, child_task_port(move(port))
{
}

Core::MachPort child_task_port;
#endif
};
Expand Down
4 changes: 2 additions & 2 deletions Userland/Libraries/LibCore/Platform/ProcessStatistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
namespace Core::Platform {

struct ProcessStatistics {
template<typename ProcessInfoType, typename Callback>
template<typename Callback>
void for_each_process(Callback&& callback)
{
for (auto& process : processes)
callback(verify_cast<ProcessInfoType>(*process));
callback(*process);
}

u64 total_time_scheduled { 0 };
Expand Down
7 changes: 7 additions & 0 deletions Userland/Libraries/LibCore/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ struct ArgvList {
}
};

Process Process::current()
{
auto p = Process { getpid() };
p.m_should_disown = false;
return p;
}

ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
{
#define CHECK(invocation) \
Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibCore/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Process {
}

static ErrorOr<Process> spawn(ProcessSpawnOptions const& options);
static Process current();

// FIXME: Make the following 2 functions return Process instance or delete them.
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);
Expand Down
3 changes: 0 additions & 3 deletions Userland/Libraries/LibImageDecoderClient/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ void Client::die()
promise->reject(Error::from_string_literal("ImageDecoder disconnected"));
}
m_pending_decoded_images.clear();

if (on_death)
on_death();
}

NonnullRefPtr<Core::Promise<DecodedImage>> Client::decode_image(ReadonlyBytes encoded_data, Function<ErrorOr<void>(DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> mime_type)
Expand Down
100 changes: 100 additions & 0 deletions Userland/Libraries/LibWebView/Application.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibImageDecoderClient/Client.h>
#include <LibWebView/Application.h>
#include <LibWebView/WebContentClient.h>

namespace WebView {

Application* Application::s_the = nullptr;

Application::Application(int, char**)
{
VERIFY(!s_the);
s_the = this;

m_process_manager.on_process_exited = [this](Process&& process) {
dbgln("ProcessManager: Process{}: {} ({}) exited", process.pid(), process.title().value_or("Untitled"_string), process_name_from_type(process.type()));
process_did_exit(move(process));
};
}

Application::~Application()
{
s_the = nullptr;
}

int Application::exec()
{
int ret = m_event_loop.exec();
m_in_shutdown = true;
return ret;
}

void Application::add_child_process(WebView::Process&& process)
{
m_process_manager.add_process(move(process));
}

#if defined(AK_OS_MACH)
void Application::set_process_mach_port(pid_t pid, Core::MachPort&& port)
{
m_process_manager.set_process_mach_port(pid, move(port));
}
#endif

Optional<Process&> Application::find_process(pid_t pid)
{
return m_process_manager.find_process(pid);
}

void Application::update_process_statistics()
{
m_process_manager.update_all_process_statistics();
}

String Application::generate_process_statistics_html()
{
return m_process_manager.generate_html();
}

void Application::process_did_exit(Process&& process)
{
if (m_in_shutdown)
return;

dbgln("Process {} died, type: {}", process.pid(), process_name_from_type(process.type()));

switch (process.type()) {
case ProcessType::ImageDecoder:
if (auto client = process.client<ImageDecoderClient::Client>(); client.has_value()) {
dbgln("Restart ImageDecoder process");
if (auto on_death = move(client->on_death)) {
on_death();
}
}
break;
case ProcessType::RequestServer:
dbgln("FIXME: Restart request server");
break;
case ProcessType::WebContent:
if (auto client = process.client<WebContentClient>(); client.has_value()) {
dbgln("Restart WebContent process");
if (auto on_web_content_process_crash = move(client->on_web_content_process_crash))
on_web_content_process_crash();
}
break;
case ProcessType::WebWorker:
dbgln("WebWorker {} died, not sure what to do.", process.pid());
break;
case ProcessType::Chrome:
dbgln("Invalid process type to be dying: Chrome");
VERIFY_NOT_REACHED();
}
}

}
55 changes: 55 additions & 0 deletions Userland/Libraries/LibWebView/Application.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <LibCore/EventLoop.h>
#include <LibWebView/Process.h>
#include <LibWebView/ProcessManager.h>

namespace WebView {

class Application {
AK_MAKE_NONCOPYABLE(Application);

public:
Application(int argc, char** argv);
virtual ~Application();

int exec();

static Application& the() { return *s_the; }

Core::EventLoop& event_loop() { return m_event_loop; }

void add_child_process(Process&&);

// FIXME: Should these methods be part of Application, instead of deferring to ProcessManager?
#if defined(AK_OS_MACH)
void set_process_mach_port(pid_t, Core::MachPort&&);
#endif
Optional<Process&> find_process(pid_t);

// FIXME: Should we just expose the ProcessManager via a getter?
void update_process_statistics();
String generate_process_statistics_html();

protected:
virtual void process_did_exit(Process&&);

private:
static Application* s_the;

Core::EventLoop m_event_loop;

Optional<Process&> m_request_server_process;
Optional<Process&> m_image_decoder_process;

ProcessManager m_process_manager;
bool m_in_shutdown { false };
};

}
Loading

0 comments on commit 9616eac

Please sign in to comment.