Commit e0a9c732 authored by Dan Melnic's avatar Dan Melnic Committed by Facebook Github Bot

Add support for multiple EventBase backends

Summary: Add support for multiple EventBase backends

Reviewed By: simpkins, mjoras

Differential Revision: D16157574

fbshipit-source-id: 915c3f08cac5673b5c4ca52a661b41b8a228df75
parent c0e397b2
...@@ -719,7 +719,7 @@ if (BUILD_TESTS) ...@@ -719,7 +719,7 @@ if (BUILD_TESTS)
TEST DelayedDestructionTest SOURCES DelayedDestructionTest.cpp TEST DelayedDestructionTest SOURCES DelayedDestructionTest.cpp
TEST DelayedDestructionBaseTest SOURCES DelayedDestructionBaseTest.cpp TEST DelayedDestructionBaseTest SOURCES DelayedDestructionBaseTest.cpp
TEST DestructorCheckTest SOURCES DestructorCheckTest.cpp TEST DestructorCheckTest SOURCES DestructorCheckTest.cpp
TEST EventBaseTest SOURCES EventBaseTest.cpp TEST EventBaseTest SOURCES EventBaseTest.cpp EventBaseTestLib.cpp
TEST EventBaseLocalTest SOURCES EventBaseLocalTest.cpp TEST EventBaseLocalTest SOURCES EventBaseLocalTest.cpp
TEST HHWheelTimerTest SOURCES HHWheelTimerTest.cpp TEST HHWheelTimerTest SOURCES HHWheelTimerTest.cpp
TEST HHWheelTimerSlowTests SLOW TEST HHWheelTimerSlowTests SLOW
......
...@@ -29,11 +29,11 @@ STTimerFDTimeoutManager::~STTimerFDTimeoutManager() { ...@@ -29,11 +29,11 @@ STTimerFDTimeoutManager::~STTimerFDTimeoutManager() {
void STTimerFDTimeoutManager::setActive(AsyncTimeout* obj, bool active) { void STTimerFDTimeoutManager::setActive(AsyncTimeout* obj, bool active) {
if (obj) { if (obj) {
struct event* ev = obj->getEvent(); auto* ev = obj->getEvent();
if (active) { if (active) {
event_ref_flags(ev) |= EVLIST_ACTIVE; event_ref_flags(ev->getEvent()) |= EVLIST_ACTIVE;
} else { } else {
event_ref_flags(ev) &= ~EVLIST_ACTIVE; event_ref_flags(ev->getEvent()) &= ~EVLIST_ACTIVE;
} }
} }
} }
......
...@@ -42,6 +42,7 @@ void TimerFD::close() { ...@@ -42,6 +42,7 @@ void TimerFD::close() {
unregisterHandler(); unregisterHandler();
if (fd_ > 0) { if (fd_ > 0) {
detachEventBase();
changeHandlerFD(NetworkSocket()); changeHandlerFD(NetworkSocket());
::close(fd_); ::close(fd_);
fd_ = -1; fd_ = -1;
......
...@@ -27,25 +27,23 @@ namespace folly { ...@@ -27,25 +27,23 @@ namespace folly {
AsyncTimeout::AsyncTimeout(TimeoutManager* timeoutManager) AsyncTimeout::AsyncTimeout(TimeoutManager* timeoutManager)
: timeoutManager_(timeoutManager) { : timeoutManager_(timeoutManager) {
event_set( event_.eb_event_set(
&event_,
NetworkSocket::invalid_handle_value, NetworkSocket::invalid_handle_value,
EV_TIMEOUT, EV_TIMEOUT,
&AsyncTimeout::libeventCallback, &AsyncTimeout::libeventCallback,
this); this);
event_.ev_base = nullptr; event_.eb_ev_base(nullptr);
timeoutManager_->attachTimeoutManager( timeoutManager_->attachTimeoutManager(
this, TimeoutManager::InternalEnum::NORMAL); this, TimeoutManager::InternalEnum::NORMAL);
} }
AsyncTimeout::AsyncTimeout(EventBase* eventBase) : timeoutManager_(eventBase) { AsyncTimeout::AsyncTimeout(EventBase* eventBase) : timeoutManager_(eventBase) {
event_set( event_.eb_event_set(
&event_,
NetworkSocket::invalid_handle_value, NetworkSocket::invalid_handle_value,
EV_TIMEOUT, EV_TIMEOUT,
&AsyncTimeout::libeventCallback, &AsyncTimeout::libeventCallback,
this); this);
event_.ev_base = nullptr; event_.eb_ev_base(nullptr);
if (eventBase) { if (eventBase) {
timeoutManager_->attachTimeoutManager( timeoutManager_->attachTimeoutManager(
this, TimeoutManager::InternalEnum::NORMAL); this, TimeoutManager::InternalEnum::NORMAL);
...@@ -56,36 +54,33 @@ AsyncTimeout::AsyncTimeout( ...@@ -56,36 +54,33 @@ AsyncTimeout::AsyncTimeout(
TimeoutManager* timeoutManager, TimeoutManager* timeoutManager,
InternalEnum internal) InternalEnum internal)
: timeoutManager_(timeoutManager) { : timeoutManager_(timeoutManager) {
event_set( event_.eb_event_set(
&event_,
NetworkSocket::invalid_handle_value, NetworkSocket::invalid_handle_value,
EV_TIMEOUT, EV_TIMEOUT,
&AsyncTimeout::libeventCallback, &AsyncTimeout::libeventCallback,
this); this);
event_.ev_base = nullptr; event_.eb_ev_base(nullptr);
timeoutManager_->attachTimeoutManager(this, internal); timeoutManager_->attachTimeoutManager(this, internal);
} }
AsyncTimeout::AsyncTimeout(EventBase* eventBase, InternalEnum internal) AsyncTimeout::AsyncTimeout(EventBase* eventBase, InternalEnum internal)
: timeoutManager_(eventBase) { : timeoutManager_(eventBase) {
event_set( event_.eb_event_set(
&event_,
NetworkSocket::invalid_handle_value, NetworkSocket::invalid_handle_value,
EV_TIMEOUT, EV_TIMEOUT,
&AsyncTimeout::libeventCallback, &AsyncTimeout::libeventCallback,
this); this);
event_.ev_base = nullptr; event_.eb_ev_base(nullptr);
timeoutManager_->attachTimeoutManager(this, internal); timeoutManager_->attachTimeoutManager(this, internal);
} }
AsyncTimeout::AsyncTimeout() : timeoutManager_(nullptr) { AsyncTimeout::AsyncTimeout() : timeoutManager_(nullptr) {
event_set( event_.eb_event_set(
&event_,
NetworkSocket::invalid_handle_value, NetworkSocket::invalid_handle_value,
EV_TIMEOUT, EV_TIMEOUT,
&AsyncTimeout::libeventCallback, &AsyncTimeout::libeventCallback,
this); this);
event_.ev_base = nullptr; event_.eb_ev_base(nullptr);
} }
AsyncTimeout::~AsyncTimeout() { AsyncTimeout::~AsyncTimeout() {
...@@ -117,7 +112,7 @@ void AsyncTimeout::cancelTimeout() { ...@@ -117,7 +112,7 @@ void AsyncTimeout::cancelTimeout() {
} }
bool AsyncTimeout::isScheduled() const { bool AsyncTimeout::isScheduled() const {
return EventUtil::isEventRegistered(&event_); return event_.isEventRegistered();
} }
void AsyncTimeout::attachTimeoutManager( void AsyncTimeout::attachTimeoutManager(
...@@ -164,7 +159,9 @@ void AsyncTimeout::libeventCallback(libevent_fd_t fd, short events, void* arg) { ...@@ -164,7 +159,9 @@ void AsyncTimeout::libeventCallback(libevent_fd_t fd, short events, void* arg) {
(void)events; (void)events;
// double check that ev_flags gets reset when the timeout is not running // double check that ev_flags gets reset when the timeout is not running
assert((event_ref_flags(&timeout->event_) & ~EVLIST_INTERNAL) == EVLIST_INIT); assert(
(event_ref_flags(timeout->event_.getEvent()) & ~EVLIST_INTERNAL) ==
EVLIST_INIT);
// this can't possibly fire if timeout->eventBase_ is nullptr // this can't possibly fire if timeout->eventBase_ is nullptr
timeout->timeoutManager_->bumpHandlingTime(); timeout->timeoutManager_->bumpHandlingTime();
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#pragma once #pragma once
#include <folly/io/async/EventBaseBackendBase.h>
#include <folly/io/async/TimeoutManager.h> #include <folly/io/async/TimeoutManager.h>
#include <folly/portability/Event.h> #include <folly/portability/Event.h>
...@@ -149,7 +150,7 @@ class AsyncTimeout { ...@@ -149,7 +150,7 @@ class AsyncTimeout {
/** /**
* Returns the internal handle to the event * Returns the internal handle to the event
*/ */
struct event* getEvent() { EventBaseBackendBase::Event* getEvent() {
return &event_; return &event_;
} }
...@@ -220,7 +221,7 @@ class AsyncTimeout { ...@@ -220,7 +221,7 @@ class AsyncTimeout {
private: private:
static void libeventCallback(libevent_fd_t fd, short events, void* arg); static void libeventCallback(libevent_fd_t fd, short events, void* arg);
struct event event_; EventBaseBackendBase::Event event_;
/* /*
* Store a pointer to the TimeoutManager. We only use this * Store a pointer to the TimeoutManager. We only use this
......
...@@ -28,14 +28,102 @@ ...@@ -28,14 +28,102 @@
#include <folly/Memory.h> #include <folly/Memory.h>
#include <folly/String.h> #include <folly/String.h>
#include <folly/io/async/EventBaseBackendBase.h>
#include <folly/io/async/NotificationQueue.h> #include <folly/io/async/NotificationQueue.h>
#include <folly/io/async/VirtualEventBase.h> #include <folly/io/async/VirtualEventBase.h>
#include <folly/portability/Unistd.h> #include <folly/portability/Unistd.h>
#include <folly/synchronization/Baton.h> #include <folly/synchronization/Baton.h>
#include <folly/system/ThreadName.h> #include <folly/system/ThreadName.h>
namespace folly { namespace {
class EventBaseBackend : public folly::EventBaseBackendBase {
public:
EventBaseBackend();
explicit EventBaseBackend(event_base* evb);
~EventBaseBackend() override;
event_base* getEventBase() override {
return evb_;
}
int eb_event_base_loop(int flags) override;
int eb_event_base_loopbreak() override;
int eb_event_add(Event& event, const struct timeval* timeout) override;
int eb_event_del(EventBaseBackendBase::Event& event) override;
private:
event_base* evb_;
};
// The interface used to libevent is not thread-safe. Calls to
// event_init() and event_base_free() directly modify an internal
// global 'current_base', so a mutex is required to protect this.
//
// event_init() should only ever be called once. Subsequent calls
// should be made to event_base_new(). We can recognise that
// event_init() has already been called by simply inspecting current_base.
static std::mutex libevent_mutex_;
EventBaseBackend::EventBaseBackend() {
struct event ev;
{
std::lock_guard<std::mutex> lock(libevent_mutex_);
// The value 'current_base' (libevent 1) or
// 'event_global_current_base_' (libevent 2) is filled in by event_set(),
// allowing examination of its value without an explicit reference here.
// If ev.ev_base is nullptr, then event_init() must be called, otherwise
// call event_base_new().
::event_set(&ev, 0, 0, nullptr, nullptr);
if (!ev.ev_base) {
evb_ = event_init();
}
}
if (ev.ev_base) {
evb_ = ::event_base_new();
}
if (UNLIKELY(evb_ == nullptr)) {
LOG(ERROR) << "EventBase(): Failed to init event base.";
folly::throwSystemError("error in EventBaseBackend::EventBaseBackend()");
}
}
EventBaseBackend::EventBaseBackend(event_base* evb) : evb_(evb) {
if (UNLIKELY(evb_ == nullptr)) {
LOG(ERROR) << "EventBase(): Pass nullptr as event base.";
throw std::invalid_argument("EventBase(): event base cannot be nullptr");
}
}
int EventBaseBackend::eb_event_base_loop(int flags) {
return event_base_loop(evb_, flags);
}
int EventBaseBackend::eb_event_base_loopbreak() {
return event_base_loopbreak(evb_);
}
int EventBaseBackend::eb_event_add(
Event& event,
const struct timeval* timeout) {
return event_add(event.getEvent(), timeout);
}
int EventBaseBackend::eb_event_del(EventBaseBackendBase::Event& event) {
return event_del(event.getEvent());
}
EventBaseBackend::~EventBaseBackend() {
std::lock_guard<std::mutex> lock(libevent_mutex_);
event_base_free(evb_);
}
} // namespace
namespace folly {
/* /*
* EventBase::FunctionRunner * EventBase::FunctionRunner
*/ */
...@@ -50,7 +138,7 @@ class EventBase::FunctionRunner ...@@ -50,7 +138,7 @@ class EventBase::FunctionRunner
// To have similar bejaviour to libevent1.4, tell the loop to break here. // To have similar bejaviour to libevent1.4, tell the loop to break here.
// Note that loop() may still continue to loop, but it will also check the // Note that loop() may still continue to loop, but it will also check the
// stop_ flag as well as runInLoop callbacks, etc. // stop_ flag as well as runInLoop callbacks, etc.
event_base_loopbreak(getEventBase()->evb_); getEventBase()->getBackend()->eb_event_base_loopbreak();
if (!msg) { if (!msg) {
// terminateLoopSoon() sends a null message just to // terminateLoopSoon() sends a null message just to
...@@ -61,15 +149,6 @@ class EventBase::FunctionRunner ...@@ -61,15 +149,6 @@ class EventBase::FunctionRunner
} }
}; };
// The interface used to libevent is not thread-safe. Calls to
// event_init() and event_base_free() directly modify an internal
// global 'current_base', so a mutex is required to protect this.
//
// event_init() should only ever be called once. Subsequent calls
// should be made to event_base_new(). We can recognise that
// event_init() has already been called by simply inspecting current_base.
static std::mutex libevent_mutex_;
/* /*
* EventBase methods * EventBase methods
*/ */
...@@ -92,39 +171,25 @@ EventBase::EventBase(bool enableTimeMeasurement) ...@@ -92,39 +171,25 @@ EventBase::EventBase(bool enableTimeMeasurement)
observer_(nullptr), observer_(nullptr),
observerSampleCount_(0), observerSampleCount_(0),
executionObserver_(nullptr) { executionObserver_(nullptr) {
struct event ev; evb_ = getDefaultBackend();
{
std::lock_guard<std::mutex> lock(libevent_mutex_);
// The value 'current_base' (libevent 1) or
// 'event_global_current_base_' (libevent 2) is filled in by event_set(),
// allowing examination of its value without an explicit reference here.
// If ev.ev_base is nullptr, then event_init() must be called, otherwise
// call event_base_new().
event_set(&ev, 0, 0, nullptr, nullptr);
if (!ev.ev_base) {
evb_ = event_init();
}
}
if (ev.ev_base) {
evb_ = event_base_new();
}
if (UNLIKELY(evb_ == nullptr)) {
LOG(ERROR) << "EventBase(): Failed to init event base.";
folly::throwSystemError("error in EventBase::EventBase()");
}
VLOG(5) << "EventBase(): Created."; VLOG(5) << "EventBase(): Created.";
initNotificationQueue(); initNotificationQueue();
} }
// takes ownership of the event_base // takes ownership of the event_base
EventBase::EventBase(event_base* evb, bool enableTimeMeasurement) EventBase::EventBase(event_base* evb, bool enableTimeMeasurement)
: EventBase(
std::make_unique<EventBaseBackend>(evb),
enableTimeMeasurement) {}
// takes ownership of the backend
EventBase::EventBase(
std::unique_ptr<EventBaseBackendBase>&& evb,
bool enableTimeMeasurement)
: runOnceCallbacks_(nullptr), : runOnceCallbacks_(nullptr),
stop_(false), stop_(false),
loopThread_(), loopThread_(),
evb_(evb),
queue_(nullptr), queue_(nullptr),
fnRunner_(nullptr), fnRunner_(nullptr),
maxLatency_(0), maxLatency_(0),
...@@ -139,10 +204,7 @@ EventBase::EventBase(event_base* evb, bool enableTimeMeasurement) ...@@ -139,10 +204,7 @@ EventBase::EventBase(event_base* evb, bool enableTimeMeasurement)
observer_(nullptr), observer_(nullptr),
observerSampleCount_(0), observerSampleCount_(0),
executionObserver_(nullptr) { executionObserver_(nullptr) {
if (UNLIKELY(evb_ == nullptr)) { evb_ = evb ? std::move(evb) : getDefaultBackend();
LOG(ERROR) << "EventBase(): Pass nullptr as event base.";
throw std::invalid_argument("EventBase(): event base cannot be nullptr");
}
initNotificationQueue(); initNotificationQueue();
} }
...@@ -187,10 +249,7 @@ EventBase::~EventBase() { ...@@ -187,10 +249,7 @@ EventBase::~EventBase() {
// Stop consumer before deleting NotificationQueue // Stop consumer before deleting NotificationQueue
fnRunner_->stopConsuming(); fnRunner_->stopConsuming();
{ evb_.reset();
std::lock_guard<std::mutex> lock(libevent_mutex_);
event_base_free(evb_);
}
for (auto storage : localStorageToDtor_) { for (auto storage : localStorageToDtor_) {
storage->onEventBaseDestruction(*this); storage->onEventBaseDestruction(*this);
...@@ -199,6 +258,10 @@ EventBase::~EventBase() { ...@@ -199,6 +258,10 @@ EventBase::~EventBase() {
VLOG(5) << "EventBase(): Destroyed."; VLOG(5) << "EventBase(): Destroyed.";
} }
std::unique_ptr<EventBaseBackendBase> EventBase::getDefaultBackend() {
return std::make_unique<EventBaseBackend>();
}
size_t EventBase::getNotificationQueueSize() const { size_t EventBase::getNotificationQueueSize() const {
return queue_->size(); return queue_->size();
} }
...@@ -341,9 +404,9 @@ bool EventBase::loopBody(int flags, bool ignoreKeepAlive) { ...@@ -341,9 +404,9 @@ bool EventBase::loopBody(int flags, bool ignoreKeepAlive) {
// nobody can add loop callbacks from within this thread if // nobody can add loop callbacks from within this thread if
// we don't have to handle anything to start with... // we don't have to handle anything to start with...
if (blocking && loopCallbacks_.empty()) { if (blocking && loopCallbacks_.empty()) {
res = event_base_loop(evb_, EVLOOP_ONCE); res = evb_->eb_event_base_loop(EVLOOP_ONCE);
} else { } else {
res = event_base_loop(evb_, EVLOOP_ONCE | EVLOOP_NONBLOCK); res = evb_->eb_event_base_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
} }
ranLoopCallbacks = runLoopCallbacks(); ranLoopCallbacks = runLoopCallbacks();
...@@ -513,7 +576,7 @@ void EventBase::terminateLoopSoon() { ...@@ -513,7 +576,7 @@ void EventBase::terminateLoopSoon() {
// Call event_base_loopbreak() so that libevent will exit the next time // Call event_base_loopbreak() so that libevent will exit the next time
// around the loop. // around the loop.
event_base_loopbreak(evb_); evb_->eb_event_base_loopbreak();
// If terminateLoopSoon() is called from another thread, // If terminateLoopSoon() is called from another thread,
// the EventBase thread might be stuck waiting for events. // the EventBase thread might be stuck waiting for events.
...@@ -718,20 +781,20 @@ bool EventBase::nothingHandledYet() const noexcept { ...@@ -718,20 +781,20 @@ bool EventBase::nothingHandledYet() const noexcept {
} }
void EventBase::attachTimeoutManager(AsyncTimeout* obj, InternalEnum internal) { void EventBase::attachTimeoutManager(AsyncTimeout* obj, InternalEnum internal) {
struct event* ev = obj->getEvent(); auto* ev = obj->getEvent();
assert(ev->ev_base == nullptr); assert(ev->eb_ev_base() == nullptr);
event_base_set(getLibeventBase(), ev); ev->eb_event_base_set(this);
if (internal == AsyncTimeout::InternalEnum::INTERNAL) { if (internal == AsyncTimeout::InternalEnum::INTERNAL) {
// Set the EVLIST_INTERNAL flag // Set the EVLIST_INTERNAL flag
event_ref_flags(ev) |= EVLIST_INTERNAL; event_ref_flags(ev->getEvent()) |= EVLIST_INTERNAL;
} }
} }
void EventBase::detachTimeoutManager(AsyncTimeout* obj) { void EventBase::detachTimeoutManager(AsyncTimeout* obj) {
cancelTimeout(obj); cancelTimeout(obj);
struct event* ev = obj->getEvent(); auto* ev = obj->getEvent();
ev->ev_base = nullptr; ev->eb_ev_base(nullptr);
} }
bool EventBase::scheduleTimeout( bool EventBase::scheduleTimeout(
...@@ -743,11 +806,11 @@ bool EventBase::scheduleTimeout( ...@@ -743,11 +806,11 @@ bool EventBase::scheduleTimeout(
tv.tv_sec = long(timeout.count() / 1000LL); tv.tv_sec = long(timeout.count() / 1000LL);
tv.tv_usec = long((timeout.count() % 1000LL) * 1000LL); tv.tv_usec = long((timeout.count() % 1000LL) * 1000LL);
struct event* ev = obj->getEvent(); auto* ev = obj->getEvent();
DCHECK(ev->ev_base); DCHECK(ev->eb_ev_base());
if (event_add(ev, &tv) < 0) { if (ev->eb_event_add(&tv) < 0) {
LOG(ERROR) << "EventBase: failed to schedule timeout: " << errnoStr(errno); LOG(ERROR) << "EventBase: failed to schedule timeout: " << errnoStr(errno);
return false; return false;
} }
...@@ -757,9 +820,9 @@ bool EventBase::scheduleTimeout( ...@@ -757,9 +820,9 @@ bool EventBase::scheduleTimeout(
void EventBase::cancelTimeout(AsyncTimeout* obj) { void EventBase::cancelTimeout(AsyncTimeout* obj) {
dcheckIsInEventBaseThread(); dcheckIsInEventBaseThread();
struct event* ev = obj->getEvent(); auto* ev = obj->getEvent();
if (EventUtil::isEventRegistered(ev)) { if (ev->isEventRegistered()) {
event_del(ev); ev->eb_event_del();
} }
} }
...@@ -784,6 +847,10 @@ void EventBase::scheduleAt(Func&& fn, TimePoint const& timeout) { ...@@ -784,6 +847,10 @@ void EventBase::scheduleAt(Func&& fn, TimePoint const& timeout) {
std::chrono::duration_cast<std::chrono::milliseconds>(duration)); std::chrono::duration_cast<std::chrono::milliseconds>(duration));
} }
event_base* EventBase::getLibeventBase() const {
return evb_ ? (evb_->getEventBase()) : nullptr;
}
const char* EventBase::getLibeventVersion() { const char* EventBase::getLibeventVersion() {
return event_get_version(); return event_get_version();
} }
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <folly/synchronization/CallOnce.h> #include <folly/synchronization/CallOnce.h>
namespace folly { namespace folly {
class EventBaseBackendBase;
using Cob = Func; // defined in folly/Executor.h using Cob = Func; // defined in folly/Executor.h
template <typename MessageT> template <typename MessageT>
...@@ -323,6 +324,9 @@ class EventBase : public TimeoutManager, ...@@ -323,6 +324,9 @@ class EventBase : public TimeoutManager,
* observer, max latency and avg loop time. * observer, max latency and avg loop time.
*/ */
explicit EventBase(event_base* evb, bool enableTimeMeasurement = true); explicit EventBase(event_base* evb, bool enableTimeMeasurement = true);
explicit EventBase(
std::unique_ptr<EventBaseBackendBase>&& evb,
bool enableTimeMeasurement = true);
~EventBase() override; ~EventBase() override;
/** /**
...@@ -667,13 +671,15 @@ class EventBase : public TimeoutManager, ...@@ -667,13 +671,15 @@ class EventBase : public TimeoutManager,
return *wheelTimer_.get(); return *wheelTimer_.get();
} }
EventBaseBackendBase* getBackend() {
return evb_.get();
}
// --------- interface to underlying libevent base ------------ // --------- interface to underlying libevent base ------------
// Avoid using these functions if possible. These functions are not // Avoid using these functions if possible. These functions are not
// guaranteed to always be present if we ever provide alternative EventBase // guaranteed to always be present if we ever provide alternative EventBase
// implementations that do not use libevent internally. // implementations that do not use libevent internally.
event_base* getLibeventBase() const { event_base* getLibeventBase() const;
return evb_;
}
static const char* getLibeventVersion(); static const char* getLibeventVersion();
static const char* getLibeventMethod(); static const char* getLibeventMethod();
...@@ -802,6 +808,8 @@ class EventBase : public TimeoutManager, ...@@ -802,6 +808,8 @@ class EventBase : public TimeoutManager,
/// Implements the IOExecutor interface /// Implements the IOExecutor interface
EventBase* getEventBase() override; EventBase* getEventBase() override;
static std::unique_ptr<EventBaseBackendBase> getDefaultBackend();
protected: protected:
bool keepAliveAcquire() override { bool keepAliveAcquire() override {
if (inRunningEventBaseThread()) { if (inRunningEventBaseThread()) {
...@@ -864,9 +872,6 @@ class EventBase : public TimeoutManager, ...@@ -864,9 +872,6 @@ class EventBase : public TimeoutManager,
// std::thread::id{} if loop is not running. // std::thread::id{} if loop is not running.
std::atomic<std::thread::id> loopThread_; std::atomic<std::thread::id> loopThread_;
// pointer to underlying event_base class doing the heavy lifting
event_base* evb_;
// A notification queue for runInEventBaseThread() to use // A notification queue for runInEventBaseThread() to use
// to send function requests to the EventBase thread. // to send function requests to the EventBase thread.
std::unique_ptr<NotificationQueue<Func>> queue_; std::unique_ptr<NotificationQueue<Func>> queue_;
...@@ -923,6 +928,9 @@ class EventBase : public TimeoutManager, ...@@ -923,6 +928,9 @@ class EventBase : public TimeoutManager,
folly::once_flag virtualEventBaseInitFlag_; folly::once_flag virtualEventBaseInitFlag_;
std::unique_ptr<VirtualEventBase> virtualEventBase_; std::unique_ptr<VirtualEventBase> virtualEventBase_;
// pointer to underlying backend class doing the heavy lifting
std::unique_ptr<EventBaseBackendBase> evb_;
}; };
template <typename T> template <typename T>
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/io/async/EventBaseBackendBase.h>
#include <folly/io/async/EventBase.h>
namespace folly {
void EventBaseEvent::eb_ev_base(EventBase* evb) {
evb_ = evb;
event_.ev_base = evb ? evb->getLibeventBase() : nullptr;
}
int EventBaseEvent::eb_event_base_set(EventBase* evb) {
evb_ = evb;
auto* base = evb_ ? (evb_->getLibeventBase()) : nullptr;
if (base) {
return ::event_base_set(base, &event_);
}
return -1;
}
int EventBaseEvent::eb_event_add(const struct timeval* timeout) {
auto* backend = evb_ ? (evb_->getBackend()) : nullptr;
if (backend) {
return backend->eb_event_add(*this, timeout);
}
return -1;
}
int EventBaseEvent::eb_event_del() {
auto* backend = evb_ ? (evb_->getBackend()) : nullptr;
if (backend) {
return backend->eb_event_del(*this);
}
return -1;
}
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <folly/io/async/EventUtil.h>
#include <folly/portability/Event.h>
namespace folly {
class EventBase;
class EventBaseEvent {
public:
EventBaseEvent() = default;
~EventBaseEvent() {
if (userData_ && freeFn_) {
freeFn_(userData_);
}
}
EventBaseEvent(const EventBaseEvent&) = delete;
EventBaseEvent& operator=(const EventBaseEvent&) = delete;
typedef void (*FreeFunction)(void* userData);
const struct event* getEvent() const {
return &event_;
}
struct event* getEvent() {
return &event_;
}
bool isEventRegistered() const {
return EventUtil::isEventRegistered(&event_);
}
libevent_fd_t eb_ev_fd() const {
return event_.ev_fd;
}
short eb_ev_events() const {
return event_.ev_events;
}
int eb_ev_res() const {
return event_.ev_res;
}
void* getUserData() {
return userData_;
}
void setUserData(void* userData) {
userData_ = userData;
}
void setUserData(void* userData, FreeFunction freeFn) {
userData_ = userData;
freeFn_ = freeFn;
}
void eb_event_set(
libevent_fd_t fd,
short events,
void (*callback)(libevent_fd_t, short, void*),
void* arg) {
event_set(&event_, fd, events, callback, arg);
}
void eb_ev_base(EventBase* evb);
EventBase* eb_ev_base() const {
return evb_;
}
int eb_event_base_set(EventBase* evb);
int eb_event_add(const struct timeval* timeout);
int eb_event_del();
protected:
struct event event_;
EventBase* evb_{nullptr};
void* userData_{nullptr};
FreeFunction freeFn_{nullptr};
};
class EventBaseBackendBase {
public:
using Event = EventBaseEvent;
EventBaseBackendBase() = default;
virtual ~EventBaseBackendBase() = default;
EventBaseBackendBase(const EventBaseBackendBase&) = delete;
EventBaseBackendBase& operator=(const EventBaseBackendBase&) = delete;
virtual event_base* getEventBase() = 0;
virtual int eb_event_base_loop(int flags) = 0;
virtual int eb_event_base_loopbreak() = 0;
virtual int eb_event_add(Event& event, const struct timeval* timeout) = 0;
virtual int eb_event_del(Event& event) = 0;
};
} // namespace folly
...@@ -33,6 +33,17 @@ EventBaseThread::EventBaseThread( ...@@ -33,6 +33,17 @@ EventBaseThread::EventBaseThread(
} }
} }
EventBaseThread::EventBaseThread(
bool autostart,
std::unique_ptr<EventBaseBackendBase>&& evb,
EventBaseManager* ebm,
folly::StringPiece threadName)
: ebm_(ebm), evb_(std::move(evb)) {
if (autostart) {
start(threadName);
}
}
EventBaseThread::EventBaseThread(EventBaseManager* ebm) EventBaseThread::EventBaseThread(EventBaseManager* ebm)
: EventBaseThread(true, ebm) {} : EventBaseThread(true, ebm) {}
...@@ -54,7 +65,8 @@ void EventBaseThread::start(folly::StringPiece threadName) { ...@@ -54,7 +65,8 @@ void EventBaseThread::start(folly::StringPiece threadName) {
if (th_) { if (th_) {
return; return;
} }
th_ = std::make_unique<ScopedEventBaseThread>(ebm_, threadName); th_ = std::make_unique<ScopedEventBaseThread>(
std::move(evb_), ebm_, threadName);
} }
void EventBaseThread::stop() { void EventBaseThread::stop() {
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
namespace folly { namespace folly {
class EventBase; class EventBase;
class EventBaseBackendBase;
class EventBaseManager; class EventBaseManager;
class ScopedEventBaseThread; class ScopedEventBaseThread;
...@@ -32,6 +33,11 @@ class EventBaseThread { ...@@ -32,6 +33,11 @@ class EventBaseThread {
bool autostart, bool autostart,
EventBaseManager* ebm = nullptr, EventBaseManager* ebm = nullptr,
folly::StringPiece threadName = folly::StringPiece()); folly::StringPiece threadName = folly::StringPiece());
EventBaseThread(
bool autostart,
std::unique_ptr<EventBaseBackendBase>&& evb,
EventBaseManager* ebm = nullptr,
folly::StringPiece threadName = folly::StringPiece());
explicit EventBaseThread(EventBaseManager* ebm); explicit EventBaseThread(EventBaseManager* ebm);
~EventBaseThread(); ~EventBaseThread();
...@@ -48,6 +54,7 @@ class EventBaseThread { ...@@ -48,6 +54,7 @@ class EventBaseThread {
private: private:
EventBaseManager* ebm_; EventBaseManager* ebm_;
std::unique_ptr<EventBaseBackendBase> evb_;
std::unique_ptr<ScopedEventBaseThread> th_; std::unique_ptr<ScopedEventBaseThread> th_;
}; };
} // namespace folly } // namespace folly
...@@ -23,14 +23,14 @@ ...@@ -23,14 +23,14 @@
namespace folly { namespace folly {
EventHandler::EventHandler(EventBase* eventBase, NetworkSocket fd) { EventHandler::EventHandler(EventBase* eventBase, NetworkSocket fd) {
event_set(&event_, fd.data, 0, &EventHandler::libeventCallback, this); event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this);
if (eventBase != nullptr) { if (eventBase != nullptr) {
setEventBase(eventBase); setEventBase(eventBase);
} else { } else {
// Callers must set the EventBase and fd before using this timeout. // Callers must set the EventBase and fd before using this timeout.
// Set event_->ev_base to nullptr to ensure that this happens. // Set event_->ev_base to nullptr to ensure that this happens.
// (otherwise libevent will initialize it to the "default" event_base) // (otherwise libevent will initialize it to the "default" event_base)
event_.ev_base = nullptr; event_.eb_ev_base(nullptr);
eventBase_ = nullptr; eventBase_ = nullptr;
} }
} }
...@@ -40,36 +40,32 @@ EventHandler::~EventHandler() { ...@@ -40,36 +40,32 @@ EventHandler::~EventHandler() {
} }
bool EventHandler::registerImpl(uint16_t events, bool internal) { bool EventHandler::registerImpl(uint16_t events, bool internal) {
assert(event_.ev_base != nullptr); assert(event_.eb_ev_base() != nullptr);
// We have to unregister the event before we can change the event flags // We have to unregister the event before we can change the event flags
if (isHandlerRegistered()) { if (isHandlerRegistered()) {
// If the new events are the same are the same as the already registered // If the new events are the same are the same as the already registered
// flags, we don't have to do anything. Just return. // flags, we don't have to do anything. Just return.
auto flags = event_ref_flags(&event_); auto flags = folly::event_ref_flags(event_.getEvent());
if (events == event_.ev_events && if (events == event_.eb_ev_events() &&
static_cast<bool>(flags & EVLIST_INTERNAL) == internal) { static_cast<bool>(flags & EVLIST_INTERNAL) == internal) {
return true; return true;
} }
event_del(&event_); event_.eb_event_del();
} }
// Update the event flags // Update the event flags
// Unfortunately, event_set() resets the event_base, so we have to remember // Unfortunately, event_set() resets the event_base, so we have to remember
// it before hand, then pass it back into event_base_set() afterwards // it before hand, then pass it back into event_base_set() afterwards
struct event_base* evb = event_.ev_base; auto* evb = event_.eb_ev_base();
event_set( event_.eb_event_set(
&event_, event_.eb_ev_fd(), short(events), &EventHandler::libeventCallback, this);
event_.ev_fd, event_.eb_event_base_set(evb);
short(events),
&EventHandler::libeventCallback,
this);
event_base_set(evb, &event_);
// Set EVLIST_INTERNAL if this is an internal event // Set EVLIST_INTERNAL if this is an internal event
if (internal) { if (internal) {
event_ref_flags(&event_) |= EVLIST_INTERNAL; folly::event_ref_flags(event_.getEvent()) |= EVLIST_INTERNAL;
} }
// Add the event. // Add the event.
...@@ -89,11 +85,11 @@ bool EventHandler::registerImpl(uint16_t events, bool internal) { ...@@ -89,11 +85,11 @@ bool EventHandler::registerImpl(uint16_t events, bool internal) {
// if the I/O event flags haven't changed. Using a separate event struct is // if the I/O event flags haven't changed. Using a separate event struct is
// therefore slightly more efficient in this case (although it does take up // therefore slightly more efficient in this case (although it does take up
// more space). // more space).
if (event_add(&event_, nullptr) < 0) { if (event_.eb_event_add(nullptr) < 0) {
LOG(ERROR) << "EventBase: failed to register event handler for fd " LOG(ERROR) << "EventBase: failed to register event handler for fd "
<< event_.ev_fd << ": " << errnoStr(errno); << event_.eb_ev_fd() << ": " << errnoStr(errno);
// Call event_del() to make sure the event is completely uninstalled // Call event_del() to make sure the event is completely uninstalled
event_del(&event_); event_.eb_event_del();
return false; return false;
} }
...@@ -102,13 +98,13 @@ bool EventHandler::registerImpl(uint16_t events, bool internal) { ...@@ -102,13 +98,13 @@ bool EventHandler::registerImpl(uint16_t events, bool internal) {
void EventHandler::unregisterHandler() { void EventHandler::unregisterHandler() {
if (isHandlerRegistered()) { if (isHandlerRegistered()) {
event_del(&event_); event_.eb_event_del();
} }
} }
void EventHandler::attachEventBase(EventBase* eventBase) { void EventHandler::attachEventBase(EventBase* eventBase) {
// attachEventBase() may only be called on detached handlers // attachEventBase() may only be called on detached handlers
assert(event_.ev_base == nullptr); assert(event_.eb_ev_base() == nullptr);
assert(!isHandlerRegistered()); assert(!isHandlerRegistered());
// This must be invoked from the EventBase's thread // This must be invoked from the EventBase's thread
eventBase->dcheckIsInEventBaseThread(); eventBase->dcheckIsInEventBaseThread();
...@@ -118,20 +114,21 @@ void EventHandler::attachEventBase(EventBase* eventBase) { ...@@ -118,20 +114,21 @@ void EventHandler::attachEventBase(EventBase* eventBase) {
void EventHandler::detachEventBase() { void EventHandler::detachEventBase() {
ensureNotRegistered(__func__); ensureNotRegistered(__func__);
event_.ev_base = nullptr; event_.eb_ev_base(nullptr);
} }
void EventHandler::changeHandlerFD(NetworkSocket fd) { void EventHandler::changeHandlerFD(NetworkSocket fd) {
ensureNotRegistered(__func__); ensureNotRegistered(__func__);
// event_set() resets event_base.ev_base, so manually restore it afterwards // event_set() resets event_base.ev_base, so manually restore it afterwards
struct event_base* evb = event_.ev_base; auto* evb = event_.eb_ev_base();
event_set(&event_, fd.data, 0, &EventHandler::libeventCallback, this); event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this);
event_.ev_base = evb; // don't use event_base_set(), since evb may be nullptr event_.eb_ev_base(
evb); // don't use event_base_set(), since evb may be nullptr
} }
void EventHandler::initHandler(EventBase* eventBase, NetworkSocket fd) { void EventHandler::initHandler(EventBase* eventBase, NetworkSocket fd) {
ensureNotRegistered(__func__); ensureNotRegistered(__func__);
event_set(&event_, fd.data, 0, &EventHandler::libeventCallback, this); event_.eb_event_set(fd.data, 0, &EventHandler::libeventCallback, this);
setEventBase(eventBase); setEventBase(eventBase);
} }
...@@ -147,7 +144,7 @@ void EventHandler::ensureNotRegistered(const char* fn) { ...@@ -147,7 +144,7 @@ void EventHandler::ensureNotRegistered(const char* fn) {
void EventHandler::libeventCallback(libevent_fd_t fd, short events, void* arg) { void EventHandler::libeventCallback(libevent_fd_t fd, short events, void* arg) {
auto handler = reinterpret_cast<EventHandler*>(arg); auto handler = reinterpret_cast<EventHandler*>(arg);
assert(fd == handler->event_.ev_fd); assert(fd == handler->event_.eb_ev_fd());
(void)fd; // prevent unused variable warnings (void)fd; // prevent unused variable warnings
auto observer = handler->eventBase_->getExecutionObserver(); auto observer = handler->eventBase_->getExecutionObserver();
...@@ -166,13 +163,13 @@ void EventHandler::libeventCallback(libevent_fd_t fd, short events, void* arg) { ...@@ -166,13 +163,13 @@ void EventHandler::libeventCallback(libevent_fd_t fd, short events, void* arg) {
} }
void EventHandler::setEventBase(EventBase* eventBase) { void EventHandler::setEventBase(EventBase* eventBase) {
event_base_set(eventBase->getLibeventBase(), &event_); event_.eb_event_base_set(eventBase);
eventBase_ = eventBase; eventBase_ = eventBase;
} }
bool EventHandler::isPending() const { bool EventHandler::isPending() const {
if (event_ref_flags(&event_) & EVLIST_ACTIVE) { if (folly::event_ref_flags(event_.getEvent()) & EVLIST_ACTIVE) {
if (event_.ev_res & EV_READ) { if (event_.eb_ev_res() & EV_READ) {
return true; return true;
} }
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <glog/logging.h> #include <glog/logging.h>
#include <folly/io/async/EventBaseBackendBase.h>
#include <folly/io/async/EventUtil.h> #include <folly/io/async/EventUtil.h>
#include <folly/net/NetworkSocket.h> #include <folly/net/NetworkSocket.h>
#include <folly/portability/Event.h> #include <folly/portability/Event.h>
...@@ -110,7 +111,7 @@ class EventHandler { ...@@ -110,7 +111,7 @@ class EventHandler {
* Returns true if the handler is currently registered. * Returns true if the handler is currently registered.
*/ */
bool isHandlerRegistered() const { bool isHandlerRegistered() const {
return EventUtil::isEventRegistered(&event_); return event_.isEventRegistered();
} }
/** /**
...@@ -155,7 +156,7 @@ class EventHandler { ...@@ -155,7 +156,7 @@ class EventHandler {
* Return the set of events that we're currently registered for. * Return the set of events that we're currently registered for.
*/ */
uint16_t getRegisteredEvents() const { uint16_t getRegisteredEvents() const {
return (isHandlerRegistered()) ? uint16_t(event_.ev_events) : 0u; return (isHandlerRegistered()) ? (uint16_t)(event_.eb_ev_events()) : 0u;
} }
/** /**
...@@ -186,7 +187,7 @@ class EventHandler { ...@@ -186,7 +187,7 @@ class EventHandler {
static void libeventCallback(libevent_fd_t fd, short events, void* arg); static void libeventCallback(libevent_fd_t fd, short events, void* arg);
struct event event_; EventBaseBackendBase::Event event_;
EventBase* eventBase_; EventBase* eventBase_;
}; };
......
...@@ -24,8 +24,11 @@ namespace folly { ...@@ -24,8 +24,11 @@ namespace folly {
#if LIBEVENT_VERSION_NUMBER <= 0x02010101 #if LIBEVENT_VERSION_NUMBER <= 0x02010101
#define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_##name #define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_##name
#define FOLLY_LIBEVENT_COMPAT_PLUCK2(name) ev_##name
#else #else
#define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_evcallback.evcb_##name #define FOLLY_LIBEVENT_COMPAT_PLUCK(name) ev_evcallback.evcb_##name
#define FOLLY_LIBEVENT_COMPAT_PLUCK2(name) \
ev_evcallback.evcb_cb_union.evcb_##name
#endif #endif
#define FOLLY_LIBEVENT_DEF_ACCESSORS(name) \ #define FOLLY_LIBEVENT_DEF_ACCESSORS(name) \
inline auto event_ref_##name(struct event* ev) \ inline auto event_ref_##name(struct event* ev) \
...@@ -38,7 +41,21 @@ namespace folly { ...@@ -38,7 +41,21 @@ namespace folly {
} \ } \
// //
#define FOLLY_LIBEVENT_DEF_ACCESSORS2(name) \
inline auto event_ref_##name(struct event* ev) \
->decltype(std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) { \
return std::ref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name)); \
} \
inline auto event_ref_##name(struct event const* ev) \
->decltype(std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name))) { \
return std::cref(ev->FOLLY_LIBEVENT_COMPAT_PLUCK2(name)); \
} \
//
FOLLY_LIBEVENT_DEF_ACCESSORS(arg)
FOLLY_LIBEVENT_DEF_ACCESSORS(flags) FOLLY_LIBEVENT_DEF_ACCESSORS(flags)
// evcb_callback is inside a union{...} evcb_cb_union
FOLLY_LIBEVENT_DEF_ACCESSORS2(callback)
#undef FOLLY_LIBEVENT_COMPAT_PLUCK #undef FOLLY_LIBEVENT_COMPAT_PLUCK
#undef FOLLY_LIBEVENT_DEF_ACCESSORS #undef FOLLY_LIBEVENT_DEF_ACCESSORS
......
...@@ -56,10 +56,19 @@ ScopedEventBaseThread::ScopedEventBaseThread(EventBaseManager* ebm) ...@@ -56,10 +56,19 @@ ScopedEventBaseThread::ScopedEventBaseThread(EventBaseManager* ebm)
: ScopedEventBaseThread(ebm, "") {} : ScopedEventBaseThread(ebm, "") {}
ScopedEventBaseThread::ScopedEventBaseThread( ScopedEventBaseThread::ScopedEventBaseThread(
EventBaseManager* ebm,
StringPiece name)
: ScopedEventBaseThread(
std::unique_ptr<EventBaseBackendBase>(),
ebm,
name) {}
ScopedEventBaseThread::ScopedEventBaseThread(
std::unique_ptr<EventBaseBackendBase>&& backend,
EventBaseManager* ebm, EventBaseManager* ebm,
StringPiece name) StringPiece name)
: ebm_(ebm ? ebm : EventBaseManager::get()) { : ebm_(ebm ? ebm : EventBaseManager::get()) {
new (&eb_) EventBase(); new (&eb_) EventBase(std::move(backend));
th_ = thread(run, ebm_, &eb_, &stop_, name); th_ = thread(run, ebm_, &eb_, &stop_, name);
eb_.waitUntilRunning(); eb_.waitUntilRunning();
} }
......
...@@ -42,6 +42,10 @@ class ScopedEventBaseThread : public IOExecutor, public SequencedExecutor { ...@@ -42,6 +42,10 @@ class ScopedEventBaseThread : public IOExecutor, public SequencedExecutor {
explicit ScopedEventBaseThread(StringPiece name); explicit ScopedEventBaseThread(StringPiece name);
explicit ScopedEventBaseThread(EventBaseManager* ebm); explicit ScopedEventBaseThread(EventBaseManager* ebm);
explicit ScopedEventBaseThread(EventBaseManager* ebm, StringPiece name); explicit ScopedEventBaseThread(EventBaseManager* ebm, StringPiece name);
explicit ScopedEventBaseThread(
std::unique_ptr<EventBaseBackendBase>&& backend,
EventBaseManager* ebm,
StringPiece name);
~ScopedEventBaseThread(); ~ScopedEventBaseThread();
EventBase* getEventBase() const { EventBase* getEventBase() const {
......
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <folly/Function.h>
#include <folly/io/async/EventBase.h>
namespace folly {
namespace test {
class EventBaseBackendProvider {
public:
EventBaseBackendProvider() = delete;
~EventBaseBackendProvider() = delete;
using GetBackendFunc =
folly::Function<std::unique_ptr<folly::EventBaseBackendBase>()>;
static std::unique_ptr<folly::EventBaseBackendBase> getBackend() {
CHECK(!!getBackendFunc_);
return getBackendFunc_();
}
static void setGetBackendFunc(GetBackendFunc&& func) {
getBackendFunc_ = std::move(func);
}
private:
static GetBackendFunc getBackendFunc_;
};
} // namespace test
} // 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