1 //
2 //
3 // Copyright 2015 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 <grpc/event_engine/event_engine.h>
20 #include <grpc/support/time.h>
21
22 #include <cstdint>
23 #include <limits>
24 #include <vector>
25
26 #include "absl/types/optional.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 #include "src/core/lib/event_engine/posix_engine/timer.h"
30 #include "src/core/util/time.h"
31
32 using testing::Mock;
33 using testing::Return;
34 using testing::StrictMock;
35
36 namespace grpc_event_engine {
37 namespace experimental {
38
39 namespace {
40 const int64_t kHoursIn25Days = 25 * 24;
41 const grpc_core::Duration k25Days = grpc_core::Duration::Hours(kHoursIn25Days);
42
43 class MockClosure : public experimental::EventEngine::Closure {
44 public:
45 MOCK_METHOD(void, Run, ());
46 };
47
48 class MockHost : public TimerListHost {
49 public:
~MockHost()50 virtual ~MockHost() {}
51 MOCK_METHOD(grpc_core::Timestamp, Now, ());
52 MOCK_METHOD(void, Kick, ());
53 };
54
55 enum class CheckResult { kTimersFired, kCheckedAndEmpty, kNotChecked };
56
FinishCheck(absl::optional<std::vector<experimental::EventEngine::Closure * >> result)57 CheckResult FinishCheck(
58 absl::optional<std::vector<experimental::EventEngine::Closure*>> result) {
59 if (!result.has_value()) return CheckResult::kNotChecked;
60 if (result->empty()) return CheckResult::kCheckedAndEmpty;
61 for (auto closure : *result) {
62 closure->Run();
63 }
64 return CheckResult::kTimersFired;
65 }
66
67 } // namespace
68
TEST(TimerListTest,Add)69 TEST(TimerListTest, Add) {
70 Timer timers[20];
71 StrictMock<MockClosure> closures[20];
72
73 const auto kStart =
74 grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(100);
75
76 StrictMock<MockHost> host;
77 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
78 TimerList timer_list(&host);
79
80 // 10 ms timers. will expire in the current epoch
81 for (int i = 0; i < 10; i++) {
82 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
83 timer_list.TimerInit(&timers[i],
84 kStart + grpc_core::Duration::Milliseconds(10),
85 &closures[i]);
86 }
87
88 // 1010 ms timers. will expire in the next epoch
89 for (int i = 10; i < 20; i++) {
90 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
91 timer_list.TimerInit(&timers[i],
92 kStart + grpc_core::Duration::Milliseconds(1010),
93 &closures[i]);
94 }
95
96 // collect timers. Only the first batch should be ready.
97 EXPECT_CALL(host, Now())
98 .WillOnce(Return(kStart + grpc_core::Duration::Milliseconds(500)));
99 for (int i = 0; i < 10; i++) {
100 EXPECT_CALL(closures[i], Run());
101 }
102 EXPECT_EQ(FinishCheck(timer_list.TimerCheck(nullptr)),
103 CheckResult::kTimersFired);
104 for (int i = 0; i < 10; i++) {
105 Mock::VerifyAndClearExpectations(&closures[i]);
106 }
107
108 EXPECT_CALL(host, Now())
109 .WillOnce(Return(kStart + grpc_core::Duration::Milliseconds(600)));
110 EXPECT_EQ(FinishCheck(timer_list.TimerCheck(nullptr)),
111 CheckResult::kCheckedAndEmpty);
112
113 // collect the rest of the timers
114 EXPECT_CALL(host, Now())
115 .WillOnce(Return(kStart + grpc_core::Duration::Milliseconds(1500)));
116 for (int i = 10; i < 20; i++) {
117 EXPECT_CALL(closures[i], Run());
118 }
119 EXPECT_EQ(FinishCheck(timer_list.TimerCheck(nullptr)),
120 CheckResult::kTimersFired);
121 for (int i = 10; i < 20; i++) {
122 Mock::VerifyAndClearExpectations(&closures[i]);
123 }
124
125 EXPECT_CALL(host, Now())
126 .WillOnce(Return(kStart + grpc_core::Duration::Milliseconds(1600)));
127 EXPECT_EQ(FinishCheck(timer_list.TimerCheck(nullptr)),
128 CheckResult::kCheckedAndEmpty);
129 }
130
131 // Cleaning up a list with pending timers.
TEST(TimerListTest,Destruction)132 TEST(TimerListTest, Destruction) {
133 Timer timers[5];
134 StrictMock<MockClosure> closures[5];
135
136 StrictMock<MockHost> host;
137 EXPECT_CALL(host, Now())
138 .WillOnce(
139 Return(grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(0)));
140 TimerList timer_list(&host);
141
142 EXPECT_CALL(host, Now())
143 .WillOnce(
144 Return(grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(0)));
145 timer_list.TimerInit(
146 &timers[0], grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(100),
147 &closures[0]);
148 EXPECT_CALL(host, Now())
149 .WillOnce(
150 Return(grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(0)));
151 timer_list.TimerInit(
152 &timers[1], grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(3),
153 &closures[1]);
154 EXPECT_CALL(host, Now())
155 .WillOnce(
156 Return(grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(0)));
157 timer_list.TimerInit(
158 &timers[2], grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(100),
159 &closures[2]);
160 EXPECT_CALL(host, Now())
161 .WillOnce(
162 Return(grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(0)));
163 timer_list.TimerInit(
164 &timers[3], grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(3),
165 &closures[3]);
166 EXPECT_CALL(host, Now())
167 .WillOnce(
168 Return(grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(0)));
169 timer_list.TimerInit(
170 &timers[4], grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(1),
171 &closures[4]);
172 EXPECT_CALL(host, Now())
173 .WillOnce(
174 Return(grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(2)));
175 EXPECT_CALL(closures[4], Run());
176 EXPECT_EQ(FinishCheck(timer_list.TimerCheck(nullptr)),
177 CheckResult::kTimersFired);
178 Mock::VerifyAndClearExpectations(&closures[4]);
179 EXPECT_FALSE(timer_list.TimerCancel(&timers[4]));
180 EXPECT_TRUE(timer_list.TimerCancel(&timers[0]));
181 EXPECT_TRUE(timer_list.TimerCancel(&timers[3]));
182 EXPECT_TRUE(timer_list.TimerCancel(&timers[1]));
183 EXPECT_TRUE(timer_list.TimerCancel(&timers[2]));
184 }
185
186 // Cleans up a list with pending timers that simulate long-running-services.
187 // This test does the following:
188 // 1) Simulates grpc server start time to 25 days in the past (completed in
189 // `main` using TestOnlyGlobalInit())
190 // 2) Creates 4 timers - one with a deadline 25 days in the future, one just
191 // 3 milliseconds in future, one way out in the future, and one using the
192 // Timestamp::FromTimespecRoundUp function to compute a deadline of 25
193 // days in the future
194 // 3) Simulates 4 milliseconds of elapsed time by changing `now` (cached at
195 // step 1) to `now+4`
196 // 4) Shuts down the timer list
197 // https://github.com/grpc/grpc/issues/15904
TEST(TimerListTest,LongRunningServiceCleanup)198 TEST(TimerListTest, LongRunningServiceCleanup) {
199 Timer timers[4];
200 StrictMock<MockClosure> closures[4];
201
202 const auto kStart =
203 grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(k25Days.millis());
204
205 StrictMock<MockHost> host;
206 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
207 TimerList timer_list(&host);
208
209 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
210 timer_list.TimerInit(&timers[0], kStart + k25Days, &closures[0]);
211 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
212 timer_list.TimerInit(
213 &timers[1], kStart + grpc_core::Duration::Milliseconds(3), &closures[1]);
214 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
215 timer_list.TimerInit(&timers[2],
216 grpc_core::Timestamp::FromMillisecondsAfterProcessEpoch(
217 std::numeric_limits<int64_t>::max() - 1),
218 &closures[2]);
219
220 gpr_timespec deadline_spec =
221 (kStart + k25Days).as_timespec(gpr_clock_type::GPR_CLOCK_MONOTONIC);
222
223 // Timestamp::FromTimespecRoundUp is how users usually compute a millisecond
224 // input value into grpc_timer_init, so we mimic that behavior here
225 EXPECT_CALL(host, Now()).WillOnce(Return(kStart));
226 timer_list.TimerInit(&timers[3],
227 grpc_core::Timestamp::FromTimespecRoundUp(deadline_spec),
228 &closures[3]);
229
230 EXPECT_CALL(host, Now())
231 .WillOnce(Return(kStart + grpc_core::Duration::Milliseconds(4)));
232 EXPECT_CALL(closures[1], Run());
233 EXPECT_EQ(FinishCheck(timer_list.TimerCheck(nullptr)),
234 CheckResult::kTimersFired);
235 EXPECT_TRUE(timer_list.TimerCancel(&timers[0]));
236 EXPECT_FALSE(timer_list.TimerCancel(&timers[1]));
237 EXPECT_TRUE(timer_list.TimerCancel(&timers[2]));
238 EXPECT_TRUE(timer_list.TimerCancel(&timers[3]));
239 }
240
241 } // namespace experimental
242 } // namespace grpc_event_engine
243
main(int argc,char ** argv)244 int main(int argc, char** argv) {
245 ::testing::InitGoogleTest(&argc, argv);
246 return RUN_ALL_TESTS();
247 }
248