• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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