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/binary_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 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,
36 pw_chrono_SystemClock_Duration for_at_least);
37 bool pw_sync_BinarySemaphore_CallTryAcquireUntil(
38 pw_sync_BinarySemaphore* semaphore,
39 pw_chrono_SystemClock_TimePoint until_at_least);
40 ptrdiff_t pw_sync_BinarySemaphore_CallMax(void);
41
42 } // extern "C"
43
44 // We can't control the SystemClock's period configuration, so just in case
45 // duration cannot be accurately expressed in integer ticks, round the
46 // duration up.
47 constexpr SystemClock::duration kRoundedArbitraryDuration =
48 SystemClock::for_at_least(42ms);
49 constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryDurationInC =
50 PW_SYSTEM_CLOCK_MS(42);
51
TEST(BinarySemaphore,EmptyInitialState)52 TEST(BinarySemaphore, EmptyInitialState) {
53 BinarySemaphore semaphore;
54 EXPECT_FALSE(semaphore.try_acquire());
55 }
56
57 // TODO(pwbug/291): Add real concurrency tests once we have pw::thread.
58
TEST(BinarySemaphore,Release)59 TEST(BinarySemaphore, Release) {
60 BinarySemaphore semaphore;
61 semaphore.release();
62 semaphore.release();
63 semaphore.acquire();
64 // Ensure it fails when empty.
65 EXPECT_FALSE(semaphore.try_acquire());
66 }
67
68 BinarySemaphore empty_initial_semaphore;
TEST(BinarySemaphore,EmptyInitialStateStatic)69 TEST(BinarySemaphore, EmptyInitialStateStatic) {
70 EXPECT_FALSE(empty_initial_semaphore.try_acquire());
71 }
72
73 BinarySemaphore release_semaphore;
TEST(BinarySemaphore,ReleaseStatic)74 TEST(BinarySemaphore, ReleaseStatic) {
75 release_semaphore.release();
76 release_semaphore.release();
77 release_semaphore.acquire();
78 // Ensure it fails when empty.
79 EXPECT_FALSE(release_semaphore.try_acquire());
80 }
81
TEST(BinarySemaphore,TryAcquireFor)82 TEST(BinarySemaphore, TryAcquireFor) {
83 BinarySemaphore semaphore;
84 semaphore.release();
85
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 // Ensure it blocks and fails when empty.
92 before = SystemClock::now();
93 EXPECT_FALSE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
94 time_elapsed = SystemClock::now() - before;
95 EXPECT_GE(time_elapsed, kRoundedArbitraryDuration);
96 }
97
TEST(BinarySemaphore,TryAcquireUntil)98 TEST(BinarySemaphore, TryAcquireUntil) {
99 BinarySemaphore semaphore;
100 semaphore.release();
101
102 const SystemClock::time_point deadline =
103 SystemClock::now() + kRoundedArbitraryDuration;
104 EXPECT_TRUE(semaphore.try_acquire_until(deadline));
105 EXPECT_LT(SystemClock::now(), deadline);
106
107 // Ensure it blocks and fails when empty.
108 EXPECT_FALSE(semaphore.try_acquire_until(deadline));
109 EXPECT_GE(SystemClock::now(), deadline);
110 }
111
TEST(BinarySemaphore,EmptyInitialStateInC)112 TEST(BinarySemaphore, EmptyInitialStateInC) {
113 BinarySemaphore semaphore;
114 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquire(&semaphore));
115 }
116
TEST(BinarySemaphore,ReleaseInC)117 TEST(BinarySemaphore, ReleaseInC) {
118 BinarySemaphore semaphore;
119 pw_sync_BinarySemaphore_CallRelease(&semaphore);
120 pw_sync_BinarySemaphore_CallRelease(&semaphore);
121 pw_sync_BinarySemaphore_CallAcquire(&semaphore);
122 // Ensure it fails when empty.
123 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquire(&semaphore));
124 }
125
TEST(BinarySemaphore,TryAcquireForInC)126 TEST(BinarySemaphore, TryAcquireForInC) {
127 BinarySemaphore semaphore;
128 pw_sync_BinarySemaphore_CallRelease(&semaphore);
129
130 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
131 ASSERT_TRUE(pw_sync_BinarySemaphore_CallTryAcquireFor(
132 &semaphore, kRoundedArbitraryDurationInC));
133 pw_chrono_SystemClock_Duration time_elapsed =
134 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
135 EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
136
137 // Ensure it blocks and fails when empty.
138 before = pw_chrono_SystemClock_Now();
139 EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquireFor(
140 &semaphore, kRoundedArbitraryDurationInC));
141 time_elapsed =
142 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
143 EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryDurationInC.ticks);
144 }
145
TEST(BinarySemaphore,TryAcquireUntilInC)146 TEST(BinarySemaphore, TryAcquireUntilInC) {
147 BinarySemaphore semaphore;
148 pw_sync_BinarySemaphore_CallRelease(&semaphore);
149
150 pw_chrono_SystemClock_TimePoint deadline;
151 deadline.duration_since_epoch = {
152 .ticks = pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
153 kRoundedArbitraryDurationInC.ticks,
154 };
155 ASSERT_TRUE(
156 pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
157 EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
158 deadline.duration_since_epoch.ticks);
159
160 // Ensure it blocks and fails when empty.
161 EXPECT_FALSE(
162 pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
163 EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
164 deadline.duration_since_epoch.ticks);
165 }
166
TEST(BinarySemaphore,MaxInC)167 TEST(BinarySemaphore, MaxInC) {
168 EXPECT_EQ(BinarySemaphore::max(), pw_sync_BinarySemaphore_Max());
169 }
170
171 } // namespace
172 } // namespace pw::sync
173