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/event_engine/forkable.h"
16
17 #include <grpc/support/port_platform.h>
18
19 #ifdef GPR_POSIX_SUBPROCESS
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <sys/wait.h>
23 #include <unistd.h>
24 #endif // GPR_POSIX_SUBPROCESS
25
26 #include <memory>
27
28 #include "absl/types/optional.h"
29 #include "gtest/gtest.h"
30
31 #include <grpc/support/log.h>
32
33 #include "src/core/lib/config/config_vars.h"
34 #include "src/core/lib/gprpp/no_destruct.h"
35
36 namespace {
37 using ::grpc_event_engine::experimental::Forkable;
38 using ::grpc_event_engine::experimental::ObjectGroupForkHandler;
39
40 grpc_core::NoDestruct<ObjectGroupForkHandler> g_forkable_manager;
41
42 class ForkCallbackMethods {
43 public:
Prefork()44 static void Prefork() { g_forkable_manager->Prefork(); }
PostforkParent()45 static void PostforkParent() { g_forkable_manager->PostforkParent(); }
PostforkChild()46 static void PostforkChild() { g_forkable_manager->PostforkChild(); }
47 };
48 } // namespace
49
50 class ForkableTest : public testing::Test {};
51
52 #ifdef GPR_POSIX_SUBPROCESS
TEST_F(ForkableTest,BasicPthreadAtForkOperations)53 TEST_F(ForkableTest, BasicPthreadAtForkOperations) {
54 class SomeForkable : public Forkable {
55 public:
56 void PrepareFork() override { prepare_called_ = true; }
57 void PostforkParent() override { parent_called_ = true; }
58 void PostforkChild() override { child_called_ = true; }
59
60 void CheckParent() {
61 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
62 EXPECT_TRUE(prepare_called_);
63 EXPECT_TRUE(parent_called_);
64 EXPECT_FALSE(child_called_);
65 #else
66 EXPECT_FALSE(prepare_called_);
67 EXPECT_FALSE(parent_called_);
68 EXPECT_FALSE(child_called_);
69 #endif
70 }
71
72 void CheckChild() {
73 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
74 EXPECT_TRUE(prepare_called_);
75 EXPECT_FALSE(parent_called_);
76 EXPECT_TRUE(child_called_);
77 #else
78 EXPECT_FALSE(prepare_called_);
79 EXPECT_FALSE(parent_called_);
80 EXPECT_FALSE(child_called_);
81 #endif
82 }
83
84 private:
85 bool prepare_called_ = false;
86 bool parent_called_ = false;
87 bool child_called_ = false;
88 };
89
90 auto forkable = std::make_shared<SomeForkable>();
91 g_forkable_manager->RegisterForkable(forkable, ForkCallbackMethods::Prefork,
92 ForkCallbackMethods::PostforkParent,
93 ForkCallbackMethods::PostforkChild);
94 int child_pid = fork();
95 ASSERT_NE(child_pid, -1);
96 if (child_pid == 0) {
97 gpr_log(GPR_DEBUG, "I am child pid: %d", getpid());
98 forkable->CheckChild();
99 exit(testing::Test::HasFailure());
100 } else {
101 gpr_log(GPR_DEBUG, "I am parent pid: %d", getpid());
102 forkable->CheckParent();
103 int status;
104 gpr_log(GPR_DEBUG, "Waiting for child pid: %d", child_pid);
105 do {
106 // retry on EINTR, and fail otherwise
107 if (waitpid(child_pid, &status, 0) != -1) break;
108 ASSERT_EQ(errno, EINTR);
109 } while (true);
110 if (WIFEXITED(status)) {
111 ASSERT_EQ(WEXITSTATUS(status), 0);
112 } else {
113 // exited abnormally, fail and print the exit status
114 ASSERT_EQ(-99, status);
115 }
116 }
117 }
118 #endif // GPR_POSIX_SUBPROCESS
119
TEST_F(ForkableTest,NonPthreadManualForkOperations)120 TEST_F(ForkableTest, NonPthreadManualForkOperations) {
121 // Manually simulates a fork event for non-pthread-enabled environments
122 #ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK
123 // This platform does not need to exercise fork support manually.
124 GTEST_SKIP() << "Unnecessary test, this platform supports pthreads.";
125 #endif
126
127 class SomeForkable : public Forkable {
128 public:
129 void PrepareFork() override { prepare_called_ = true; }
130 void PostforkParent() override { parent_called_ = true; }
131 void PostforkChild() override { child_called_ = true; }
132
133 void AssertStates(bool prepare, bool parent, bool child) {
134 EXPECT_EQ(prepare_called_, prepare);
135 EXPECT_EQ(parent_called_, parent);
136 EXPECT_EQ(child_called_, child);
137 }
138
139 private:
140 bool prepare_called_ = false;
141 bool parent_called_ = false;
142 bool child_called_ = false;
143 };
144
145 ObjectGroupForkHandler forkable_manager;
146 class NoopForkCallbackMethods {
147 public:
148 static void Prefork() {}
149 static void PostforkParent() {}
150 static void PostforkChild() {}
151 };
152 auto forkable = std::make_shared<SomeForkable>();
153 forkable_manager.RegisterForkable(forkable, NoopForkCallbackMethods::Prefork,
154 NoopForkCallbackMethods::PostforkParent,
155 NoopForkCallbackMethods::PostforkChild);
156 forkable->AssertStates(/*prepare=*/false, /*parent=*/false, /*child=*/false);
157 forkable_manager.Prefork();
158 forkable->AssertStates(/*prepare=*/true, /*parent=*/false, /*child=*/false);
159 forkable_manager.PostforkParent();
160 forkable->AssertStates(/*prepare=*/true, /*parent=*/true, /*child=*/false);
161 forkable_manager.Prefork();
162 forkable_manager.PostforkChild();
163 forkable->AssertStates(/*prepare=*/true, /*parent=*/true, /*child=*/true);
164 }
165
main(int argc,char ** argv)166 int main(int argc, char** argv) {
167 testing::InitGoogleTest(&argc, argv);
168 // Force enable fork support to allow testing the fork handler registry.
169 grpc_core::ConfigVars::Overrides config_overrides;
170 config_overrides.enable_fork_support = true;
171 grpc_core::ConfigVars::SetOverrides(config_overrides);
172 auto result = RUN_ALL_TESTS();
173 return result;
174 }
175