Commit 30aacd4b authored by Dave Watson's avatar Dave Watson Committed by Facebook Github Bot

Iterative prepare() locking

Summary:
prepare() blocks mostly just lock locks, but we don't know what order to lock - so similar
to std::lock, just iterate using try_lock until success.

Reviewed By: ot

Differential Revision: D7676738

fbshipit-source-id: 2e0d28e4829a3a21d361a5801383c1aad0d66757
parent 6b3aaa2b
...@@ -28,7 +28,7 @@ namespace { ...@@ -28,7 +28,7 @@ namespace {
struct AtForkTask { struct AtForkTask {
void* object; void* object;
folly::Function<void()> prepare; folly::Function<bool()> prepare;
folly::Function<void()> parent; folly::Function<void()> parent;
folly::Function<void()> child; folly::Function<void()> child;
}; };
...@@ -42,9 +42,20 @@ class AtForkList { ...@@ -42,9 +42,20 @@ class AtForkList {
static void prepare() noexcept { static void prepare() noexcept {
instance().tasksLock.lock(); instance().tasksLock.lock();
auto& tasks = instance().tasks; while (true) {
for (auto task = tasks.rbegin(); task != tasks.rend(); ++task) { auto& tasks = instance().tasks;
task->prepare(); auto task = tasks.rbegin();
for (; task != tasks.rend(); ++task) {
if (!task->prepare()) {
break;
}
}
if (task == tasks.rend()) {
return;
}
for (auto untask = tasks.rbegin(); untask != task; ++untask) {
untask->parent();
}
} }
} }
...@@ -91,7 +102,7 @@ void AtFork::init() { ...@@ -91,7 +102,7 @@ void AtFork::init() {
void AtFork::registerHandler( void AtFork::registerHandler(
void* object, void* object,
folly::Function<void()> prepare, folly::Function<bool()> prepare,
folly::Function<void()> parent, folly::Function<void()> parent,
folly::Function<void()> child) { folly::Function<void()> child) {
std::lock_guard<std::mutex> lg(AtForkList::instance().tasksLock); std::lock_guard<std::mutex> lg(AtForkList::instance().tasksLock);
......
...@@ -25,7 +25,7 @@ struct AtFork { ...@@ -25,7 +25,7 @@ struct AtFork {
static void init(); static void init();
static void registerHandler( static void registerHandler(
void* object, void* object,
folly::Function<void()> prepare, folly::Function<bool()> prepare,
folly::Function<void()> parent, folly::Function<void()> parent,
folly::Function<void()> child); folly::Function<void()> child);
static void unregisterHandler(void* object); static void unregisterHandler(void* object);
......
...@@ -409,8 +409,8 @@ struct StaticMeta : StaticMetaBase { ...@@ -409,8 +409,8 @@ struct StaticMeta : StaticMetaBase {
return threadEntry; return threadEntry;
} }
static void preFork() { static bool preFork() {
instance().lock_.lock(); // Make sure it's created return instance().lock_.try_lock(); // Make sure it's created
} }
static void onForkParent() { static void onForkParent() {
......
...@@ -36,7 +36,7 @@ rcu_domain<Tag>::rcu_domain(Executor* executor) noexcept ...@@ -36,7 +36,7 @@ rcu_domain<Tag>::rcu_domain(Executor* executor) noexcept
// that use read locks *is* supported. // that use read locks *is* supported.
detail::AtFork::registerHandler( detail::AtFork::registerHandler(
this, this,
[this]() { syncMutex_.lock(); }, [this]() { return syncMutex_.try_lock(); },
[this]() { syncMutex_.unlock(); }, [this]() { syncMutex_.unlock(); },
[this]() { [this]() {
counters_.resetAfterFork(); counters_.resetAfterFork();
......
...@@ -18,11 +18,21 @@ ...@@ -18,11 +18,21 @@
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
#include <glog/logging.h> #include <glog/logging.h>
#include <atomic>
#include <mutex>
#include <thread>
TEST(ThreadLocal, AtFork) { TEST(ThreadLocal, AtFork) {
int foo; int foo;
bool forked = false; bool forked = false;
folly::detail::AtFork::registerHandler( folly::detail::AtFork::registerHandler(
&foo, [&] { forked = true; }, [] {}, [] {}); &foo,
[&] {
forked = true;
return true;
},
[] {},
[] {});
auto pid = fork(); auto pid = fork();
if (pid) { if (pid) {
int status; int status;
...@@ -46,3 +56,42 @@ TEST(ThreadLocal, AtFork) { ...@@ -46,3 +56,42 @@ TEST(ThreadLocal, AtFork) {
} }
EXPECT_FALSE(forked); EXPECT_FALSE(forked);
} }
TEST(ThreadLocal, AtForkOrdering) {
std::atomic<bool> done{false};
std::atomic<bool> started{false};
std::mutex a;
std::mutex b;
int foo;
int foo2;
folly::detail::AtFork::registerHandler(
&foo,
[&] { return a.try_lock(); },
[&] { a.unlock(); },
[&] { a.unlock(); });
folly::detail::AtFork::registerHandler(
&foo2,
[&] { return b.try_lock(); },
[&] { b.unlock(); },
[&] { b.unlock(); });
auto thr = std::thread([&]() {
std::lock_guard<std::mutex> g(a);
started = true;
usleep(100);
std::lock_guard<std::mutex> g2(b);
});
while (!started) {
}
auto pid = fork();
if (pid) {
int status;
auto pid2 = wait(&status);
EXPECT_EQ(status, 0);
EXPECT_EQ(pid, pid2);
} else {
exit(0);
}
done = true;
thr.join();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment