LibCore: Keep main event loops alive

Add an explicit initializer for process-lifetime event loops and use it
for browser, helper service, and utility main loops. This preserves weak
event loop references for cross-thread users while making main thread
loop lifetime independent of normal program teardown.
This commit is contained in:
Andreas Kling 2026-06-04 19:51:47 +02:00 committed by Andreas Kling
parent 02b205361d
commit 83b293e4f2
21 changed files with 34 additions and 27 deletions

View file

@ -42,6 +42,11 @@ EventLoop::~EventLoop()
current_event_loop() = nullptr;
}
EventLoop& EventLoop::initialize_for_current_thread()
{
return *new EventLoop;
}
bool EventLoop::is_running()
{
return current_event_loop() != nullptr;

View file

@ -44,7 +44,6 @@ class CORE_API EventLoop {
AK_MAKE_NONMOVABLE(EventLoop);
AK_MAKE_NONCOPYABLE(EventLoop);
private:
public:
enum class WaitMode {
WaitForEvents,
@ -54,6 +53,9 @@ public:
EventLoop();
~EventLoop();
// Create an event loop for the current thread and keep it alive for the rest of the program.
static EventLoop& initialize_for_current_thread();
// Pump the event loop until its exit is requested.
int exec();

View file

@ -451,7 +451,7 @@ ErrorOr<void> Application::initialize(Main::Arguments const& arguments)
initialize_actions();
m_event_loop = create_platform_event_loop();
m_event_loop = &create_platform_event_loop();
TRY(launch_services());
return {};
@ -1036,9 +1036,9 @@ ErrorOr<int> Application::execute()
return m_event_loop->exec();
}
NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
Core::EventLoop& Application::create_platform_event_loop()
{
return make<Core::EventLoop>();
return Core::EventLoop::initialize_for_current_thread();
}
void Application::add_child_process(WebView::Process&& process)

View file

@ -213,7 +213,7 @@ protected:
virtual void create_platform_arguments(Core::ArgsParser&) { }
virtual void create_platform_options(BrowserOptions&, RequestServerOptions&, WebContentOptions&) { }
virtual NonnullOwnPtr<Core::EventLoop> create_platform_event_loop();
virtual Core::EventLoop& create_platform_event_loop();
virtual Optional<ByteString> ask_user_for_download_path([[maybe_unused]] StringView file) const { return {}; }
@ -352,7 +352,7 @@ private:
OwnPtr<Core::TimeZoneWatcher> m_time_zone_watcher;
OwnPtr<Core::EventLoop> m_event_loop;
Core::EventLoop* m_event_loop { nullptr };
OwnPtr<ProcessManager> m_process_manager;
RefPtr<Action> m_reload_action;

View file

@ -45,7 +45,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
Gfx::SkiaBackendContext::initialize_gpu_backend();
auto skia_backend_context = Gfx::SkiaBackendContext::the_main_thread_context();
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
auto client = TRY(IPC::take_over_accepted_client_from_system_server<Compositor::ConnectionFromClient>(
mach_server_name, move(skia_backend_context), !disable_async_scrolling));

View file

@ -28,7 +28,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
if (wait_for_debugger)
Core::Process::wait_for_debugger_and_break();
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
auto client = TRY(IPC::take_over_accepted_client_from_system_server<ImageDecoder::ConnectionFromClient>(mach_server_name));

View file

@ -71,7 +71,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
MUST(Core::System::signal(SIGPIPE, SIG_IGN));
#endif
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
// FIXME: Have another way to signal the event loop to gracefully quit on windows.
#ifndef AK_OS_WINDOWS
Core::EventLoop::register_signal(SIGINT, handle_signal);

View file

@ -127,7 +127,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
return -1;
}
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
WebView::platform_init();

View file

@ -115,7 +115,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
auto webdriver_socket_path = ByteString::formatted("{}/webdriver", TRY(Core::StandardPaths::runtime_directory()));
TRY(Core::Directory::create(webdriver_socket_path, Core::Directory::CreateDirectories::Yes));
Core::EventLoop loop;
auto& loop = Core::EventLoop::initialize_for_current_thread();
auto server = TRY(Core::TCPServer::try_create());
HashTable<NonnullRefPtr<WebDriver::Client>> clients;

View file

@ -77,7 +77,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
auto worker_type = TRY(agent_type_from_string(worker_type_string));
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
WebView::platform_init();

View file

@ -13,7 +13,7 @@
ErrorOr<int> service_main(int ipc_socket)
{
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
auto socket = TRY(Core::LocalSocket::adopt_fd(ipc_socket));
auto client = TRY(ImageDecoder::ConnectionFromClient::try_create(make<IPC::Transport>(move(socket))));

View file

@ -29,7 +29,7 @@ ErrorOr<int> service_main(int ipc_socket)
RequestServer::g_default_certificate_path = ByteString::formatted("{}/cacert.pem", WebView::s_ladybird_resource_root);
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
auto socket = TRY(Core::LocalSocket::adopt_fd(ipc_socket));
auto client = TRY(RequestServer::ConnectionFromClient::try_create(make<IPC::Transport>(move(socket))));

View file

@ -46,7 +46,7 @@ static ErrorOr<void> load_autoplay_allowlist();
ErrorOr<int> service_main(int ipc_socket)
{
Core::EventLoop event_loop;
auto& event_loop = Core::EventLoop::initialize_for_current_thread();
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPlugin);

View file

@ -18,7 +18,7 @@ class Application final : public WebView::Application {
private:
explicit Application();
virtual NonnullOwnPtr<Core::EventLoop> create_platform_event_loop() override;
virtual Core::EventLoop& create_platform_event_loop() override;
virtual Optional<WebView::ViewImplementation&> active_web_view() const override;
virtual Optional<WebView::ViewImplementation&> open_blank_new_tab(Web::HTML::ActivateTab) const override;

View file

@ -27,7 +27,7 @@ namespace Ladybird {
Application::Application() = default;
NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
Core::EventLoop& Application::create_platform_event_loop()
{
if (!browser_options().headless_mode.has_value()) {
Core::EventLoopManager::install(*new EventLoopManagerMacOS);

View file

@ -22,7 +22,7 @@ Application::~Application()
g_clear_object(&m_adw_application);
}
NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
Core::EventLoop& Application::create_platform_event_loop()
{
if (!browser_options().headless_mode.has_value()) {
Core::EventLoopManager::install(*new EventLoopManagerGtk);
@ -42,10 +42,10 @@ NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
setup_dbus_handlers();
}
auto event_loop = WebView::Application::create_platform_event_loop();
auto& event_loop = WebView::Application::create_platform_event_loop();
if (!browser_options().headless_mode.has_value())
static_cast<EventLoopImplementationGtk&>(event_loop->impl()).set_main_loop();
static_cast<EventLoopImplementationGtk&>(event_loop.impl()).set_main_loop();
return event_loop;
}

View file

@ -44,7 +44,7 @@ public:
private:
explicit Application();
virtual NonnullOwnPtr<Core::EventLoop> create_platform_event_loop() override;
virtual Core::EventLoop& create_platform_event_loop() override;
virtual Optional<WebView::ViewImplementation&> active_web_view() const override;
virtual Optional<WebView::ViewImplementation&> open_blank_new_tab(Web::HTML::ActivateTab) const override;

View file

@ -124,17 +124,17 @@ void Application::create_platform_options(WebView::BrowserOptions&, WebView::Req
web_content_options.config_path = Settings::the()->directory();
}
NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
Core::EventLoop& Application::create_platform_event_loop()
{
if (!browser_options().headless_mode.has_value()) {
Core::EventLoopManager::install(*new EventLoopManagerQt);
m_application = make<LadybirdQApplication>(arguments());
}
auto event_loop = WebView::Application::create_platform_event_loop();
auto& event_loop = WebView::Application::create_platform_event_loop();
if (!browser_options().headless_mode.has_value())
static_cast<EventLoopImplementationQt&>(event_loop->impl()).set_main_loop();
static_cast<EventLoopImplementationQt&>(event_loop.impl()).set_main_loop();
return event_loop;
}

View file

@ -42,7 +42,7 @@ private:
explicit Application();
virtual void create_platform_options(WebView::BrowserOptions&, WebView::RequestServerOptions&, WebView::WebContentOptions&) override;
virtual NonnullOwnPtr<Core::EventLoop> create_platform_event_loop() override;
virtual Core::EventLoop& create_platform_event_loop() override;
virtual Optional<WebView::ViewImplementation&> active_web_view() const override;
virtual Optional<WebView::ViewImplementation&> open_blank_new_tab(Web::HTML::ActivateTab) const override;

View file

@ -71,7 +71,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
return 1;
}
Core::EventLoop loop;
auto& loop = Core::EventLoop::initialize_for_current_thread();
DNS::Resolver resolver {
[&] -> ErrorOr<DNS::Resolver::SocketResult> {

View file

@ -329,7 +329,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
StringView input { input_data };
[[maybe_unused]] Core::EventLoop event_loop;
[[maybe_unused]] auto& event_loop = Core::EventLoop::initialize_for_current_thread();
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPlugin);
Web::Platform::FontPlugin::install(*new Web::Platform::FontPlugin(false));
Web::Bindings::initialize_main_thread_vm(Web::Bindings::AgentType::SimilarOriginWindow);