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