Skip to content

Commit e6d6989

Browse files
Byte-Labfacebook-github-bot
authored andcommitted
Remove AtFork handling logic from RCU
Summary: folly RCU currently has a somewhat awkward restriction that there may be no RCU readers across a fork() call, and that after fork() has been invoked, no AtFork handlers may enter a read region *unless* they spawn a separate thread in the AtFork handler. In the child's fork() handler, we then reset the forked child thread's TLS read counters, and even have a ForkTest that validates that if we were in a read region while calling fork(), that the child's state is wiped out. The fact that we zero out the forked child thread's readers rather than explicitly just panicking is very confusing, and does not match the advertised behavior of the RCU subsystem. The only thing that's really valid to do in if fork() is involved is: 1. Have a single threaded program that is not in a read region. 2. Have a single threaded program that is in a read region, and immediately invokes exec(). These restrictions are common to most librcu implementations. For folly RCU, we can't do anything to verify that the program was single threaded when we get to an AtFork handler, and we also can't check that if the caller is in a read region that they will soon exec(). What we definitely should *not* do is instill some artificial state of clearing out the read region of the forking thread on a fork() invocation. This change therefore just removes the folly AtFork handling logic. Reviewed By: paulmckrcu Differential Revision: D35557080 fbshipit-source-id: 77e3bdaaea078c60e7f7c562228de08635f164e6
1 parent 10ad62a commit e6d6989

File tree

4 files changed

+20
-39
lines changed

4 files changed

+20
-39
lines changed

folly/synchronization/Rcu-inl.h

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
#include <folly/Function.h>
1818
#include <folly/detail/TurnSequencer.h>
19-
#include <folly/system/AtFork.h>
2019

2120
namespace folly {
2221

@@ -29,24 +28,6 @@ rcu_domain<Tag>::rcu_domain(Executor* executor) noexcept
2928
// Please use a unique tag for each domain.
3029
CHECK(!singleton_);
3130
singleton_ = true;
32-
33-
// Register fork handlers. Holding read locks across fork is not
34-
// supported. Using read locks in other atfork handlers is not
35-
// supported. Other atfork handlers launching new child threads
36-
// that use read locks *is* supported.
37-
AtFork::registerHandler(
38-
this,
39-
[this]() { return syncMutex_.try_lock(); },
40-
[this]() { syncMutex_.unlock(); },
41-
[this]() {
42-
counters_.resetAfterFork();
43-
syncMutex_.unlock();
44-
});
45-
}
46-
47-
template <typename Tag>
48-
rcu_domain<Tag>::~rcu_domain() {
49-
AtFork::unregisterHandler(this);
5031
}
5132

5233
template <typename Tag>

folly/synchronization/Rcu.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,15 @@
188188
// the default executor type.
189189

190190
// API correctness limitations:
191+
//
192+
// - fork():
193+
// Invoking fork() in a multithreaded program with any thread other than the
194+
// forking thread being present in a read region will result in undefined
195+
// behavior. Similarly, a forking thread must immediately invoke exec if
196+
// fork() is invoked while in a read region. Invoking fork() inside of a read
197+
// region, and then exiting before invoking exec(), will similarly result in
198+
// undefined behavior.
199+
//
191200
// - Exceptions:
192201
// In short, nothing about this is exception safe. retire functions should
193202
// not throw exceptions in their destructors, move constructors or call
@@ -328,7 +337,6 @@ class rcu_domain {
328337
rcu_domain(rcu_domain&&) = delete;
329338
rcu_domain& operator=(const rcu_domain&) = delete;
330339
rcu_domain& operator=(rcu_domain&&) = delete;
331-
~rcu_domain();
332340

333341
// Reader locks: Prevent any calls from occuring, retired memory
334342
// from being freed, and synchronize() calls from completing until

folly/synchronization/detail/ThreadCachedReaders.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,6 @@ class ThreadCachedReaders {
186186
}
187187
waiting_.store(0, std::memory_order_relaxed);
188188
}
189-
190-
// We are guaranteed to be called while StaticMeta lock is still
191-
// held because of ordering in AtForkList. We can therefore safely
192-
// touch orphan_ and clear out all counts.
193-
void resetAfterFork() {
194-
if (tls_cache_) {
195-
tls_cache_->epoch_readers_ = 0;
196-
}
197-
orphan_epoch_readers_ = 0;
198-
folly::asymmetricLightBarrier();
199-
}
200189
};
201190

202191
template <typename Tag>

folly/synchronization/test/RcuTest.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,21 +197,24 @@ TEST(RcuTest, SynchronizeInCall) {
197197
rcu_synchronize();
198198
}
199199

200-
TEST(RcuTest, ForkTest) {
200+
TEST(RcuTest, SafeForkTest) {
201201
rcu_default_domain().lock();
202+
rcu_default_domain().unlock();
202203
auto pid = fork();
203-
if (pid) {
204-
// parent
205-
rcu_default_domain().unlock();
204+
if (pid > 0) {
205+
// Parent branch -- wait for child to exit.
206206
rcu_synchronize();
207207
int status = -1;
208208
auto pid2 = waitpid(pid, &status, 0);
209-
EXPECT_EQ(status, 0);
210209
EXPECT_EQ(pid, pid2);
211-
} else {
212-
// child
210+
EXPECT_EQ(status, 0);
211+
} else if (pid == 0) {
213212
rcu_synchronize();
214-
exit(0); // Do not print gtest results
213+
// Exit quickly to avoid spamming gtest output to console.
214+
exit(0);
215+
} else {
216+
// Skip the test if fork() fails.
217+
GTEST_SKIP();
215218
}
216219
}
217220

0 commit comments

Comments
 (0)