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_thread/sleep.h"
19 #include "pw_thread/thread.h"
20 #include "pw_unit_test/framework.h"
21
22 using pw::chrono::SystemClock;
23 using namespace std::chrono_literals;
24
25 namespace pw::this_thread {
26 namespace {
27
28 extern "C" {
29
30 // Functions defined in sleep_facade_test_c.c which call the API from C.
31 void pw_this_thread_CallSleepFor(pw_chrono_SystemClock_Duration sleep_duration);
32 void pw_this_thread_CallSleepUntil(pw_chrono_SystemClock_TimePoint wakeup_time);
33
34 } // extern "C"
35
36 // We can't control the SystemClock's period configuration, so just in case
37 // duration cannot be accurately expressed in integer ticks, round the
38 // duration up.
39 constexpr SystemClock::duration kRoundedArbitraryShortDuration =
40 SystemClock::for_at_least(42ms);
41 constexpr SystemClock::duration kRoundedArbitraryLongDuration =
42 SystemClock::for_at_least(1s);
43 constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryShortDurationInC =
44 PW_SYSTEM_CLOCK_MS(42);
45 constexpr pw_chrono_SystemClock_Duration kRoundedArbitraryLongDurationInC =
46 PW_SYSTEM_CLOCK_S(1);
47
TEST(Sleep,SleepForPositiveDuration)48 TEST(Sleep, SleepForPositiveDuration) {
49 // Ensure we are in a thread context, meaning we are permitted to sleep.
50 ASSERT_NE(get_id(), Thread::id());
51
52 SystemClock::time_point before = SystemClock::now();
53 sleep_for(kRoundedArbitraryShortDuration);
54 SystemClock::duration time_elapsed = SystemClock::now() - before;
55 EXPECT_GE(time_elapsed, kRoundedArbitraryShortDuration);
56 }
57
TEST(Sleep,SleepForZeroLengthDuration)58 TEST(Sleep, SleepForZeroLengthDuration) {
59 // Ensure we are in a thread context, meaning we are permitted to sleep.
60 ASSERT_NE(get_id(), Thread::id());
61
62 // Ensure it doesn't sleep when a zero length duration is used.
63 SystemClock::time_point before = SystemClock::now();
64 sleep_for(SystemClock::duration::zero());
65 SystemClock::duration time_elapsed = SystemClock::now() - before;
66 EXPECT_LT(time_elapsed, kRoundedArbitraryLongDuration);
67 }
68
TEST(Sleep,SleepForNegativeDuration)69 TEST(Sleep, SleepForNegativeDuration) {
70 // Ensure we are in a thread context, meaning we are permitted to sleep.
71 ASSERT_NE(get_id(), Thread::id());
72
73 // Ensure it doesn't sleep when a negative duration is used.
74 SystemClock::time_point before = SystemClock::now();
75 sleep_for(-kRoundedArbitraryLongDuration);
76 SystemClock::duration time_elapsed = SystemClock::now() - before;
77 EXPECT_LT(time_elapsed, kRoundedArbitraryLongDuration);
78 }
79
TEST(Sleep,SleepUntilFutureWakeupTime)80 TEST(Sleep, SleepUntilFutureWakeupTime) {
81 // Ensure we are in a thread context, meaning we are permitted to sleep.
82 ASSERT_NE(get_id(), Thread::id());
83
84 SystemClock::time_point deadline =
85 SystemClock::now() + kRoundedArbitraryShortDuration;
86 sleep_until(deadline);
87 EXPECT_GE(SystemClock::now(), deadline);
88 }
89
TEST(Sleep,SleepUntilCurrentWakeupTime)90 TEST(Sleep, SleepUntilCurrentWakeupTime) {
91 // Ensure we are in a thread context, meaning we are permitted to sleep.
92 ASSERT_NE(get_id(), Thread::id());
93
94 // Ensure it doesn't sleep when now is used.
95 SystemClock::time_point deadline =
96 SystemClock::now() + kRoundedArbitraryLongDuration;
97 sleep_until(SystemClock::now());
98 EXPECT_LT(SystemClock::now(), deadline);
99 }
100
TEST(Sleep,SleepUntilPastWakeupTime)101 TEST(Sleep, SleepUntilPastWakeupTime) {
102 // Ensure we are in a thread context, meaning we are permitted to sleep.
103 ASSERT_NE(get_id(), Thread::id());
104
105 // Ensure it doesn't sleep when a timestamp in the past is used.
106 SystemClock::time_point deadline =
107 SystemClock::now() + kRoundedArbitraryLongDuration;
108 sleep_until(SystemClock::now() - kRoundedArbitraryLongDuration);
109 EXPECT_LT(SystemClock::now(), deadline);
110 }
111
TEST(Sleep,SleepForPositiveDurationInC)112 TEST(Sleep, SleepForPositiveDurationInC) {
113 // Ensure we are in a thread context, meaning we are permitted to sleep.
114 ASSERT_NE(get_id(), Thread::id());
115
116 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
117 pw_this_thread_SleepFor(kRoundedArbitraryShortDurationInC);
118 pw_chrono_SystemClock_Duration time_elapsed =
119 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
120 EXPECT_GE(time_elapsed.ticks, kRoundedArbitraryShortDurationInC.ticks);
121 }
122
TEST(Sleep,SleepForZeroLengthDurationInC)123 TEST(Sleep, SleepForZeroLengthDurationInC) {
124 // Ensure we are in a thread context, meaning we are permitted to sleep.
125 ASSERT_NE(get_id(), Thread::id());
126
127 // Ensure it doesn't sleep when a zero length duration is used.
128 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
129 pw_this_thread_SleepFor(PW_SYSTEM_CLOCK_MS(0));
130 pw_chrono_SystemClock_Duration time_elapsed =
131 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
132 EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryLongDurationInC.ticks);
133 }
134
TEST(Sleep,SleepForNegativeDurationInC)135 TEST(Sleep, SleepForNegativeDurationInC) {
136 // Ensure we are in a thread context, meaning we are permitted to sleep.
137 ASSERT_NE(get_id(), Thread::id());
138
139 // Ensure it doesn't sleep when a negative duration is used.
140 pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
141 pw_this_thread_SleepFor(
142 PW_SYSTEM_CLOCK_MS(-kRoundedArbitraryLongDurationInC.ticks));
143 pw_chrono_SystemClock_Duration time_elapsed =
144 pw_chrono_SystemClock_TimeElapsed(before, pw_chrono_SystemClock_Now());
145 EXPECT_LT(time_elapsed.ticks, kRoundedArbitraryLongDurationInC.ticks);
146 }
147
TEST(Sleep,SleepUntilFutureWakeupTimeInC)148 TEST(Sleep, SleepUntilFutureWakeupTimeInC) {
149 // Ensure we are in a thread context, meaning we are permitted to sleep.
150 ASSERT_NE(get_id(), Thread::id());
151
152 pw_chrono_SystemClock_TimePoint deadline;
153 deadline.duration_since_epoch.ticks =
154 pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
155 kRoundedArbitraryShortDurationInC.ticks;
156 pw_this_thread_CallSleepUntil(deadline);
157 EXPECT_GE(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
158 deadline.duration_since_epoch.ticks);
159 }
160
TEST(Sleep,SleepUntilCurrentWakeupTimeInC)161 TEST(Sleep, SleepUntilCurrentWakeupTimeInC) {
162 // Ensure we are in a thread context, meaning we are permitted to sleep.
163 ASSERT_NE(get_id(), Thread::id());
164
165 // Ensure it doesn't sleep when now is used.
166 pw_chrono_SystemClock_TimePoint deadline;
167 deadline.duration_since_epoch.ticks =
168 pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
169 kRoundedArbitraryLongDurationInC.ticks;
170 pw_this_thread_CallSleepUntil(pw_chrono_SystemClock_Now());
171 EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
172 deadline.duration_since_epoch.ticks);
173 }
174
TEST(Sleep,SleepUntilPastWakeupTimeInC)175 TEST(Sleep, SleepUntilPastWakeupTimeInC) {
176 // Ensure we are in a thread context, meaning we are permitted to sleep.
177 ASSERT_NE(get_id(), Thread::id());
178
179 // Ensure it doesn't sleep when a timestamp in the past is used.
180 pw_chrono_SystemClock_TimePoint deadline;
181 deadline.duration_since_epoch.ticks =
182 pw_chrono_SystemClock_Now().duration_since_epoch.ticks +
183 kRoundedArbitraryLongDurationInC.ticks;
184 pw_chrono_SystemClock_TimePoint old_timestamp;
185 old_timestamp.duration_since_epoch.ticks =
186 pw_chrono_SystemClock_Now().duration_since_epoch.ticks -
187 kRoundedArbitraryLongDurationInC.ticks;
188 pw_this_thread_CallSleepUntil(old_timestamp);
189 EXPECT_LT(pw_chrono_SystemClock_Now().duration_since_epoch.ticks,
190 deadline.duration_since_epoch.ticks);
191 }
192
193 } // namespace
194 } // namespace pw::this_thread
195