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