1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_sync/binary_semaphore.h"
16 #include "pw_thread/id.h"
17 #include "pw_thread/non_portable_test_thread_options.h"
18 #include "pw_thread/thread.h"
19 #include "pw_unit_test/framework.h"
20
21 namespace {
22
23 using ::pw::sync::BinarySemaphore;
24 using ::pw::thread::Id;
25 using ::pw::thread::Thread;
26 using ::pw::thread::ThreadCore;
27 using ::pw::thread::test::TestOptionsThread0;
28 using ::pw::thread::test::TestOptionsThread1;
29 using ::pw::thread::test::WaitUntilDetachedThreadsCleanedUp;
30
TEST(Thread,DefaultIds)31 TEST(Thread, DefaultIds) {
32 Thread not_executing_thread;
33 EXPECT_EQ(not_executing_thread.get_id(), Id());
34 }
35
36 #if PW_THREAD_JOINING_ENABLED
TEST(Thread,DefaultConstructedThreadIsNotJoinable)37 TEST(Thread, DefaultConstructedThreadIsNotJoinable) {
38 Thread thread;
39 EXPECT_FALSE(thread.joinable());
40 }
41
TEST(Thread,JoinWaitsForLambdaCompletion)42 TEST(Thread, JoinWaitsForLambdaCompletion) {
43 BinarySemaphore thread_ran;
44 Thread thread(TestOptionsThread0(), [&thread_ran] { thread_ran.release(); });
45 EXPECT_TRUE(thread.joinable());
46 thread.join();
47 EXPECT_EQ(thread.get_id(), Id());
48 EXPECT_TRUE(thread_ran.try_acquire());
49 }
50
ReleaseBinarySemaphore(void * arg)51 static void ReleaseBinarySemaphore(void* arg) {
52 static_cast<BinarySemaphore*>(arg)->release();
53 }
54
TEST(Thread,JoinWaitsForDeprecatedFunctionPointerCompletion)55 TEST(Thread, JoinWaitsForDeprecatedFunctionPointerCompletion) {
56 BinarySemaphore thread_ran;
57 Thread thread(TestOptionsThread0(), ReleaseBinarySemaphore, &thread_ran);
58 EXPECT_TRUE(thread.joinable());
59 thread.join();
60 EXPECT_EQ(thread.get_id(), Id());
61 EXPECT_TRUE(thread_ran.try_acquire());
62 }
63 #endif // PW_THREAD_JOINING_ENABLED
64
TEST(Thread,DetachAllowsThreadToRunAfterExitingScope)65 TEST(Thread, DetachAllowsThreadToRunAfterExitingScope) {
66 struct {
67 BinarySemaphore thread_blocker;
68 BinarySemaphore thread_finished;
69 } semaphores;
70 {
71 Thread thread(TestOptionsThread0(), [&semaphores] {
72 semaphores.thread_blocker.acquire();
73 semaphores.thread_finished.release();
74 });
75 EXPECT_NE(thread.get_id(), Id());
76 EXPECT_TRUE(thread.joinable());
77 thread.detach();
78 EXPECT_EQ(thread.get_id(), Id());
79 EXPECT_FALSE(thread.joinable());
80 }
81 EXPECT_FALSE(semaphores.thread_finished.try_acquire());
82 semaphores.thread_blocker.release();
83 semaphores.thread_finished.acquire();
84
85 WaitUntilDetachedThreadsCleanedUp();
86 }
87
TEST(Thread,SwapWithoutExecution)88 TEST(Thread, SwapWithoutExecution) {
89 Thread thread_0;
90 Thread thread_1;
91
92 // Make sure we can swap threads which are not associated with any execution.
93 thread_0.swap(thread_1);
94 }
95
TEST(Thread,SwapWithOneExecuting)96 TEST(Thread, SwapWithOneExecuting) {
97 Thread thread_0;
98 EXPECT_EQ(thread_0.get_id(), Id());
99
100 BinarySemaphore thread_ran_sem;
101 Thread thread_1(TestOptionsThread1(),
102 [&thread_ran_sem] { thread_ran_sem.release(); });
103
104 EXPECT_NE(thread_1.get_id(), Id());
105
106 thread_0.swap(thread_1);
107 EXPECT_NE(thread_0.get_id(), Id());
108 EXPECT_EQ(thread_1.get_id(), Id());
109
110 thread_0.detach();
111 EXPECT_EQ(thread_0.get_id(), Id());
112
113 thread_ran_sem.acquire();
114 WaitUntilDetachedThreadsCleanedUp();
115 }
116
TEST(Thread,SwapWithTwoExecuting)117 TEST(Thread, SwapWithTwoExecuting) {
118 BinarySemaphore thread_a_ran_sem;
119 Thread thread_0(TestOptionsThread0(),
120 [&thread_a_ran_sem] { thread_a_ran_sem.release(); });
121 BinarySemaphore thread_b_ran_sem;
122 Thread thread_1(TestOptionsThread1(),
123 [&thread_b_ran_sem] { thread_b_ran_sem.release(); });
124 const Id thread_a_id = thread_0.get_id();
125 EXPECT_NE(thread_a_id, Id());
126 const Id thread_b_id = thread_1.get_id();
127 EXPECT_NE(thread_b_id, Id());
128 EXPECT_NE(thread_a_id, thread_b_id);
129
130 thread_0.swap(thread_1);
131 EXPECT_EQ(thread_1.get_id(), thread_a_id);
132 EXPECT_EQ(thread_0.get_id(), thread_b_id);
133
134 thread_0.detach();
135 EXPECT_EQ(thread_0.get_id(), Id());
136 thread_1.detach();
137 EXPECT_EQ(thread_1.get_id(), Id());
138
139 thread_a_ran_sem.acquire();
140 thread_b_ran_sem.acquire();
141 WaitUntilDetachedThreadsCleanedUp();
142 }
143
TEST(Thread,MoveOperator)144 TEST(Thread, MoveOperator) {
145 Thread thread_0;
146 EXPECT_EQ(thread_0.get_id(), Id());
147
148 BinarySemaphore thread_ran_sem;
149 Thread thread_1(TestOptionsThread1(),
150 [&thread_ran_sem] { thread_ran_sem.release(); });
151 EXPECT_NE(thread_1.get_id(), Id());
152
153 thread_0 = std::move(thread_1);
154 EXPECT_NE(thread_0.get_id(), Id());
155 #ifndef __clang_analyzer__
156 EXPECT_EQ(thread_1.get_id(), Id());
157 #endif // ignore use-after-move
158
159 thread_0.detach();
160 EXPECT_EQ(thread_0.get_id(), Id());
161
162 thread_ran_sem.acquire();
163 WaitUntilDetachedThreadsCleanedUp();
164 }
165
166 class SemaphoreReleaser : public ThreadCore {
167 public:
semaphore()168 BinarySemaphore& semaphore() { return semaphore_; }
169
170 private:
Run()171 void Run() override { semaphore_.release(); }
172
173 BinarySemaphore semaphore_;
174 };
175
TEST(Thread,ThreadCore)176 TEST(Thread, ThreadCore) {
177 SemaphoreReleaser semaphore_releaser;
178 Thread thread(TestOptionsThread0(), semaphore_releaser);
179 EXPECT_NE(thread.get_id(), Id());
180 EXPECT_TRUE(thread.joinable());
181 thread.detach();
182 EXPECT_EQ(thread.get_id(), Id());
183 EXPECT_FALSE(thread.joinable());
184 semaphore_releaser.semaphore().acquire();
185
186 WaitUntilDetachedThreadsCleanedUp();
187 }
188
189 } // namespace
190