1 //
2 //
3 // Copyright 2019 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/lib/iomgr/timer.h"
20
21 #include <gtest/gtest.h>
22
23 #include <grpc/grpc.h>
24 #include <grpc/support/log.h>
25
26 #include "src/core/lib/gprpp/crash.h"
27 #include "src/core/lib/gprpp/time.h"
28 #include "src/core/lib/iomgr/closure.h"
29 #include "src/core/lib/iomgr/error.h"
30 #include "src/core/lib/iomgr/exec_ctx.h"
31 #include "src/core/lib/iomgr/timer_manager.h"
32 #include "test/core/util/test_config.h"
33
34 #ifdef GRPC_POSIX_SOCKET_EV
35 #include "src/core/lib/iomgr/ev_posix.h"
36 #endif
37
38 // MAYBE_SKIP_TEST is a macro to determine if this particular test configuration
39 // should be skipped based on a decision made at SetUp time.
40 #define MAYBE_SKIP_TEST \
41 do { \
42 if (do_not_test_) { \
43 return; \
44 } \
45 } while (0)
46
47 class TimerTest : public ::testing::Test {
48 protected:
SetUp()49 void SetUp() override {
50 grpc_init();
51 // Skip test if slowdown factor > 1, or we are
52 // using event manager.
53 #ifdef GRPC_POSIX_SOCKET_EV
54 if (grpc_test_slowdown_factor() != 1 ||
55 grpc_event_engine_run_in_background()) {
56 #else
57 if (grpc_test_slowdown_factor() != 1) {
58 #endif
59 do_not_test_ = true;
60 }
61 }
62
63 void TearDown() override { grpc_shutdown(); }
64
65 bool do_not_test_{false};
66 };
67
68 #ifndef GPR_WINDOWS
69 // the test fails with too many wakeups on windows opt build
70 // the mechanism by which that happens is described in
71 // https://github.com/grpc/grpc/issues/20436
TEST_F(TimerTest,NoTimers)72 TEST_F(TimerTest, NoTimers) {
73 MAYBE_SKIP_TEST;
74 grpc_core::ExecCtx exec_ctx;
75 gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1500));
76
77 // We expect to get 1 wakeup per second. Sometimes we also get a wakeup
78 // during initialization, so in 1.5 seconds we expect to get 1 or 2 wakeups.
79 int64_t wakeups = grpc_timer_manager_get_wakeups_testonly();
80 GPR_ASSERT(wakeups == 1 || wakeups == 2);
81 }
82 #endif
83
TEST_F(TimerTest,OneTimerExpires)84 TEST_F(TimerTest, OneTimerExpires) {
85 MAYBE_SKIP_TEST;
86 grpc_core::ExecCtx exec_ctx;
87 grpc_timer timer;
88 int timer_fired = 0;
89 grpc_timer_init(
90 &timer,
91 grpc_core::Timestamp::Now() + grpc_core::Duration::Milliseconds(500),
92 GRPC_CLOSURE_CREATE(
93 [](void* arg, grpc_error_handle) {
94 int* timer_fired = static_cast<int*>(arg);
95 ++*timer_fired;
96 },
97 &timer_fired, grpc_schedule_on_exec_ctx));
98 gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1500));
99 GPR_ASSERT(1 == timer_fired);
100
101 // We expect to get 1 wakeup/second + 1 wakeup for the expired timer + maybe 1
102 // wakeup during initialization. i.e. in 1.5 seconds we expect 2 or 3 wakeups.
103 // Actual number of wakeups is more due to bug
104 // https://github.com/grpc/grpc/issues/19947
105 int64_t wakeups = grpc_timer_manager_get_wakeups_testonly();
106 gpr_log(GPR_DEBUG, "wakeups: %" PRId64 "", wakeups);
107 }
108
TEST_F(TimerTest,MultipleTimersExpire)109 TEST_F(TimerTest, MultipleTimersExpire) {
110 MAYBE_SKIP_TEST;
111 grpc_core::ExecCtx exec_ctx;
112 const int kNumTimers = 10;
113 grpc_timer timers[kNumTimers];
114 int timer_fired = 0;
115 for (int i = 0; i < kNumTimers; ++i) {
116 grpc_timer_init(&timers[i],
117 grpc_core::Timestamp::Now() +
118 grpc_core::Duration::Milliseconds(500) +
119 grpc_core::Duration::Milliseconds(i),
120 GRPC_CLOSURE_CREATE(
121 [](void* arg, grpc_error_handle) {
122 int* timer_fired = static_cast<int*>(arg);
123 ++*timer_fired;
124 },
125 &timer_fired, grpc_schedule_on_exec_ctx));
126 }
127
128 gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1500));
129 GPR_ASSERT(kNumTimers == timer_fired);
130
131 // We expect to get 1 wakeup/second + 1 wakeup for per timer fired + maybe 1
132 // wakeup during initialization. i.e. in 1.5 seconds we expect 11 or 12
133 // wakeups. Actual number of wakeups is more due to bug
134 // https://github.com/grpc/grpc/issues/19947
135 int64_t wakeups = grpc_timer_manager_get_wakeups_testonly();
136 gpr_log(GPR_DEBUG, "wakeups: %" PRId64 "", wakeups);
137 }
138
TEST_F(TimerTest,CancelSomeTimers)139 TEST_F(TimerTest, CancelSomeTimers) {
140 MAYBE_SKIP_TEST;
141 grpc_core::ExecCtx exec_ctx;
142 const int kNumTimers = 10;
143 grpc_timer timers[kNumTimers];
144 std::atomic<int> timer_fired{0};
145 grpc_core::ExecCtx::Get()->InvalidateNow();
146 for (int i = 0; i < kNumTimers; ++i) {
147 // Set a large firing time for timers which are bound to be cancelled
148 // and set a small firing time for timers which need to execute.
149 grpc_timer_init(
150 &timers[i],
151 grpc_core::Timestamp::Now() +
152 ((i < kNumTimers / 2) ? grpc_core ::Duration::Milliseconds(60000)
153 : grpc_core ::Duration::Milliseconds(100) +
154 grpc_core::Duration::Milliseconds(i)),
155 GRPC_CLOSURE_CREATE(
156 [](void* arg, grpc_error_handle error) {
157 if (error == absl::CancelledError()) {
158 return;
159 }
160 std::atomic<int>* timer_fired =
161 static_cast<std::atomic<int>*>(arg);
162 ++*timer_fired;
163 },
164 &timer_fired, grpc_schedule_on_exec_ctx));
165 }
166 for (int i = 0; i < kNumTimers / 2; ++i) {
167 grpc_timer_cancel(&timers[i]);
168 }
169
170 gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1500));
171 GPR_ASSERT(kNumTimers / 2 == timer_fired);
172
173 // We expect to get 1 wakeup/second + 1 wakeup per timer fired + maybe 1
174 // wakeup during initialization. i.e. in 1.5 seconds we expect 6 or 7 wakeups.
175 // Actual number of wakeups is more due to bug
176 // https://github.com/grpc/grpc/issues/19947
177 int64_t wakeups = grpc_timer_manager_get_wakeups_testonly();
178 gpr_log(GPR_DEBUG, "wakeups: %" PRId64 "", wakeups);
179 }
180
181 // Enable the following test after
182 // https://github.com/grpc/grpc/issues/20049 has been fixed.
TEST_F(TimerTest,DISABLED_TimerNotCanceled)183 TEST_F(TimerTest, DISABLED_TimerNotCanceled) {
184 grpc_core::ExecCtx exec_ctx;
185 grpc_timer timer;
186 grpc_timer_init(
187 &timer, grpc_core::Timestamp::Now() + grpc_core::Duration::Seconds(10),
188 GRPC_CLOSURE_CREATE([](void*, grpc_error_handle) {}, nullptr,
189 grpc_schedule_on_exec_ctx));
190 }
191
192 // Enable the following test after
193 // https://github.com/grpc/grpc/issues/20064 has been fixed.
TEST_F(TimerTest,DISABLED_CancelRace)194 TEST_F(TimerTest, DISABLED_CancelRace) {
195 MAYBE_SKIP_TEST;
196 grpc_core::ExecCtx exec_ctx;
197 const int kNumTimers = 10;
198 grpc_timer timers[kNumTimers];
199 for (int i = 0; i < kNumTimers; ++i) {
200 grpc_timer* arg = (i != 0) ? &timers[i - 1] : nullptr;
201 grpc_timer_init(
202 &timers[i],
203 grpc_core::Timestamp::Now() + grpc_core::Duration::Milliseconds(100),
204 GRPC_CLOSURE_CREATE(
205 [](void* arg, grpc_error_handle /*error*/) {
206 grpc_timer* timer = static_cast<grpc_timer*>(arg);
207 if (timer) {
208 grpc_timer_cancel(timer);
209 }
210 },
211 arg, grpc_schedule_on_exec_ctx));
212 }
213 gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(100));
214 }
215
216 // Enable the following test after
217 // https://github.com/grpc/grpc/issues/20066 has been fixed.
TEST_F(TimerTest,DISABLED_CancelNextTimer)218 TEST_F(TimerTest, DISABLED_CancelNextTimer) {
219 MAYBE_SKIP_TEST;
220 grpc_core::ExecCtx exec_ctx;
221 const int kNumTimers = 10;
222 grpc_timer timers[kNumTimers];
223
224 for (int i = 0; i < kNumTimers; ++i) {
225 grpc_timer_init_unset(&timers[i]);
226 }
227
228 for (int i = 0; i < kNumTimers; ++i) {
229 grpc_timer* arg = nullptr;
230 if (i < kNumTimers - 1) {
231 arg = &timers[i + 1];
232 }
233 grpc_timer_init(
234 &timers[i],
235 grpc_core::Timestamp::Now() + grpc_core::Duration::Milliseconds(100),
236 GRPC_CLOSURE_CREATE(
237 [](void* arg, grpc_error_handle /*error*/) {
238 grpc_timer* timer = static_cast<grpc_timer*>(arg);
239 if (timer) {
240 grpc_timer_cancel(timer);
241 }
242 },
243 arg, grpc_schedule_on_exec_ctx));
244 }
245 grpc_timer_cancel(&timers[0]);
246 gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(100));
247 }
248
main(int argc,char ** argv)249 int main(int argc, char** argv) {
250 grpc::testing::TestEnvironment env(&argc, argv);
251 ::testing::InitGoogleTest(&argc, argv);
252 return RUN_ALL_TESTS();
253 }
254