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 {
struct AtForkTask {
void* object;
folly::Function<void()> prepare;
folly::Function<bool()> prepare;
folly::Function<void()> parent;
folly::Function<void()> child;
};
......@@ -42,9 +42,20 @@ class AtForkList {
static void prepare() noexcept {
instance().tasksLock.lock();
auto& tasks = instance().tasks;
for (auto task = tasks.rbegin(); task != tasks.rend(); ++task) {
task->prepare();
while (true) {
auto& tasks = instance().tasks;
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() {
void AtFork::registerHandler(
void* object,
folly::Function<void()> prepare,
folly::Function<bool()> prepare,
folly::Function<void()> parent,
folly::Function<void()> child) {
std::lock_guard<std::mutex> lg(AtForkList::instance().tasksLock);
......
......@@ -25,7 +25,7 @@ struct AtFork {
static void init();
static void registerHandler(
void* object,
folly::Function<void()> prepare,
folly::Function<bool()> prepare,
folly::Function<void()> parent,
folly::Function<void()> child);
static void unregisterHandler(void* object);
......
......@@ -409,8 +409,8 @@ struct StaticMeta : StaticMetaBase {
return threadEntry;
}
static void preFork() {
instance().lock_.lock(); // Make sure it's created
static bool preFork() {
return instance().lock_.try_lock(); // Make sure it's created
}
static void onForkParent() {
......
......@@ -36,7 +36,7 @@ rcu_domain<Tag>::rcu_domain(Executor* executor) noexcept
// that use read locks *is* supported.
detail::AtFork::registerHandler(
this,
[this]() { syncMutex_.lock(); },
[this]() { return syncMutex_.try_lock(); },
[this]() { syncMutex_.unlock(); },
[this]() {
counters_.resetAfterFork();
......
......@@ -18,11 +18,21 @@
#include <folly/portability/GTest.h>
#include <glog/logging.h>
#include <atomic>
#include <mutex>
#include <thread>
TEST(ThreadLocal, AtFork) {
int foo;
bool forked = false;
folly::detail::AtFork::registerHandler(
&foo, [&] { forked = true; }, [] {}, [] {});
&foo,
[&] {
forked = true;
return true;
},
[] {},
[] {});
auto pid = fork();
if (pid) {
int status;
......@@ -46,3 +56,42 @@ TEST(ThreadLocal, AtFork) {
}
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