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