Commit cfc602df authored by Giuseppe Ottaviano's avatar Giuseppe Ottaviano Committed by Facebook Github Bot

Make global executors shutdown-safe

Summary:
The `get*Executor()` APIs don't check whether the singletons
haven't been destroyed already. Add a check and allow to return
`nullptr` during shutdown.

Also do a general clean up of the code, there was no reason to use
three independent singletons (non-atomically destroyed) for each
executor.

Reviewed By: philippv, luciang

Differential Revision: D6589486

fbshipit-source-id: 20fb835db7e446bd811bbd6d5ddbc41db9e98b54
parent 3975849d
...@@ -14,6 +14,11 @@ ...@@ -14,6 +14,11 @@
* limitations under the License. * limitations under the License.
*/ */
#include <memory>
#include <thread>
#include <folly/Function.h>
#include <folly/SharedMutex.h>
#include <folly/Singleton.h> #include <folly/Singleton.h>
#include <folly/executors/IOExecutor.h> #include <folly/executors/IOExecutor.h>
#include <folly/executors/IOThreadPoolExecutor.h> #include <folly/executors/IOThreadPoolExecutor.h>
...@@ -23,90 +28,93 @@ using namespace folly; ...@@ -23,90 +28,93 @@ using namespace folly;
namespace { namespace {
// lock protecting global CPU executor template <class ExecutorBase>
struct CPUExecutorLock {}; class GlobalExecutor {
Singleton<RWSpinLock, CPUExecutorLock> globalCPUExecutorLock; public:
// global CPU executor explicit GlobalExecutor(
Singleton<std::weak_ptr<Executor>> globalCPUExecutor; Function<std::unique_ptr<ExecutorBase>()> constructDefault)
// default global CPU executor is an InlineExecutor : constructDefault_(std::move(constructDefault)) {}
Singleton<std::shared_ptr<InlineExecutor>> globalInlineExecutor([] {
return new std::shared_ptr<InlineExecutor>(
std::make_shared<InlineExecutor>());
});
// lock protecting global IO executor
struct IOExecutorLock {};
Singleton<RWSpinLock, IOExecutorLock> globalIOExecutorLock;
// global IO executor
Singleton<std::weak_ptr<IOExecutor>> globalIOExecutor;
// default global IO executor is an IOThreadPoolExecutor
Singleton<std::shared_ptr<IOThreadPoolExecutor>> globalIOThreadPool([] {
return new std::shared_ptr<IOThreadPoolExecutor>(
std::make_shared<IOThreadPoolExecutor>(
sysconf(_SC_NPROCESSORS_ONLN),
std::make_shared<NamedThreadFactory>("GlobalIOThreadPool")));
});
} // namespace
namespace folly {
template <class Exe, class DefaultExe, class LockTag>
std::shared_ptr<Exe> getExecutor(
Singleton<std::weak_ptr<Exe>>& sExecutor,
Singleton<std::shared_ptr<DefaultExe>>& sDefaultExecutor,
Singleton<RWSpinLock, LockTag>& sExecutorLock) {
std::shared_ptr<Exe> executor;
auto singleton = sExecutor.try_get();
auto lock = sExecutorLock.try_get();
std::shared_ptr<ExecutorBase> get() {
{ {
RWSpinLock::ReadHolder guard(lock.get()); SharedMutex::ReadHolder guard(mutex_);
if ((executor = sExecutor.try_get()->lock())) { if (auto executor = executor_.lock()) {
return executor; // Fast path.
}
}
SharedMutex::WriteHolder guard(mutex_);
if (auto executor = executor_.lock()) {
return executor; return executor;
} }
if (!defaultExecutor_) {
defaultExecutor_ = constructDefault_();
} }
RWSpinLock::WriteHolder guard(lock.get()); return defaultExecutor_;
executor = singleton->lock();
if (!executor) {
std::weak_ptr<Exe> defaultExecutor = *sDefaultExecutor.try_get().get();
executor = defaultExecutor.lock();
sExecutor.try_get().get()->swap(defaultExecutor);
} }
return executor;
}
template <class Exe, class LockTag> void set(std::weak_ptr<ExecutorBase> executor) {
void setExecutor( SharedMutex::WriteHolder guard(mutex_);
std::weak_ptr<Exe> executor, executor_.swap(executor);
Singleton<std::weak_ptr<Exe>>& sExecutor, }
Singleton<RWSpinLock, LockTag>& sExecutorLock) {
auto lock = sExecutorLock.try_get(); private:
RWSpinLock::WriteHolder guard(*lock); SharedMutex mutex_;
std::weak_ptr<Exe> executor_weak = std::move(executor); std::weak_ptr<ExecutorBase> executor_;
sExecutor.try_get().get()->swap(executor_weak); std::shared_ptr<ExecutorBase> defaultExecutor_;
} Function<std::unique_ptr<ExecutorBase>()> constructDefault_;
};
Singleton<GlobalExecutor<Executor>> gGlobalCPUExecutor([] {
return new GlobalExecutor<Executor>(
// Default global CPU executor is an InlineExecutor.
[] { return std::make_unique<InlineExecutor>(); });
});
Singleton<GlobalExecutor<IOExecutor>> gGlobalIOExecutor([] {
return new GlobalExecutor<IOExecutor>(
// Default global IO executor is an IOThreadPoolExecutor.
[] {
return std::make_unique<IOThreadPoolExecutor>(
std::thread::hardware_concurrency(),
std::make_shared<NamedThreadFactory>("GlobalIOThreadPool"));
});
});
} // namespace
namespace folly {
std::shared_ptr<Executor> getCPUExecutor() { std::shared_ptr<Executor> getCPUExecutor() {
return getExecutor( if (auto singleton = gGlobalCPUExecutor.try_get()) {
globalCPUExecutor, globalInlineExecutor, globalCPUExecutorLock); return singleton->get();
}
return nullptr;
} }
void setCPUExecutor(std::weak_ptr<Executor> executor) { void setCPUExecutor(std::weak_ptr<Executor> executor) {
setExecutor(std::move(executor), globalCPUExecutor, globalCPUExecutorLock); if (auto singleton = gGlobalCPUExecutor.try_get()) {
singleton->set(std::move(executor));
}
} }
std::shared_ptr<IOExecutor> getIOExecutor() { std::shared_ptr<IOExecutor> getIOExecutor() {
return getExecutor( if (auto singleton = gGlobalIOExecutor.try_get()) {
globalIOExecutor, globalIOThreadPool, globalIOExecutorLock); return singleton->get();
}
return nullptr;
} }
EventBase* getEventBase() { void setIOExecutor(std::weak_ptr<IOExecutor> executor) {
return getIOExecutor()->getEventBase(); if (auto singleton = gGlobalIOExecutor.try_get()) {
singleton->set(std::move(executor));
}
} }
void setIOExecutor(std::weak_ptr<IOExecutor> executor) { EventBase* getEventBase() {
setExecutor(std::move(executor), globalIOExecutor, globalIOExecutorLock); return getIOExecutor()->getEventBase();
} }
} // namespace folly } // namespace folly
...@@ -23,27 +23,44 @@ ...@@ -23,27 +23,44 @@
namespace folly { namespace folly {
// Retrieve the global Executor. If there is none, a default InlineExecutor /**
// will be constructed and returned. This is named CPUExecutor to distinguish * Retrieve the global Executor. If there is none, a default InlineExecutor
// it from IOExecutor below and to hint that it's intended for CPU-bound tasks. * will be constructed and returned. This is named CPUExecutor to distinguish
* it from IOExecutor below and to hint that it's intended for CPU-bound tasks.
*
* Can return nullptr on shutdown.
*/
std::shared_ptr<folly::Executor> getCPUExecutor(); std::shared_ptr<folly::Executor> getCPUExecutor();
// Set an Executor to be the global Executor which will be returned by /**
// subsequent calls to getCPUExecutor(). * Set an Executor to be the global Executor which will be returned by
* subsequent calls to getCPUExecutor().
*/
void setCPUExecutor(std::weak_ptr<folly::Executor> executor); void setCPUExecutor(std::weak_ptr<folly::Executor> executor);
// Retrieve the global IOExecutor. If there is none, a default /**
// IOThreadPoolExecutor will be constructed and returned. * Retrieve the global IOExecutor. If there is none, a default
// * IOThreadPoolExecutor will be constructed and returned.
// IOExecutors differ from Executors in that they drive and provide access to *
// one or more EventBases. * IOExecutors differ from Executors in that they drive and provide access to
* one or more EventBases.
*
* Can return nullptr on shutdown.
*/
std::shared_ptr<IOExecutor> getIOExecutor(); std::shared_ptr<IOExecutor> getIOExecutor();
// Retrieve an event base from the global IOExecutor /**
folly::EventBase* getEventBase(); * Set an IOExecutor to be the global IOExecutor which will be returned by
* subsequent calls to getIOExecutor().
// Set an IOExecutor to be the global IOExecutor which will be returned by */
// subsequent calls to getIOExecutor().
void setIOExecutor(std::weak_ptr<IOExecutor> executor); void setIOExecutor(std::weak_ptr<IOExecutor> executor);
/**
* Retrieve an event base from the global IOExecutor
*
* NOTE: This is not shutdown-safe, the returned pointer may be
* invalid during shutdown.
*/
folly::EventBase* getEventBase();
} // namespace folly } // namespace folly
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