move TEventBaseManager to folly/io/async/EventBaseManager
authorJames Sedgwick <jsedgwick@fb.com>
Thu, 9 Oct 2014 00:15:17 +0000 (17:15 -0700)
committerAndrii Grynenko <andrii@fb.com>
Wed, 15 Oct 2014 00:56:16 +0000 (17:56 -0700)
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
folly/io/async/EventBase.cpp
folly/io/async/EventBase.h
folly/io/async/EventBaseManager.cpp [new file with mode: 0644]
folly/io/async/EventBaseManager.h [new file with mode: 0644]

index 3da17a77cc06b6a9390c5fbc62e37c78f67d301c..9c122e5e7a4f6e3d169ea6b4fe52594f863d4e42 100644 (file)
@@ -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 \
index cae583fee18b152d3985e39cd4d56a6461e0ac0f..17cdd5c181b3862829556e2b987bbdaabcc95b6f 100644 (file)
@@ -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
index 2406f02b285ee6907a9ce45fd3097556b4c94c08..4930478f0aec113ee7aa09a601a11229715e78c0 100644 (file)
@@ -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 (file)
index 0000000..4f465f2
--- /dev/null
@@ -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 <folly/io/async/EventBaseManager.h>
+
+namespace folly {
+
+std::atomic<EventBaseManager*> 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<EventBaseManager *>(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 (file)
index 0000000..1a0b0b1
--- /dev/null
@@ -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 <folly/ThreadLocal.h>
+#include <folly/io/async/EventBase.h>
+#include <set>
+#include <list>
+
+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<EventBaseObserver>& 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<typename FunctionType>
+  void withEventBaseSet(const FunctionType& runnable) {
+    // grab the mutex for the caller
+    std::lock_guard<std::mutex> g(*&eventBaseSetMutex_);
+    // give them only a const set to work with
+    const std::set<EventBase *>& 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<std::mutex> g(*&eventBaseSetMutex_);
+    eventBaseSet_.insert(evb);
+  }
+
+  void untrackEventBase(EventBase *evb) {
+    std::lock_guard<std::mutex> g(*&eventBaseSetMutex_);
+    eventBaseSet_.erase(evb);
+  }
+
+  mutable folly::ThreadLocalPtr<EventBaseInfo> localStore_;
+
+  // set of "active" EventBase instances
+  // (also see the mutex "eventBaseSetMutex_" below
+  // which governs access to this).
+  mutable std::set<EventBase *> eventBaseSet_;
+
+  // a mutex to use as a guard for the above set
+  std::mutex eventBaseSetMutex_;
+
+  std::shared_ptr<folly::EventBaseObserver> observer_;
+};
+
+} // folly