Commit f8dfa34d authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

Tweak BlockingContext

Summary: [Folly] Tweak `BlockingContext` in various ways.

Differential Revision: D21657789

fbshipit-source-id: 4b03b3b43dddc0e9011a7594001994f2594a1a5b
parent 2fc0cee3
......@@ -15,12 +15,14 @@
*/
#include <folly/Executor.h>
#include <folly/SingletonThreadLocal.h>
#include <stdexcept>
#include <glog/logging.h>
#include <folly/Portability.h>
#include <folly/lang/Exception.h>
namespace folly {
void Executor::addWithPriority(Func, int8_t /* priority */) {
......@@ -37,35 +39,47 @@ void Executor::keepAliveRelease() {
<< "which do not override keepAliveAcquire()";
}
namespace {
using BlockingContextSingletonT =
SingletonThreadLocal<folly::Optional<BlockingContext>>;
} // namespace
#if defined(FOLLY_TLS)
folly::Optional<BlockingContext> getBlockingContext() {
return BlockingContextSingletonT::get();
}
extern constexpr bool const executor_blocking_list_enabled = true;
thread_local ExecutorBlockingList* executor_blocking_list = nullptr;
BlockingGuard::BlockingGuard(folly::StringPiece executorName)
: previousContext_{BlockingContextSingletonT::get()} {
BlockingContextSingletonT::get() = BlockingContext{executorName};
}
#else
extern constexpr bool const executor_blocking_list_enabled = false;
ExecutorBlockingList* executor_blocking_list = nullptr;
#endif
BlockingGuard::BlockingGuard()
: previousContext_{BlockingContextSingletonT::get()} {
BlockingContextSingletonT::get() = folly::none;
Optional<ExecutorBlockingContext> getExecutorBlockingContext() noexcept {
return //
!executor_blocking_list || !executor_blocking_list->forbid ? none : //
make_optional(executor_blocking_list->curr);
}
BlockingGuard::~BlockingGuard() {
BlockingContextSingletonT::get() = std::move(previousContext_);
ExecutorBlockingGuard::ExecutorBlockingGuard(PermitTag) noexcept
: list_{false, executor_blocking_list, {}} {
if (executor_blocking_list_enabled) {
executor_blocking_list = &list_;
}
}
BlockingGuard makeBlockingDisallowedGuard(folly::StringPiece executorName) {
return BlockingGuard{executorName};
ExecutorBlockingGuard::ExecutorBlockingGuard(
ForbidTag,
StringPiece name) noexcept
: list_{true, executor_blocking_list, {name}} {
if (executor_blocking_list_enabled) {
executor_blocking_list = &list_;
}
}
BlockingGuard makeBlockingAllowedGuard() {
return BlockingGuard{};
ExecutorBlockingGuard::~ExecutorBlockingGuard() {
if (executor_blocking_list_enabled) {
if (executor_blocking_list != &list_) {
terminate_with<std::logic_error>("dtor mismatch");
}
executor_blocking_list = list_.prev;
}
}
} // namespace folly
......@@ -299,32 +299,43 @@ Executor::KeepAlive<ExecutorT> getKeepAliveToken(
return ka.copy();
}
struct BlockingContext {
folly::StringPiece executorName;
struct ExecutorBlockingContext {
StringPiece name;
};
static_assert(
std::is_standard_layout<ExecutorBlockingContext>::value,
"non-standard layout");
struct ExecutorBlockingList {
bool forbid;
ExecutorBlockingList* prev;
ExecutorBlockingContext curr;
};
static_assert(
std::is_standard_layout<ExecutorBlockingList>::value,
"non-standard layout");
class BlockingGuard;
class ExecutorBlockingGuard {
public:
struct PermitTag {};
struct ForbidTag {};
BlockingGuard makeBlockingDisallowedGuard(folly::StringPiece executorName);
BlockingGuard makeBlockingAllowedGuard();
~ExecutorBlockingGuard();
ExecutorBlockingGuard() = delete;
class FOLLY_NODISCARD BlockingGuard {
public:
~BlockingGuard();
explicit ExecutorBlockingGuard(PermitTag) noexcept;
explicit ExecutorBlockingGuard(ForbidTag, StringPiece name) noexcept;
private:
// Disallow blocking
BlockingGuard(folly::StringPiece executorName);
// Empty guard treated as temporarily allowing blocking
BlockingGuard();
ExecutorBlockingGuard(ExecutorBlockingGuard&&) = delete;
ExecutorBlockingGuard(ExecutorBlockingGuard const&) = delete;
friend BlockingGuard makeBlockingDisallowedGuard(
folly::StringPiece executorName);
friend BlockingGuard makeBlockingAllowedGuard();
ExecutorBlockingGuard& operator=(ExecutorBlockingGuard const&) = delete;
ExecutorBlockingGuard& operator=(ExecutorBlockingGuard&&) = delete;
folly::Optional<BlockingContext> previousContext_;
private:
ExecutorBlockingList list_;
};
folly::Optional<BlockingContext> getBlockingContext();
Optional<ExecutorBlockingContext> getExecutorBlockingContext() noexcept;
} // namespace folly
......@@ -196,7 +196,7 @@ bool CPUThreadPoolExecutor::taskShouldStop(folly::Optional<CPUTask>& task) {
void CPUThreadPoolExecutor::threadRun(ThreadPtr thread) {
this->threadPoolHook_.registerThread();
auto guard = folly::makeBlockingDisallowedGuard(executorName);
ExecutorBlockingGuard guard{ExecutorBlockingGuard::ForbidTag{}, executorName};
thread->startupBaton.post();
while (true) {
......
......@@ -342,7 +342,7 @@ folly::Executor::KeepAlive<> EDFThreadPoolExecutor::deadlineExecutor(
void EDFThreadPoolExecutor::threadRun(ThreadPtr thread) {
this->threadPoolHook_.registerThread();
auto guard = folly::makeBlockingDisallowedGuard(executorName);
ExecutorBlockingGuard guard{ExecutorBlockingGuard::ForbidTag{}, executorName};
thread->startupBaton.post();
for (;;) {
......
......@@ -1012,30 +1012,30 @@ TEST(ThreadPoolExecutorTest, VirtualExecutorTestEDF) {
// Test use of guard inside executors
template <class TPE>
static void currentThreadTest(folly::StringPiece executorName) {
folly::Optional<BlockingContext> blockingContext{};
folly::Optional<ExecutorBlockingContext> ctx{};
TPE tpe(1);
tpe.add([&blockingContext]() { blockingContext = getBlockingContext(); });
tpe.add([&ctx]() { ctx = getExecutorBlockingContext(); });
tpe.join();
EXPECT_EQ(blockingContext->executorName, executorName);
EXPECT_EQ(ctx->name, executorName);
}
// Test the nesting of the allowing guard
// Test the nesting of the permit guard
template <class TPE>
static void currentThreadTestDisabled(folly::StringPiece executorName) {
folly::Optional<BlockingContext> blockingContextAllowed{};
folly::Optional<BlockingContext> blockingContextDisallowed{};
folly::Optional<ExecutorBlockingContext> ctxPermit{};
folly::Optional<ExecutorBlockingContext> ctxForbid{};
TPE tpe(1);
tpe.add([&]() {
{
// Nest the guard that reallows blocking
auto guard = folly::makeBlockingAllowedGuard();
blockingContextAllowed = getBlockingContext();
// Nest the guard that permits blocking
ExecutorBlockingGuard guard{ExecutorBlockingGuard::PermitTag{}};
ctxPermit = getExecutorBlockingContext();
}
blockingContextDisallowed = getBlockingContext();
ctxForbid = getExecutorBlockingContext();
});
tpe.join();
EXPECT_TRUE(!blockingContextAllowed.has_value());
EXPECT_EQ(blockingContextDisallowed->executorName, executorName);
EXPECT_TRUE(!ctxPermit.has_value());
EXPECT_EQ(ctxForbid->name, executorName);
}
TEST(ThreadPoolExecutorTest, CPUCurrentThreadExecutor) {
......
......@@ -311,7 +311,7 @@ void EventBase::waitUntilRunning() {
// enters the event_base loop -- will only exit when forced to
bool EventBase::loop() {
auto guard = folly::makeBlockingDisallowedGuard(executorName);
ExecutorBlockingGuard guard{ExecutorBlockingGuard::ForbidTag{}, executorName};
return loopBody();
}
......
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