1 | //===- unittests/Threading.cpp - Thread 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/Threading.h" |
10 | #include "llvm/Support/thread.h" |
11 | #include "llvm/TargetParser/Host.h" |
12 | #include "llvm/TargetParser/Triple.h" |
13 | #include "gtest/gtest.h" |
14 | |
15 | #include <atomic> |
16 | #include <condition_variable> |
17 | |
18 | using namespace llvm; |
19 | |
20 | namespace { |
21 | |
22 | static bool isThreadingSupportedArchAndOS() { |
23 | #if LLVM_ENABLE_THREADS |
24 | Triple Host(Triple::normalize(Str: sys::getProcessTriple())); |
25 | |
26 | // Initially this is only testing detection of the number of |
27 | // physical cores, which is currently only supported/tested on |
28 | // some systems. |
29 | return (Host.isOSWindows() && llvm_is_multithreaded()) || Host.isOSDarwin() || |
30 | (Host.isX86() && Host.isOSLinux()) || |
31 | (Host.isOSLinux() && !Host.isAndroid()) || |
32 | (Host.isSystemZ() && Host.isOSzOS()) || Host.isOSAIX(); |
33 | #else |
34 | return false; |
35 | #endif |
36 | } |
37 | |
38 | TEST(Threading, PhysicalConcurrency) { |
39 | auto Num = heavyweight_hardware_concurrency(); |
40 | // Since Num is unsigned this will also catch us trying to |
41 | // return -1. |
42 | ASSERT_LE(Num.compute_thread_count(), |
43 | hardware_concurrency().compute_thread_count()); |
44 | } |
45 | |
46 | TEST(Threading, NumPhysicalCoresSupported) { |
47 | if (!isThreadingSupportedArchAndOS()) |
48 | GTEST_SKIP(); |
49 | int Num = get_physical_cores(); |
50 | ASSERT_GT(Num, 0); |
51 | } |
52 | |
53 | TEST(Threading, NumPhysicalCoresUnsupported) { |
54 | if (isThreadingSupportedArchAndOS()) |
55 | GTEST_SKIP(); |
56 | int Num = get_physical_cores(); |
57 | ASSERT_EQ(Num, -1); |
58 | } |
59 | |
60 | #if LLVM_ENABLE_THREADS |
61 | |
62 | class Notification { |
63 | public: |
64 | void notify() { |
65 | { |
66 | std::lock_guard<std::mutex> Lock(M); |
67 | Notified = true; |
68 | // Broadcast with the lock held, so it's safe to destroy the Notification |
69 | // after wait() returns. |
70 | CV.notify_all(); |
71 | } |
72 | } |
73 | |
74 | bool wait() { |
75 | std::unique_lock<std::mutex> Lock(M); |
76 | using steady_clock = std::chrono::steady_clock; |
77 | auto Deadline = steady_clock::now() + |
78 | std::chrono::duration_cast<steady_clock::duration>( |
79 | d: std::chrono::duration<double>(5)); |
80 | return CV.wait_until(lock&: Lock, atime: Deadline, p: [this] { return Notified; }); |
81 | } |
82 | |
83 | private: |
84 | bool Notified = false; |
85 | mutable std::condition_variable CV; |
86 | mutable std::mutex M; |
87 | }; |
88 | |
89 | TEST(Threading, RunOnThreadSyncAsync) { |
90 | Notification ThreadStarted, ThreadAdvanced, ThreadFinished; |
91 | |
92 | auto ThreadFunc = [&] { |
93 | ThreadStarted.notify(); |
94 | ASSERT_TRUE(ThreadAdvanced.wait()); |
95 | ThreadFinished.notify(); |
96 | }; |
97 | |
98 | llvm::thread Thread(ThreadFunc); |
99 | Thread.detach(); |
100 | ASSERT_TRUE(ThreadStarted.wait()); |
101 | ThreadAdvanced.notify(); |
102 | ASSERT_TRUE(ThreadFinished.wait()); |
103 | } |
104 | |
105 | TEST(Threading, RunOnThreadSync) { |
106 | std::atomic_bool Executed(false); |
107 | llvm::thread Thread( |
108 | [](void *Arg) { *static_cast<std::atomic_bool *>(Arg) = true; }, |
109 | &Executed); |
110 | Thread.join(); |
111 | ASSERT_EQ(Executed, true); |
112 | } |
113 | |
114 | #if defined(__APPLE__) |
115 | TEST(Threading, AppleStackSize) { |
116 | llvm::thread Thread([] { |
117 | volatile unsigned char Var[8 * 1024 * 1024 - 10240]; |
118 | Var[0] = 0xff; |
119 | ASSERT_EQ(Var[0], 0xff); |
120 | }); |
121 | Thread.join(); |
122 | } |
123 | #endif |
124 | #endif |
125 | |
126 | } // namespace |
127 | |