1 // Copyright 2020 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 <chrono>
16
17 #include "pw_chrono/system_clock.h"
18 #include "pw_sync/binary_semaphore.h"
19 #include "pw_unit_test/framework.h"
20
21 using pw::chrono::SystemClock;
22 using namespace std::chrono_literals;
23
24 namespace pw::sync {
25 namespace {
26
27 extern "C" {
28
29 // Functions defined in binary_semaphore_facade_test_c.c which call the API
30 // from C.
31 void pw_sync_BinarySemaphore_CallRelease(pw_sync_BinarySemaphore* semaphore);
32 void pw_sync_BinarySemaphore_CallAcquire(pw_sync_BinarySemaphore* semaphore);
33 bool pw_sync_BinarySemaphore_CallTryAcquire(pw_sync_BinarySemaphore* semaphore);
34 bool pw_sync_BinarySemaphore_CallTryAcquireFor(
35 pw_sync_BinarySemaphore* semaphore, pw_chrono_SystemClock_Duration timeout);
36 bool pw_sync_BinarySemaphore_CallTryAcquireUntil(
37 pw_sync_BinarySemaphore* semaphore,
38 pw_chrono_SystemClock_TimePoint deadline);
39 ptrdiff_t pw_sync_BinarySemaphore_CallMax(void);
40
41 } // extern "C"
42
43 // We can't control the SystemClock's period configuration, so just in case
44 // duration cannot be accurately expressed in integer ticks, round the
45 // duration up.
46 constexpr SystemClock::duration kRoundedArbitraryDuration =
47 SystemClock::for_at_least(42ms);
48 constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryDurationInC =
49 PW_SYSTEM_CLOCK_MS(42);
50
TEST(BinarySemaphore,EmptyInitialState)51 TEST(BinarySemaphore, EmptyInitialState) {
52 BinarySemaphore semaphore;
53 EXPECT_FALSE(semaphore.try_acquire());
54 }
55
56 // TODO: b/235284163 - Add real concurrency tests once we have pw::thread.
57
TEST(BinarySemaphore,Release)58 TEST(BinarySemaphore, Release) {
59 BinarySemaphore semaphore;
60 semaphore.release();
61 semaphore.release();
62 semaphore.acquire();
63 // Ensure it fails when empty.
64 EXPECT_FALSE(semaphore.try_acquire());
65 }
66
67 BinarySemaphore empty_initial_semaphore;
TEST(BinarySemaphore,EmptyInitialStateStatic)68 TEST(BinarySemaphore, EmptyInitialStateStatic) {
69 EXPECT_FALSE(empty_initial_semaphore.try_acquire());
70 }
71
72 BinarySemaphore release_semaphore;
TEST(BinarySemaphore,ReleaseStatic)73 TEST(BinarySemaphore, ReleaseStatic) {
74 release_semaphore.release();
75 release_semaphore.release();
76 release_semaphore.acquire();
77 // Ensure it fails when empty.
78 EXPECT_FALSE(release_semaphore.try_acquire());
79 }
80
TEST(BinarySemaphore,TryAcquireForFull)81 TEST(BinarySemaphore, TryAcquireForFull) {
82 BinarySemaphore semaphore;
83 semaphore.release();
84
85 // Ensure it doesn't block and succeeds if not empty.
86 SystemClock::time_point before = SystemClock::now();
87 EXPECT_TRUE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
88 SystemClock::duration time_elapsed = SystemClock::now() - before;
89 EXPECT_LT(time_elapsed, kRoundedArbitraryDuration);
90 }
91
TEST(BinarySemaphore,TryAcquireForEmptyPositiveTimeout)92 TEST(BinarySemaphore, TryAcquireForEmptyPositiveTimeout) {
93 BinarySemaphore semaphore;
94
95 // Ensure it blocks and fails when empty.
96 SystemClock::time_point before = SystemClock::now();
97 EXPECT_FALSE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
98 SystemClock::duration time_elapsed = SystemClock::now() - before;
99 EXPECT_GE(time_elapsed, kRoundedArbitraryDuration);
100 }
101
TEST(BinarySemaphore,TryAcquireForEmptyZeroLengthTimeout)102 TEST(BinarySemaphore, TryAcquireForEmptyZeroLengthTimeout) {
103 BinarySemaphore semaphore;
104
105 // Ensure it doesn't block and fails when empty and a zero length duration is
106 // used.
107 SystemClock::time_point before = SystemClock::now();
108 EXPECT_FALSE(semaphore.try_acquire_for(SystemClock::duration::zero()));
109 SystemClock::duration time_elapsed = SystemClock::now() - before;
110 EXPECT_LT(time_elapsed, kRoundedArbitraryDuration);
111 }
112
TEST(BinarySemaphore,TryAcquireForEmptyNegativeTimeout)113 TEST(BinarySemaphore, TryAcquireForEmptyNegativeTimeout) {
114 BinarySemaphore semaphore;
115
116 // Ensure it doesn't block and fails when empty and a negative duration is
117 // used.
118 SystemClock::time_point before = SystemClock::now();
119 EXPECT_FALSE(semaphore.try_acquire_for(-kRoundedArbitraryDuration));
120 SystemClock::duration time_elapsed = SystemClock::now() - before;
121 EXPECT_LT(time_elapsed, kRoundedArbitraryDuration);
122 }
123
TEST(BinarySemaphore,TryAcquireUntilFull)124 TEST(BinarySemaphore, TryAcquireUntilFull) {
125 BinarySemaphore semaphore;
126 semaphore.release();
127
128 // Ensure it doesn't block and succeeds if not empty.
129 SystemClock::time_point deadline =
130 SystemClock::now() + kRoundedArbitraryDuration;
131 EXPECT_TRUE(semaphore.try_acquire_until(deadline));
132 EXPECT_LT(SystemClock::now(), deadline);
133 }
134
TEST(BinarySemaphore,TryAcquireUntilEmptyFutureDeadline)135 TEST(BinarySemaphore, TryAcquireUntilEmptyFutureDeadline) {
136 BinarySemaphore semaphore;
137
138 // Ensure it blocks and fails when empty.
139 SystemClock::time_point deadline =
140 SystemClock::now() + kRoundedArbitraryDuration;
141 EXPECT_FALSE(semaphore.try_acquire_until(deadline));
142 EXPECT_GE(SystemClock::now(), deadline);
143 }
144
TEST(BinarySemaphore,TryAcquireUntilEmptyCurrentDeadline)145 TEST(BinarySemaphore, TryAcquireUntilEmptyCurrentDeadline) {
146 BinarySemaphore semaphore;
147
148 // Ensure it doesn't block and fails when empty and now is used.
149 SystemClock::time_point deadline =
150 SystemClock::now() + kRoundedArbitraryDuration;
151 EXPECT_FALSE(semaphore.try_acquire_until(SystemClock::now()));
152 EXPECT_LT(SystemClock::now(), deadline);
153 }
154
TEST(BinarySemaphore,TryAcquireUntilEmptyPastDeadline)155 TEST(BinarySemaphore, TryAcquireUntilEmptyPastDeadline) {
156 BinarySemaphore semaphore;
157
158 // Ensure it doesn't block and fails when empty and a timestamp in the past is
159 // used.
160 SystemClock::time_point deadline =
161 SystemClock::now() + kRoundedArbitraryDuration;
162 EXPECT_FALSE(semaphore.try_acquire_until(SystemClock::now() -
163 kRoundedArbitraryDuration));
164 EXPECT_LT(SystemClock::now(), deadline);
165 }
166
TEST(BinarySemaphore,EmptyInitialStateInC)167 TEST(BinarySemaphore, EmptyInitialStateInC) {
168 BinarySemaphore semaphore;
169 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquire(&semaphore));
170 }
171
TEST(BinarySemaphore,ReleaseInC)172 TEST(BinarySemaphore, ReleaseInC) {
173 BinarySemaphore semaphore;
174 pw_sync_BinarySemaphore_CallRelease(&semaphore);
175 pw_sync_BinarySemaphore_CallRelease(&semaphore);
176 pw_sync_BinarySemaphore_CallAcquire(&semaphore);
177 // Ensure it fails when empty.
178 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquire(&semaphore));
179 }
180
TEST(BinarySemaphore,TryAcquireForFullInC)181 TEST(BinarySemaphore, TryAcquireForFullInC) {
182 BinarySemaphore semaphore;
183 pw_sync_BinarySemaphore_CallRelease(&semaphore);
184
185 // Ensure it doesn't block and succeeds if not empty.
186 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
187 ASSERT_TRUE(pw_sync_BinarySemaphore_CallTryAcquireFor(
188 &semaphore, kRoundedArbitraryDurationInC));
189 pw_chrono_SystemClock_Duration time_elapsed =
190 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
191 EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
192 }
193
TEST(BinarySemaphore,TryAcquireForEmptyPositiveTimeoutInC)194 TEST(BinarySemaphore, TryAcquireForEmptyPositiveTimeoutInC) {
195 BinarySemaphore semaphore;
196
197 // Ensure it blocks and fails when empty.
198 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
199 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquireFor(
200 &semaphore, kRoundedArbitraryDurationInC));
201 pw_chrono_SystemClock_Duration time_elapsed =
202 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
203 EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
204 }
205
TEST(BinarySemaphore,TryAcquireForEmptyZeroLengthTimeoutInC)206 TEST(BinarySemaphore, TryAcquireForEmptyZeroLengthTimeoutInC) {
207 BinarySemaphore semaphore;
208
209 // Ensure it doesn't block and fails when empty and a zero length duration is
210 // used.
211 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
212 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquireFor(
213 &semaphore, PW_SYSTEM_CLOCK_MS(0)));
214 pw_chrono_SystemClock_Duration time_elapsed =
215 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
216 EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
217 }
218
TEST(BinarySemaphore,TryAcquireForEmptyNegativeTimeoutInC)219 TEST(BinarySemaphore, TryAcquireForEmptyNegativeTimeoutInC) {
220 BinarySemaphore semaphore;
221
222 // Ensure it doesn't block and fails when empty and a negative duration is
223 // used.
224 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
225 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquireFor(
226 &semaphore, PW_SYSTEM_CLOCK_MS(-kRoundedArbitraryDurationInC.ticks)));
227 pw_chrono_SystemClock_Duration time_elapsed =
228 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
229 EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
230 }
231
TEST(BinarySemaphore,TryAcquireUntilFullInC)232 TEST(BinarySemaphore, TryAcquireUntilFullInC) {
233 BinarySemaphore semaphore;
234 pw_sync_BinarySemaphore_CallRelease(&semaphore);
235
236 pw_chrono_SystemClock_TimePoint deadline;
237 deadline.duration_since_epoch.ticks =
238 pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
239 kRoundedArbitraryDurationInC.ticks;
240 ASSERT_TRUE(
241 pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
242 EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
243 deadline.duration_since_epoch.ticks);
244 }
245
TEST(BinarySemaphore,TryAcquireUntilEmptyFutureDeadlineInC)246 TEST(BinarySemaphore, TryAcquireUntilEmptyFutureDeadlineInC) {
247 BinarySemaphore semaphore;
248
249 // Ensure it blocks and fails when empty.
250 pw_chrono_SystemClock_TimePoint deadline;
251 deadline.duration_since_epoch.ticks =
252 pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
253 kRoundedArbitraryDurationInC.ticks;
254 EXPECT_FALSE(
255 pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
256 EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
257 deadline.duration_since_epoch.ticks);
258 }
259
TEST(BinarySemaphore,TryAcquireUntilEmptyCurrentDeadlineInC)260 TEST(BinarySemaphore, TryAcquireUntilEmptyCurrentDeadlineInC) {
261 BinarySemaphore semaphore;
262
263 // Ensure it doesn't block and fails when empty and now is used.
264 pw_chrono_SystemClock_TimePoint deadline;
265 deadline.duration_since_epoch.ticks =
266 pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
267 kRoundedArbitraryDurationInC.ticks;
268 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquireUntil(
269 &semaphore, pw_chrono_SystemClock_Now()));
270 EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
271 deadline.duration_since_epoch.ticks);
272 }
273
TEST(BinarySemaphore,TryAcquireUntilEmptyPastDeadlineInC)274 TEST(BinarySemaphore, TryAcquireUntilEmptyPastDeadlineInC) {
275 BinarySemaphore semaphore;
276
277 // Ensure it doesn't block and fails when empty and a timestamp in the past is
278 // used.
279 pw_chrono_SystemClock_TimePoint deadline;
280 deadline.duration_since_epoch.ticks =
281 pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
282 kRoundedArbitraryDurationInC.ticks;
283 pw_chrono_SystemClock_TimePoint old_timestamp;
284 old_timestamp.duration_since_epoch.ticks =
285 pw_chrono_SystemClock_Now().duration_since_epoch.ticks -
286 kRoundedArbitraryDurationInC.ticks;
287 EXPECT_FALSE(
288 pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, old_timestamp));
289 EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
290 deadline.duration_since_epoch.ticks);
291 }
292
TEST(BinarySemaphore,MaxInC)293 TEST(BinarySemaphore, MaxInC) {
294 EXPECT_EQ(BinarySemaphore::max(), pw_sync_BinarySemaphore_Max());
295 }
296
297 } // namespace
298 } // namespace pw::sync
299