Pārlūkot izejas kodu

LibThreading: ThreadPool deadlock test

Enable `LibThreading` tests and add a test to check for deadlocks in
`ThreadPool`s
Braydn 1 gadu atpakaļ
vecāks
revīzija
a94bf9bd09

+ 1 - 0
Meta/Lagom/CMakeLists.txt

@@ -514,6 +514,7 @@ if (BUILD_TESTING)
         LibCompress
         LibTest
         LibTextCodec
+        LibThreading
         LibUnicode
         LibURL
         LibXML

+ 1 - 0
Meta/gn/secondary/Tests/BUILD.gn

@@ -2,6 +2,7 @@ group("Tests") {
   deps = [
     "//Tests/AK",
     "//Tests/LibJS",
+    "//Tests/LibThreading",
     "//Tests/LibURL",
     "//Tests/LibWeb",
   ]

+ 1 - 0
Tests/LibThreading/CMakeLists.txt

@@ -1,5 +1,6 @@
 set(TEST_SOURCES
     TestThread.cpp
+    TestThreadPool.cpp
 )
 
 foreach(source IN LISTS TEST_SOURCES)

+ 57 - 0
Tests/LibThreading/TestThreadPool.cpp

@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024, Braydn Moore <braydn.moore@uwaterloo.ca>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Time.h>
+#include <LibCore/ElapsedTimer.h>
+#include <LibTest/TestCase.h>
+#include <LibThreading/ThreadPool.h>
+
+using namespace AK::TimeLiterals;
+
+TEST_CASE(thread_pool_deadlock)
+{
+    static constexpr auto RUN_TIMEOUT = 120_sec;
+    static constexpr u64 NUM_RUNS = 1000;
+    static constexpr u64 MAX_VALUE = 1 << 15;
+
+    for (u64 i = 0; i < NUM_RUNS; ++i) {
+        u64 expected_value = (MAX_VALUE * (MAX_VALUE + 1)) / 2;
+        Atomic<u64> sum;
+
+        // heap allocate the ThreadPool in case it deadlocks. Exiting in the
+        // case of a deadlock will purposefully leak memory to avoid calling the
+        // destructor and hanging the test
+        auto* thread_pool = new Threading::ThreadPool<u64>(
+            [&sum](u64 current_val) {
+                sum += current_val;
+            });
+
+        for (u64 j = 0; j <= MAX_VALUE; ++j) {
+            thread_pool->submit(j);
+        }
+
+        auto join_thread = Threading::Thread::construct([thread_pool]() -> intptr_t {
+            thread_pool->wait_for_all();
+            delete thread_pool;
+            return 0;
+        });
+
+        join_thread->start();
+        auto timer = Core::ElapsedTimer::start_new(Core::TimerType::Precise);
+        while (!join_thread->has_exited() && timer.elapsed_milliseconds() < RUN_TIMEOUT.to_milliseconds())
+            ;
+        EXPECT(join_thread->has_exited());
+        // exit since the current pool is deadlocked and we have no way of
+        // unblocking the pool other than having the OS teardown the process
+        // struct
+        if (!join_thread->has_exited()) {
+            return;
+        }
+
+        (void)join_thread->join();
+        EXPECT_EQ(sum.load(), expected_value);
+    }
+}