• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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