1 //========- unittests/Support/ThreadPools.cpp - ThreadPools.h tests --========// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Support/ThreadPool.h" 10 11 #include "llvm/ADT/STLExtras.h" 12 #include "llvm/ADT/SmallVector.h" 13 #include "llvm/ADT/Triple.h" 14 #include "llvm/Support/Host.h" 15 #include "llvm/Support/TargetSelect.h" 16 17 #include "gtest/gtest.h" 18 19 using namespace llvm; 20 21 // Fixture for the unittests, allowing to *temporarily* disable the unittests 22 // on a particular platform 23 class ThreadPoolTest : public testing::Test { 24 Triple Host; 25 SmallVector<Triple::ArchType, 4> UnsupportedArchs; 26 SmallVector<Triple::OSType, 4> UnsupportedOSs; 27 SmallVector<Triple::EnvironmentType, 1> UnsupportedEnvironments; 28 protected: 29 // This is intended for platform as a temporary "XFAIL" 30 bool isUnsupportedOSOrEnvironment() { 31 Triple Host(Triple::normalize(sys::getProcessTriple())); 32 33 if (find(UnsupportedEnvironments, Host.getEnvironment()) != 34 UnsupportedEnvironments.end()) 35 return true; 36 37 if (is_contained(UnsupportedOSs, Host.getOS())) 38 return true; 39 40 if (is_contained(UnsupportedArchs, Host.getArch())) 41 return true; 42 43 return false; 44 } 45 46 ThreadPoolTest() { 47 // Add unsupported configuration here, example: 48 // UnsupportedArchs.push_back(Triple::x86_64); 49 50 // See https://llvm.org/bugs/show_bug.cgi?id=25829 51 UnsupportedArchs.push_back(Triple::ppc64le); 52 UnsupportedArchs.push_back(Triple::ppc64); 53 } 54 55 /// Make sure this thread not progress faster than the main thread. 56 void waitForMainThread() { 57 std::unique_lock<std::mutex> LockGuard(WaitMainThreadMutex); 58 WaitMainThread.wait(LockGuard, [&] { return MainThreadReady; }); 59 } 60 61 /// Set the readiness of the main thread. 62 void setMainThreadReady() { 63 { 64 std::unique_lock<std::mutex> LockGuard(WaitMainThreadMutex); 65 MainThreadReady = true; 66 } 67 WaitMainThread.notify_all(); 68 } 69 70 void SetUp() override { MainThreadReady = false; } 71 72 std::condition_variable WaitMainThread; 73 std::mutex WaitMainThreadMutex; 74 bool MainThreadReady = false; 75 }; 76 77 #define CHECK_UNSUPPORTED() \ 78 do { \ 79 if (isUnsupportedOSOrEnvironment()) \ 80 return; \ 81 } while (0); \ 82 83 TEST_F(ThreadPoolTest, AsyncBarrier) { 84 CHECK_UNSUPPORTED(); 85 // test that async & barrier work together properly. 86 87 std::atomic_int checked_in{0}; 88 89 ThreadPool Pool; 90 for (size_t i = 0; i < 5; ++i) { 91 Pool.async([this, &checked_in] { 92 waitForMainThread(); 93 ++checked_in; 94 }); 95 } 96 ASSERT_EQ(0, checked_in); 97 setMainThreadReady(); 98 Pool.wait(); 99 ASSERT_EQ(5, checked_in); 100 } 101 102 static void TestFunc(std::atomic_int &checked_in, int i) { checked_in += i; } 103 104 TEST_F(ThreadPoolTest, AsyncBarrierArgs) { 105 CHECK_UNSUPPORTED(); 106 // Test that async works with a function requiring multiple parameters. 107 std::atomic_int checked_in{0}; 108 109 ThreadPool Pool; 110 for (size_t i = 0; i < 5; ++i) { 111 Pool.async(TestFunc, std::ref(checked_in), i); 112 } 113 Pool.wait(); 114 ASSERT_EQ(10, checked_in); 115 } 116 117 TEST_F(ThreadPoolTest, Async) { 118 CHECK_UNSUPPORTED(); 119 ThreadPool Pool; 120 std::atomic_int i{0}; 121 Pool.async([this, &i] { 122 waitForMainThread(); 123 ++i; 124 }); 125 Pool.async([&i] { ++i; }); 126 ASSERT_NE(2, i.load()); 127 setMainThreadReady(); 128 Pool.wait(); 129 ASSERT_EQ(2, i.load()); 130 } 131 132 TEST_F(ThreadPoolTest, GetFuture) { 133 CHECK_UNSUPPORTED(); 134 ThreadPool Pool{2}; 135 std::atomic_int i{0}; 136 Pool.async([this, &i] { 137 waitForMainThread(); 138 ++i; 139 }); 140 // Force the future using get() 141 Pool.async([&i] { ++i; }).get(); 142 ASSERT_NE(2, i.load()); 143 setMainThreadReady(); 144 Pool.wait(); 145 ASSERT_EQ(2, i.load()); 146 } 147 148 TEST_F(ThreadPoolTest, PoolDestruction) { 149 CHECK_UNSUPPORTED(); 150 // Test that we are waiting on destruction 151 std::atomic_int checked_in{0}; 152 { 153 ThreadPool Pool; 154 for (size_t i = 0; i < 5; ++i) { 155 Pool.async([this, &checked_in] { 156 waitForMainThread(); 157 ++checked_in; 158 }); 159 } 160 ASSERT_EQ(0, checked_in); 161 setMainThreadReady(); 162 } 163 ASSERT_EQ(5, checked_in); 164 } 165