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