Sfoglia il codice sorgente

LibWeb: Schedule HTML::EventLoop processing when there are queued tasks

Since we can't simply give HTML::EventLoop control of the whole program,
we have to integrate with Core::EventLoop.

We do this by having a single-shot 0ms Core::Timer that we start when
a task is added to the queue, and restart after processing the queue and
there are still tasks in the queue.
Andreas Kling 3 anni fa
parent
commit
909e522cf7

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -65,6 +65,7 @@ class CanvasRenderingContext2D;
 class CloseEvent;
 class DOMParser;
 struct EventHandler;
+class EventLoop;
 class HTMLAnchorElement;
 class HTMLAreaElement;
 class HTMLAudioElement;

+ 21 - 0
Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <LibCore/Timer.h>
 #include <LibJS/Runtime/VM.h>
 #include <LibWeb/Bindings/MainThreadVM.h>
 #include <LibWeb/HTML/EventLoop/EventLoop.h>
@@ -11,6 +12,7 @@
 namespace Web::HTML {
 
 EventLoop::EventLoop()
+    : m_task_queue(*this)
 {
 }
 
@@ -18,6 +20,18 @@ EventLoop::~EventLoop()
 {
 }
 
+void EventLoop::schedule()
+{
+    if (!m_system_event_loop_timer) {
+        m_system_event_loop_timer = Core::Timer::create_single_shot(0, [this] {
+            process();
+        });
+    }
+
+    if (!m_system_event_loop_timer->is_active())
+        m_system_event_loop_timer->restart();
+}
+
 void EventLoop::set_vm(JS::VM& vm)
 {
     VERIFY(!m_vm);
@@ -66,6 +80,9 @@ void EventLoop::process()
     // 2. Let oldestTask be the first runnable task in taskQueue, and remove it from taskQueue.
     auto oldest_task = task_queue.take_first_runnable();
 
+    // FIXME: Figure out if we need to be here when there's no task.
+    VERIFY(oldest_task);
+
     // 3. Set the event loop's currently running task to oldestTask.
     m_currently_running_task = oldest_task.ptr();
 
@@ -145,6 +162,10 @@ void EventLoop::process()
     // FIXME:        3. Update the rendering of that dedicated worker to reflect the current state.
 
     // FIXME:     2. If there are no tasks in the event loop's task queues and the WorkerGlobalScope object's closing flag is true, then destroy the event loop, aborting these steps, resuming the run a worker steps described in the Web workers section below.
+
+    // If there are tasks in the queue, schedule a new round of processing. :^)
+    if (!m_task_queue.is_empty())
+        schedule();
 }
 
 }

+ 6 - 0
Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h

@@ -7,6 +7,8 @@
 #pragma once
 
 #include <AK/Function.h>
+#include <LibCore/Forward.h>
+#include <LibJS/Forward.h>
 #include <LibWeb/HTML/EventLoop/TaskQueue.h>
 
 namespace Web::HTML {
@@ -40,6 +42,8 @@ public:
 
     void set_vm(JS::VM&);
 
+    void schedule();
+
 private:
     Type m_type { Type::Window };
 
@@ -49,6 +53,8 @@ private:
     Task* m_currently_running_task { nullptr };
 
     JS::VM* m_vm { nullptr };
+
+    RefPtr<Core::Timer> m_system_event_loop_timer;
 };
 
 EventLoop& main_thread_event_loop();

+ 4 - 1
Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.cpp

@@ -4,11 +4,13 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <LibWeb/HTML/EventLoop/EventLoop.h>
 #include <LibWeb/HTML/EventLoop/TaskQueue.h>
 
 namespace Web::HTML {
 
-TaskQueue::TaskQueue()
+TaskQueue::TaskQueue(HTML::EventLoop& event_loop)
+    : m_event_loop(event_loop)
 {
 }
 
@@ -19,6 +21,7 @@ TaskQueue::~TaskQueue()
 void TaskQueue::add(NonnullOwnPtr<Task> task)
 {
     m_tasks.enqueue(move(task));
+    m_event_loop.schedule();
 }
 
 }

+ 3 - 1
Userland/Libraries/LibWeb/HTML/EventLoop/TaskQueue.h

@@ -13,7 +13,7 @@ namespace Web::HTML {
 
 class TaskQueue {
 public:
-    TaskQueue();
+    explicit TaskQueue(HTML::EventLoop&);
     ~TaskQueue();
 
     bool is_empty() const { return m_tasks.is_empty(); }
@@ -22,6 +22,8 @@ public:
     OwnPtr<HTML::Task> take_first_runnable() { return m_tasks.dequeue(); }
 
 private:
+    HTML::EventLoop& m_event_loop;
+
     Queue<NonnullOwnPtr<HTML::Task>> m_tasks;
 };