This fixes a really nasty EventLoop bug which I debugged for 2 weeks.
The spin_until([&]{return completed_tasks == total_tasks;}) in
TraversableNavigable::check_if_unloading_is_canceled spins forever.
Cause of the bug:
check_if_unloading_is_canceled is called deferred
check_if_unloading_is_canceled creates a task:
queue_global_task(..., [&] {
...
completed_tasks++;
}));
This task is never executed.
queue_global_task calls TaskQueue::add
void TaskQueue::add(task)
{
m_tasks.append(task);
m_event_loop->schedule();
}
void HTML::EventLoop::schedule()
{
if (!m_system_event_loop_timer)
m_system_event_loop_timer = Timer::create_single_shot(
0, // delay
[&] { process(); });
if (!m_system_event_loop_timer->is_active())
m_system_event_loop_timer->restart();
}
EventLoop::process executes one task from task queue and calls
schedule again if there are more tasks.
So task processing relies on one single-shot zero-delay timer,
m_system_event_loop_timer.
Timers and other notification events are handled by Core::EventLoop
and Core::ThreadEventQueue, these are different from HTML::EventLoop
and HTML::TaskQueue mentioned above.
check_if_unloading_is_canceled is called using deferred_invoke
mechanism, different from m_system_event_loop_timer,
see Navigable::navigate and Core::EventLoop::deferred_invoke.
The core of the problem is that Core::EventLoop::pump is called again
(from spin_until) after timer fired but before its handler is executed.
In ThreadEventQueue::process events are moved into local variable before
executing. The first of those events is check_if_unloading_is_canceled.
One of the rest events is Web::HTML::EventLoop::process sheduled in
EventLoop::schedule using m_system_event_loop_timer.
When check_if_unloading_is_canceled calls queue_global_task its
m_system_event_loop_timer is still active because Timer::timer_event
was not yet called, so the timer is not restarted.
But Timer::timer_event (and hence EventLoop::process) will never execute
because check_if_unloading_is_canceled calls spin_until after
queue_global_task, and EventLoop::process is no longer in
event_queue.m_private->queued_events.
By making a single-shot timer manually-reset we are allowing it to fire
several times. So when spin_until is executed m_system_event_loop_timer
is fired again. Not an ideal solution, but this is the best I could
come up with. This commit makes the behavior match EventLoopImplUnix,
in which single-shot timer can also fire several times.
Adding event_queue.process(); at the start of pump like in EvtLoopImplQt
doesn't fix the problem.
Note: Timer::start calls EventReceiver::start_timer, which calls
EventLoop::register_timer with should_reload always set to true
(single-shot vs periodic are handled in Timer::timer_event instead),
so I use static_cast<Timer&>(object).is_single_shot() instead of
!should_reload.
|
||
|---|---|---|
| .devcontainer | ||
| .github | ||
| AK | ||
| Base/res | ||
| Documentation | ||
| Libraries | ||
| Meta | ||
| Services | ||
| Tests | ||
| Toolchain | ||
| UI | ||
| Utilities | ||
| .clang-format | ||
| .clang-tidy | ||
| .clangd | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .gn | ||
| .mailmap | ||
| .pre-commit-config.yaml | ||
| .prettierignore | ||
| .prettierrc | ||
| .swift-format | ||
| .ycm_extra_conf.py | ||
| CMakeLists.txt | ||
| CMakePresets.json | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| ISSUES.md | ||
| LICENSE | ||
| README.md | ||
| SECURITY.md | ||
| vcpkg-configuration.json | ||
| vcpkg.json | ||
Ladybird
Ladybird is a truly independent web browser, using a novel engine based on web standards.
Important
Ladybird is in a pre-alpha state, and only suitable for use by developers
Features
We aim to build a complete, usable browser for the modern web.
Ladybird uses a multi-process architecture with a main UI process, several WebContent renderer processes, an ImageDecoder process, and a RequestServer process.
Image decoding and network connections are done out of process to be more robust against malicious content. Each tab has its own renderer process, which is sandboxed from the rest of the system.
At the moment, many core library support components are inherited from SerenityOS:
- LibWeb: Web rendering engine
- LibJS: JavaScript engine
- LibWasm: WebAssembly implementation
- LibCrypto/LibTLS: Cryptography primitives and Transport Layer Security
- LibHTTP: HTTP/1.1 client
- LibGfx: 2D Graphics Library, Image Decoding and Rendering
- LibUnicode: Unicode and locale support
- LibMedia: Audio and video playback
- LibCore: Event loop, OS abstraction layer
- LibIPC: Inter-process communication
How do I build and run this?
See build instructions for information on how to build Ladybird.
Ladybird runs on Linux, macOS, Windows (with WSL2), and many other *Nixes.
How do I read the documentation?
Code-related documentation can be found in the documentation folder.
Get in touch and participate!
Join our Discord server to participate in development discussion.
Please read Getting started contributing if you plan to contribute to Ladybird for the first time.
Before opening an issue, please see the issue policy and the detailed issue-reporting guidelines.
The full contribution guidelines can be found in CONTRIBUTING.md.
License
Ladybird is licensed under a 2-clause BSD license.