xref: /aosp_15_r20/external/pigweed/pw_thread/thread_facade_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/non_portable_test_thread_options.h"
17 #include "pw_thread/thread.h"
18 #include "pw_unit_test/framework.h"
19 
20 namespace {
21 
22 using ::pw::sync::BinarySemaphore;
23 using ::pw::thread::ThreadCore;
24 using ::pw::thread::test::TestOptionsThread0;
25 using ::pw::thread::test::TestOptionsThread1;
26 using ::pw::thread::test::WaitUntilDetachedThreadsCleanedUp;
27 
TEST(Thread,DefaultIds)28 TEST(Thread, DefaultIds) {
29   pw::Thread not_executing_thread;
30   EXPECT_EQ(not_executing_thread.get_id(), pw::Thread::id());
31 }
32 
33 #if PW_THREAD_JOINING_ENABLED
TEST(Thread,DefaultConstructedThreadIsNotJoinable)34 TEST(Thread, DefaultConstructedThreadIsNotJoinable) {
35   pw::Thread thread;
36   EXPECT_FALSE(thread.joinable());
37 }
38 
TEST(Thread,JoinWaitsForLambdaCompletion)39 TEST(Thread, JoinWaitsForLambdaCompletion) {
40   BinarySemaphore thread_ran;
41   pw::Thread thread(TestOptionsThread0(),
42                     [&thread_ran] { thread_ran.release(); });
43   EXPECT_TRUE(thread.joinable());
44   thread.join();
45   EXPECT_EQ(thread.get_id(), pw::Thread::id());
46   EXPECT_TRUE(thread_ran.try_acquire());
47 }
48 
49 #endif  // PW_THREAD_JOINING_ENABLED
50 
TEST(Thread,DetachAllowsThreadToRunAfterExitingScope)51 TEST(Thread, DetachAllowsThreadToRunAfterExitingScope) {
52   struct {
53     BinarySemaphore thread_blocker;
54     BinarySemaphore thread_finished;
55   } semaphores;
56   {
57     pw::Thread thread(TestOptionsThread0(), [&semaphores] {
58       semaphores.thread_blocker.acquire();
59       semaphores.thread_finished.release();
60     });
61     EXPECT_NE(thread.get_id(), pw::Thread::id());
62     EXPECT_TRUE(thread.joinable());
63     thread.detach();
64     EXPECT_EQ(thread.get_id(), pw::Thread::id());
65     EXPECT_FALSE(thread.joinable());
66   }
67   EXPECT_FALSE(semaphores.thread_finished.try_acquire());
68   semaphores.thread_blocker.release();
69   semaphores.thread_finished.acquire();
70 
71   WaitUntilDetachedThreadsCleanedUp();
72 }
73 
TEST(Thread,SwapWithoutExecution)74 TEST(Thread, SwapWithoutExecution) {
75   pw::Thread thread_0;
76   pw::Thread thread_1;
77 
78   // Make sure we can swap threads which are not associated with any execution.
79   thread_0.swap(thread_1);
80 }
81 
TEST(Thread,SwapWithOneExecuting)82 TEST(Thread, SwapWithOneExecuting) {
83   pw::Thread thread_0;
84   EXPECT_EQ(thread_0.get_id(), pw::Thread::id());
85 
86   BinarySemaphore thread_ran_sem;
87   pw::Thread thread_1(TestOptionsThread1(),
88                       [&thread_ran_sem] { thread_ran_sem.release(); });
89 
90   EXPECT_NE(thread_1.get_id(), pw::Thread::id());
91 
92   thread_0.swap(thread_1);
93   EXPECT_NE(thread_0.get_id(), pw::Thread::id());
94   EXPECT_EQ(thread_1.get_id(), pw::Thread::id());
95 
96   thread_0.detach();
97   EXPECT_EQ(thread_0.get_id(), pw::Thread::id());
98 
99   thread_ran_sem.acquire();
100   WaitUntilDetachedThreadsCleanedUp();
101 }
102 
TEST(Thread,SwapWithTwoExecuting)103 TEST(Thread, SwapWithTwoExecuting) {
104   BinarySemaphore thread_a_ran_sem;
105   pw::Thread thread_0(TestOptionsThread0(),
106                       [&thread_a_ran_sem] { thread_a_ran_sem.release(); });
107   BinarySemaphore thread_b_ran_sem;
108   pw::Thread thread_1(TestOptionsThread1(),
109                       [&thread_b_ran_sem] { thread_b_ran_sem.release(); });
110   const pw::Thread::id thread_a_id = thread_0.get_id();
111   EXPECT_NE(thread_a_id, pw::Thread::id());
112   const pw::Thread::id thread_b_id = thread_1.get_id();
113   EXPECT_NE(thread_b_id, pw::Thread::id());
114   EXPECT_NE(thread_a_id, thread_b_id);
115 
116   thread_0.swap(thread_1);
117   EXPECT_EQ(thread_1.get_id(), thread_a_id);
118   EXPECT_EQ(thread_0.get_id(), thread_b_id);
119 
120   thread_0.detach();
121   EXPECT_EQ(thread_0.get_id(), pw::Thread::id());
122   thread_1.detach();
123   EXPECT_EQ(thread_1.get_id(), pw::Thread::id());
124 
125   thread_a_ran_sem.acquire();
126   thread_b_ran_sem.acquire();
127   WaitUntilDetachedThreadsCleanedUp();
128 }
129 
TEST(Thread,MoveOperator)130 TEST(Thread, MoveOperator) {
131   pw::Thread thread_0;
132   EXPECT_EQ(thread_0.get_id(), pw::Thread::id());
133 
134   BinarySemaphore thread_ran_sem;
135   pw::Thread thread_1(TestOptionsThread1(),
136                       [&thread_ran_sem] { thread_ran_sem.release(); });
137   EXPECT_NE(thread_1.get_id(), pw::Thread::id());
138 
139   thread_0 = std::move(thread_1);
140   EXPECT_NE(thread_0.get_id(), pw::Thread::id());
141 #ifndef __clang_analyzer__
142   EXPECT_EQ(thread_1.get_id(), pw::Thread::id());
143 #endif  // ignore use-after-move
144 
145   thread_0.detach();
146   EXPECT_EQ(thread_0.get_id(), pw::Thread::id());
147 
148   thread_ran_sem.acquire();
149   WaitUntilDetachedThreadsCleanedUp();
150 }
151 
152 class SemaphoreReleaser : public ThreadCore {
153  public:
semaphore()154   BinarySemaphore& semaphore() { return semaphore_; }
155 
156  private:
Run()157   void Run() override { semaphore_.release(); }
158 
159   BinarySemaphore semaphore_;
160 };
161 
TEST(Thread,ThreadCore)162 TEST(Thread, ThreadCore) {
163   SemaphoreReleaser semaphore_releaser;
164   pw::Thread thread(TestOptionsThread0(), semaphore_releaser);
165   EXPECT_NE(thread.get_id(), pw::Thread::id());
166   EXPECT_TRUE(thread.joinable());
167   thread.detach();
168   EXPECT_EQ(thread.get_id(), pw::Thread::id());
169   EXPECT_FALSE(thread.joinable());
170   semaphore_releaser.semaphore().acquire();
171 
172   WaitUntilDetachedThreadsCleanedUp();
173 }
174 
175 }  // namespace
176