From bdde233ce168266511ce333de7e092a6240a528b Mon Sep 17 00:00:00 2001 From: James Sedgwick Date: Wed, 8 Oct 2014 17:15:17 -0700 Subject: [PATCH] move TEventBaseManager to folly/io/async/EventBaseManager Summary: This class isn't thrift specific anymore, especially now that TEventBase->EventBase. Specific use case in folly: folly/experimental/wangle/concurrent/IOThreadPoolExecutor EventBaseManager itself needs some work/cleanup, but that is for a later diff For instance, we might try to push towards only allowing access to the singleton, and towards removing overrides. i.e. only getEventBase. But that's pending an audit of how people are using it now. Note that the ProfiledMutex protecting the event base set has been made a regular std::mutex Test Plan: compiled thrift/lib/cpp|cpp2, made a pass at fixing forward declarations elsewhere will let contbuild help me iterate Reviewed By: hans@fb.com Subscribers: trunkagent, nli, fbcode-common-diffs@, davejwatson, hero-diffs@, zeus-diffs@, andrewcox, netego-diffs@, alandau, apollo-diffs@, antonl, laser-diffs@, ads-dsp-eng@, darshan, micha, njormrod, panin, hdoshi, scuba-diffs@, bmatheny FB internal diff: D1590827 Tasks: 5247981 --- folly/Makefile.am | 2 + folly/io/async/EventBase.cpp | 3 + folly/io/async/EventBase.h | 4 +- folly/io/async/EventBaseManager.cpp | 90 ++++++++++++++++ folly/io/async/EventBaseManager.h | 160 ++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 folly/io/async/EventBaseManager.cpp create mode 100644 folly/io/async/EventBaseManager.h diff --git a/folly/Makefile.am b/folly/Makefile.am index 3da17a77..9c122e5e 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -116,6 +116,7 @@ nobase_follyinclude_HEADERS = \ io/async/AsyncTimeout.h \ io/async/DelayedDestruction.h \ io/async/EventBase.h \ + io/async/EventBaseManager.h \ io/async/EventFDWrapper.h \ io/async/EventHandler.h \ io/async/EventUtil.h \ @@ -239,6 +240,7 @@ libfolly_la_SOURCES = \ io/RecordIO.cpp \ io/async/AsyncTimeout.cpp \ io/async/EventBase.cpp \ + io/async/EventBaseManager.cpp \ io/async/EventHandler.cpp \ io/async/Request.cpp \ io/async/HHWheelTimer.cpp \ diff --git a/folly/io/async/EventBase.cpp b/folly/io/async/EventBase.cpp index cae583fe..17cdd5c1 100644 --- a/folly/io/async/EventBase.cpp +++ b/folly/io/async/EventBase.cpp @@ -704,4 +704,7 @@ const std::string& EventBase::getName() { return name_; } +const char* EventBase::getLibeventVersion() { return event_get_version(); } +const char* EventBase::getLibeventMethod() { return event_get_method(); } + } // folly diff --git a/folly/io/async/EventBase.h b/folly/io/async/EventBase.h index 2406f02b..4930478f 100644 --- a/folly/io/async/EventBase.h +++ b/folly/io/async/EventBase.h @@ -385,8 +385,8 @@ class EventBase : private boost::noncopyable, public TimeoutManager { // guaranteed to always be present if we ever provide alternative EventBase // implementations that do not use libevent internally. event_base* getLibeventBase() const { return evb_; } - static const char* getLibeventVersion() { return event_get_version(); } - static const char* getLibeventMethod() { return event_get_method(); } + static const char* getLibeventVersion(); + static const char* getLibeventMethod(); /** * only EventHandler/AsyncTimeout subclasses and ourselves should diff --git a/folly/io/async/EventBaseManager.cpp b/folly/io/async/EventBaseManager.cpp new file mode 100644 index 00000000..4f465f2d --- /dev/null +++ b/folly/io/async/EventBaseManager.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2014 Facebook, Inc. + * + * 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 + +namespace folly { + +std::atomic globalManager(nullptr); + +EventBaseManager* EventBaseManager::get() { + EventBaseManager* mgr = globalManager; + if (mgr) { + return mgr; + } + + EventBaseManager* new_mgr = new EventBaseManager; + bool exchanged = globalManager.compare_exchange_strong(mgr, new_mgr); + if (!exchanged) { + delete new_mgr; + return mgr; + } else { + return new_mgr; + } + +} + +/* + * EventBaseManager methods + */ + +void EventBaseManager::setEventBase(EventBase *eventBase, + bool takeOwnership) { + EventBaseInfo *info = localStore_.get(); + if (info != nullptr) { + throw std::runtime_error("EventBaseManager: cannot set a new EventBase " + "for this thread when one already exists"); + } + + info = new EventBaseInfo(eventBase, takeOwnership); + localStore_.reset(info); + this->trackEventBase(eventBase); +} + +void EventBaseManager::clearEventBase() { + EventBaseInfo *info = localStore_.get(); + if (info != nullptr) { + this->untrackEventBase(info->eventBase); + this->localStore_.reset(nullptr); + } +} + +// XXX should this really be "const"? +EventBase * EventBaseManager::getEventBase() const { + // have one? + auto *info = localStore_.get(); + if (! info) { + info = new EventBaseInfo(); + localStore_.reset(info); + + if (observer_) { + info->eventBase->setObserver(observer_); + } + + // start tracking the event base + // XXX + // note: ugly cast because this does something mutable + // even though this method is defined as "const". + // Simply removing the const causes trouble all over fbcode; + // lots of services build a const EventBaseManager and errors + // abound when we make this non-const. + (const_cast(this))->trackEventBase(info->eventBase); + } + + return info->eventBase; +} + +} // folly diff --git a/folly/io/async/EventBaseManager.h b/folly/io/async/EventBaseManager.h new file mode 100644 index 00000000..1a0b0b1e --- /dev/null +++ b/folly/io/async/EventBaseManager.h @@ -0,0 +1,160 @@ +/* + * Copyright 2014 Facebook, Inc. + * + * 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 +#include +#include +#include + +namespace folly { + +/** + * Manager for per-thread EventBase objects. + * This class will find or create a EventBase for the current + * thread, associated with thread-specific storage for that thread. + * Although a typical application will generally only have one + * EventBaseManager, there is no restriction on multiple instances; + * the EventBases belong to one instance are isolated from those of + * another. + */ +class EventBaseManager { + public: + // XXX Constructing a EventBaseManager directly is DEPRECATED and not + // encouraged. You should instead use the global singleton if possible. + EventBaseManager() { + } + + ~EventBaseManager() { + } + + explicit EventBaseManager( + const std::shared_ptr& observer + ) : observer_(observer) {} + + /** + * Get the global EventBaseManager for this program. Ideally all users + * of EventBaseManager go through this interface and do not construct + * EventBaseManager directly. + */ + static EventBaseManager* get(); + + /** + * Get the EventBase for this thread, or create one if none exists yet. + * + * If no EventBase exists for this thread yet, a new one will be created and + * returned. May throw std::bad_alloc if allocation fails. + */ + EventBase* getEventBase() const; + + /** + * Get the EventBase for this thread. + * + * Returns nullptr if no EventBase has been created for this thread yet. + */ + EventBase* getExistingEventBase() const { + EventBaseInfo* info = localStore_.get(); + if (info == nullptr) { + return nullptr; + } + return info->eventBase; + } + + /** + * Set the EventBase to be used by this thread. + * + * This may only be called if no EventBase has been defined for this thread + * yet. If a EventBase is already defined for this thread, a + * std::runtime_error is thrown. std::bad_alloc may also be thrown if + * allocation fails while setting the EventBase. + * + * This should typically be invoked by the code that will call loop() on the + * EventBase, to make sure the EventBaseManager points to the correct + * EventBase that is actually running in this thread. + */ + void setEventBase(EventBase *eventBase, bool takeOwnership); + + /** + * Clear the EventBase for this thread. + * + * This can be used if the code driving the EventBase loop() has finished + * the loop and new events should no longer be added to the EventBase. + */ + void clearEventBase(); + + /** + * Gives the caller all references to all assigned EventBase instances at + * this moment in time. Locks a mutex so that these EventBase set cannot + * be changed, and also the caller can rely on no instances being destructed. + */ + template + void withEventBaseSet(const FunctionType& runnable) { + // grab the mutex for the caller + std::lock_guard g(*&eventBaseSetMutex_); + // give them only a const set to work with + const std::set& constSet = eventBaseSet_; + runnable(constSet); + } + + + private: + struct EventBaseInfo { + EventBaseInfo(EventBase *evb, bool owned) + : eventBase(evb), + owned_(owned) {} + + EventBaseInfo() + : eventBase(new EventBase) + , owned_(true) {} + + EventBase *eventBase; + bool owned_; + ~EventBaseInfo() { + if (owned_) { + delete eventBase; + } + } + }; + + // Forbidden copy constructor and assignment opererator + EventBaseManager(EventBaseManager const &); + EventBaseManager& operator=(EventBaseManager const &); + + void trackEventBase(EventBase *evb) { + std::lock_guard g(*&eventBaseSetMutex_); + eventBaseSet_.insert(evb); + } + + void untrackEventBase(EventBase *evb) { + std::lock_guard g(*&eventBaseSetMutex_); + eventBaseSet_.erase(evb); + } + + mutable folly::ThreadLocalPtr localStore_; + + // set of "active" EventBase instances + // (also see the mutex "eventBaseSetMutex_" below + // which governs access to this). + mutable std::set eventBaseSet_; + + // a mutex to use as a guard for the above set + std::mutex eventBaseSetMutex_; + + std::shared_ptr observer_; +}; + +} // folly -- 2.34.1