1 // Copyright 2022 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/core/lib/promise/sleep.h"
16
17 #include <chrono>
18 #include <cstddef>
19 #include <memory>
20 #include <utility>
21 #include <vector>
22
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25
26 #include <grpc/grpc.h>
27 #include <grpc/support/log.h>
28
29 #include "src/core/lib/event_engine/default_event_engine.h"
30 #include "src/core/lib/gprpp/notification.h"
31 #include "src/core/lib/gprpp/orphanable.h"
32 #include "src/core/lib/iomgr/exec_ctx.h"
33 #include "src/core/lib/promise/exec_ctx_wakeup_scheduler.h"
34 #include "src/core/lib/promise/race.h"
35 #include "test/core/event_engine/mock_event_engine.h"
36 #include "test/core/promise/test_wakeup_schedulers.h"
37
38 using grpc_event_engine::experimental::EventEngine;
39 using grpc_event_engine::experimental::GetDefaultEventEngine;
40 using grpc_event_engine::experimental::MockEventEngine;
41 using testing::_;
42 using testing::DoAll;
43 using testing::Matcher;
44 using testing::Mock;
45 using testing::Return;
46 using testing::SaveArg;
47 using testing::StrictMock;
48
49 namespace grpc_core {
50 namespace {
51
TEST(Sleep,Zzzz)52 TEST(Sleep, Zzzz) {
53 ExecCtx exec_ctx;
54 Notification done;
55 Timestamp done_time = Timestamp::Now() + Duration::Seconds(1);
56 auto engine = GetDefaultEventEngine();
57 // Sleep for one second then set done to true.
58 auto activity = MakeActivity(
59 Sleep(done_time), InlineWakeupScheduler(),
60 [&done](absl::Status r) {
61 EXPECT_EQ(r, absl::OkStatus());
62 done.Notify();
63 },
64 engine.get());
65 done.WaitForNotification();
66 exec_ctx.InvalidateNow();
67 EXPECT_GE(Timestamp::Now(), done_time);
68 }
69
TEST(Sleep,OverlyEagerEventEngine)70 TEST(Sleep, OverlyEagerEventEngine) {
71 StrictMock<MockEventEngine> mock_event_engine;
72
73 ExecCtx exec_ctx;
74 bool done = false;
75 // Schedule a sleep for a very long time.
76 Timestamp done_time = Timestamp::Now() + Duration::Seconds(1e6);
77 EventEngine::Closure* wakeup = nullptr;
78 EXPECT_CALL(mock_event_engine, RunAfter(_, Matcher<EventEngine::Closure*>(_)))
79 .WillOnce(
80 DoAll(SaveArg<1>(&wakeup), Return(EventEngine::TaskHandle{42, 123})));
81 auto activity = MakeActivity(
82 Sleep(done_time), InlineWakeupScheduler(),
83 [&done](absl::Status r) {
84 EXPECT_EQ(r, absl::OkStatus());
85 done = true;
86 },
87 static_cast<EventEngine*>(&mock_event_engine));
88 Mock::VerifyAndClearExpectations(&mock_event_engine);
89 EXPECT_NE(wakeup, nullptr);
90 EXPECT_FALSE(done);
91 // Schedule the wakeup instantaneously - It won't have passed the scheduled
92 // time yet, but sleep should believe the EventEngine.
93 wakeup->Run();
94 EXPECT_TRUE(done);
95 }
96
TEST(Sleep,AlreadyDone)97 TEST(Sleep, AlreadyDone) {
98 ExecCtx exec_ctx;
99 Notification done;
100 Timestamp done_time = Timestamp::Now() - Duration::Seconds(1);
101 auto engine = GetDefaultEventEngine();
102 // Sleep for no time at all then set done to true.
103 auto activity = MakeActivity(
104 Sleep(done_time), InlineWakeupScheduler(),
105 [&done](absl::Status r) {
106 EXPECT_EQ(r, absl::OkStatus());
107 done.Notify();
108 },
109 engine.get());
110 done.WaitForNotification();
111 }
112
TEST(Sleep,Cancel)113 TEST(Sleep, Cancel) {
114 ExecCtx exec_ctx;
115 Notification done;
116 Timestamp done_time = Timestamp::Now() + Duration::Seconds(1);
117 auto engine = GetDefaultEventEngine();
118 // Sleep for one second but race it to complete immediately
119 auto activity = MakeActivity(
120 Race(Sleep(done_time), [] { return absl::CancelledError(); }),
121 InlineWakeupScheduler(),
122 [&done](absl::Status r) {
123 EXPECT_EQ(r, absl::CancelledError());
124 done.Notify();
125 },
126 engine.get());
127 done.WaitForNotification();
128 exec_ctx.InvalidateNow();
129 EXPECT_LT(Timestamp::Now(), done_time);
130 }
131
TEST(Sleep,MoveSemantics)132 TEST(Sleep, MoveSemantics) {
133 // ASAN should help determine if there are any memory leaks here
134 ExecCtx exec_ctx;
135 Notification done;
136 Timestamp done_time = Timestamp::Now() + Duration::Milliseconds(111);
137 Sleep donor(done_time);
138 Sleep sleeper = std::move(donor);
139 auto engine = GetDefaultEventEngine();
140 auto activity = MakeActivity(
141 std::move(sleeper), InlineWakeupScheduler(),
142 [&done](absl::Status r) {
143 EXPECT_EQ(r, absl::OkStatus());
144 done.Notify();
145 },
146 engine.get());
147 done.WaitForNotification();
148 exec_ctx.InvalidateNow();
149 EXPECT_GE(Timestamp::Now(), done_time);
150 }
151
TEST(Sleep,StressTest)152 TEST(Sleep, StressTest) {
153 // Kick off a bunch sleeps for one second.
154 static const int kNumActivities = 100000;
155 ExecCtx exec_ctx;
156 std::vector<std::shared_ptr<Notification>> notifications;
157 std::vector<ActivityPtr> activities;
158 auto engine = GetDefaultEventEngine();
159 gpr_log(GPR_INFO, "Starting %d sleeps for 1sec", kNumActivities);
160 for (int i = 0; i < kNumActivities; i++) {
161 auto notification = std::make_shared<Notification>();
162 auto activity = MakeActivity(
163 Sleep(Timestamp::Now() + Duration::Seconds(1)),
164 ExecCtxWakeupScheduler(),
165 [notification](absl::Status /*r*/) { notification->Notify(); },
166 engine.get());
167 notifications.push_back(std::move(notification));
168 activities.push_back(std::move(activity));
169 }
170 gpr_log(GPR_INFO,
171 "Waiting for the first %d sleeps, whilst cancelling the other half",
172 kNumActivities / 2);
173 for (size_t i = 0; i < kNumActivities / 2; i++) {
174 notifications[i]->WaitForNotification();
175 activities[i].reset();
176 activities[i + kNumActivities / 2].reset();
177 exec_ctx.Flush();
178 }
179 }
180
181 } // namespace
182 } // namespace grpc_core
183
main(int argc,char ** argv)184 int main(int argc, char** argv) {
185 ::testing::InitGoogleTest(&argc, argv);
186 grpc_init();
187 int r = RUN_ALL_TESTS();
188 grpc_shutdown();
189 return r;
190 }
191