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