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