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 "gtest/gtest.h"
18 #include "pw_chrono/system_clock.h"
19 #include "pw_sync/counting_semaphore.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 for_at_least);
42 bool pw_sync_CountingSemaphore_CallTryAcquireUntil(
43 pw_sync_CountingSemaphore* semaphore,
44 pw_chrono_SystemClock_TimePoint until_at_least);
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(pwbug/291): 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,TryAcquireFor)100 TEST(CountingSemaphore, TryAcquireFor) {
101 CountingSemaphore semaphore;
102 semaphore.release();
103
104 SystemClock::time_point before = SystemClock::now();
105 EXPECT_TRUE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
106 SystemClock::duration time_elapsed = SystemClock::now() - before;
107 EXPECT_LT(time_elapsed, kRoundedArbitraryDuration);
108
109 // Ensure it blocks and fails when empty.
110 before = SystemClock::now();
111 EXPECT_FALSE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
112 time_elapsed = SystemClock::now() - before;
113 EXPECT_GE(time_elapsed, kRoundedArbitraryDuration);
114 }
115
TEST(CountingSemaphore,TryAcquireUntil)116 TEST(CountingSemaphore, TryAcquireUntil) {
117 CountingSemaphore semaphore;
118 semaphore.release();
119
120 const SystemClock::time_point deadline =
121 SystemClock::now() + kRoundedArbitraryDuration;
122 EXPECT_TRUE(semaphore.try_acquire_until(deadline));
123 EXPECT_LT(SystemClock::now(), deadline);
124
125 // Ensure it blocks and fails when empty.
126 EXPECT_FALSE(semaphore.try_acquire_until(deadline));
127 EXPECT_GE(SystemClock::now(), deadline);
128 }
129
TEST(CountingSemaphore,EmptyInitialStateInC)130 TEST(CountingSemaphore, EmptyInitialStateInC) {
131 CountingSemaphore semaphore;
132 EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquire(&semaphore));
133 }
134
TEST(CountingSemaphore,SingeReleaseInC)135 TEST(CountingSemaphore, SingeReleaseInC) {
136 CountingSemaphore semaphore;
137 pw_sync_CountingSemaphore_CallRelease(&semaphore);
138 pw_sync_CountingSemaphore_CallRelease(&semaphore);
139 pw_sync_CountingSemaphore_CallAcquire(&semaphore);
140 pw_sync_CountingSemaphore_CallAcquire(&semaphore);
141 // Ensure it fails when empty.
142 EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquire(&semaphore));
143 }
144
TEST(CountingSemaphore,MultiReleaseInC)145 TEST(CountingSemaphore, MultiReleaseInC) {
146 CountingSemaphore semaphore;
147 pw_sync_CountingSemaphore_CallReleaseNum(&semaphore, 2);
148 pw_sync_CountingSemaphore_CallReleaseNum(&semaphore, 1);
149 pw_sync_CountingSemaphore_CallAcquire(&semaphore);
150 pw_sync_CountingSemaphore_CallAcquire(&semaphore);
151 pw_sync_CountingSemaphore_CallAcquire(&semaphore);
152 // Ensure it fails when empty.
153 EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquire(&semaphore));
154 }
155
TEST(CountingSemaphore,TryAcquireForInC)156 TEST(CountingSemaphore, TryAcquireForInC) {
157 CountingSemaphore semaphore;
158 pw_sync_CountingSemaphore_CallRelease(&semaphore);
159
160 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
161 ASSERT_TRUE(pw_sync_CountingSemaphore_CallTryAcquireFor(
162 &semaphore, kRoundedArbitraryDurationInC));
163 pw_chrono_SystemClock_Duration time_elapsed =
164 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
165 EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
166
167 // Ensure it blocks and fails when empty.
168 before = pw_chrono_SystemClock_Now();
169 EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquireFor(
170 &semaphore, kRoundedArbitraryDurationInC));
171 time_elapsed =
172 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
173 EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
174 }
175
TEST(CountingSemaphore,TryAcquireUntilInC)176 TEST(CountingSemaphore, TryAcquireUntilInC) {
177 CountingSemaphore semaphore;
178 pw_sync_CountingSemaphore_CallRelease(&semaphore);
179
180 pw_chrono_SystemClock_TimePoint deadline;
181 deadline.duration_since_epoch = {
182 .ticks = pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
183 kRoundedArbitraryDurationInC.ticks,
184 };
185 ASSERT_TRUE(
186 pw_sync_CountingSemaphore_CallTryAcquireUntil(&semaphore, deadline));
187 EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
188 deadline.duration_since_epoch.ticks);
189
190 // Ensure it blocks and fails when empty.
191 EXPECT_FALSE(
192 pw_sync_CountingSemaphore_CallTryAcquireUntil(&semaphore, deadline));
193 EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
194 deadline.duration_since_epoch.ticks);
195 }
196
TEST(CountingSemaphore,MaxInC)197 TEST(CountingSemaphore, MaxInC) {
198 EXPECT_EQ(CountingSemaphore::max(), pw_sync_CountingSemaphore_Max());
199 }
200
201 } // namespace
202 } // namespace pw::sync
203