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