Explorar el Código

LibThreading: Wake up the background worker thread when there's work

The worker thread used for BackgroundAction was going to sleep for
1 second at a time (when there was nothing to do.) This made using
background actions for anything interactive quite unresponsive since
you had to wait up to 1 second before it even started on your task.

We now use a simple Unix pipe to signal the worker thread that a new
work item is available.

This makes Assistant way more responsive when typing. :^)
Andreas Kling hace 4 años
padre
commit
e8579ed24a

+ 32 - 12
Userland/Libraries/LibThreading/BackgroundAction.cpp

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -8,24 +9,33 @@
 #include <LibThreading/BackgroundAction.h>
 #include <LibThreading/Lock.h>
 #include <LibThreading/Thread.h>
+#include <unistd.h>
 
 static Threading::Lockable<Queue<Function<void()>>>* s_all_actions;
 static Threading::Thread* s_background_thread;
+static int s_notify_pipe_fds[2];
 
 static intptr_t background_thread_func()
 {
     while (true) {
-        Function<void()> work_item;
+        char buffer[1];
+        auto nread = read(s_notify_pipe_fds[0], buffer, sizeof(buffer));
+        if (nread < 0) {
+            perror("read");
+            _exit(1);
+        }
+
+        Vector<Function<void()>> work_items;
         {
             Threading::Locker locker(s_all_actions->lock());
 
-            if (!s_all_actions->resource().is_empty())
-                work_item = s_all_actions->resource().dequeue();
+            while (!s_all_actions->resource().is_empty()) {
+                work_items.append(s_all_actions->resource().dequeue());
+            }
         }
-        if (work_item)
+
+        for (auto& work_item : work_items)
             work_item();
-        else
-            sleep(1);
     }
 
     VERIFY_NOT_REACHED();
@@ -33,22 +43,32 @@ static intptr_t background_thread_func()
 
 static void init()
 {
+    if (pipe(s_notify_pipe_fds) < 0) {
+        perror("pipe");
+        _exit(1);
+    }
     s_all_actions = new Threading::Lockable<Queue<Function<void()>>>();
     s_background_thread = &Threading::Thread::construct(background_thread_func).leak_ref();
     s_background_thread->set_name("Background thread");
     s_background_thread->start();
 }
 
-Threading::Lockable<Queue<Function<void()>>>& Threading::BackgroundActionBase::all_actions()
+Threading::Thread& Threading::BackgroundActionBase::background_thread()
 {
-    if (s_all_actions == nullptr)
+    if (s_background_thread == nullptr)
         init();
-    return *s_all_actions;
+    return *s_background_thread;
 }
 
-Threading::Thread& Threading::BackgroundActionBase::background_thread()
+void Threading::BackgroundActionBase::enqueue_work(Function<void()> work)
 {
-    if (s_background_thread == nullptr)
+    if (s_all_actions == nullptr)
         init();
-    return *s_background_thread;
+    Locker locker(s_all_actions->lock());
+    s_all_actions->resource().enqueue(move(work));
+    char ch = 'x';
+    if (write(s_notify_pipe_fds[1], &ch, sizeof(ch)) < 0) {
+        perror("write");
+        _exit(1);
+    }
 }

+ 3 - 5
Userland/Libraries/LibThreading/BackgroundAction.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
+ * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -13,7 +14,6 @@
 #include <LibCore/Event.h>
 #include <LibCore/EventLoop.h>
 #include <LibCore/Object.h>
-#include <LibThreading/Lock.h>
 #include <LibThreading/Thread.h>
 
 namespace Threading {
@@ -28,7 +28,7 @@ class BackgroundActionBase {
 private:
     BackgroundActionBase() { }
 
-    static Lockable<Queue<Function<void()>>>& all_actions();
+    static void enqueue_work(Function<void()>);
     static Thread& background_thread();
 };
 
@@ -63,9 +63,7 @@ private:
         , m_action(move(action))
         , m_on_complete(move(on_complete))
     {
-        Locker locker(all_actions().lock());
-
-        all_actions().resource().enqueue([this] {
+        enqueue_work([this] {
             m_result = m_action(*this);
             if (m_on_complete) {
                 Core::EventLoop::current().post_event(*this, make<Core::DeferredInvocationEvent>([this](auto&) {