Introduce destruction callbacks
authorStepan Palamarchuk <stepan@fb.com>
Thu, 5 Jun 2014 05:33:39 +0000 (22:33 -0700)
committerAnton Likhtarov <alikhtarov@fb.com>
Mon, 9 Jun 2014 22:35:58 +0000 (15:35 -0700)
Summary:
This change allows users to track lifetime of EventBase and perform clean shutdown when EventBase gets destructed.

It is useful for users that rely on EventBase lifetime, but don't have any feedback mechanism with the owner of EventBase.

For instance some part of code might remain running in background on the EventBase after the main object was destroyed (e.g. it might be finalizing some async requests). In such case the original owner doesn't know that there's something still running and may try to destroy EventBase. In that case such background code will remain zombie forever.

AsyncMcClient changes are presented just as an example of usage.

@davejwatson, @simpkins: Could you please take a look at the proposed changes for the EventBase? If this is something not worth adding into EventBase, could you recommend a better way of doing things?

Test Plan: fbmake runtests

Reviewed By: alikhtarov@fb.com

Subscribers: folly@lists, simpkins, davejwatson

FB internal diff: D1353101

folly/io/async/EventBase.cpp
folly/io/async/EventBase.h

index 130ba5f..ab34b7c 100644 (file)
@@ -178,6 +178,13 @@ EventBase::EventBase(event_base* evb)
 }
 
 EventBase::~EventBase() {
+  // Call all destruction callbacks, before we start cleaning up our state.
+  while (!onDestructionCallbacks_.empty()) {
+    LoopCallback* callback = &onDestructionCallbacks_.front();
+    onDestructionCallbacks_.pop_front();
+    callback->runLoopCallback();
+  }
+
   // Delete any unfired CobTimeout objects, so that we don't leak memory
   // (Note that we don't fire them.  The caller is responsible for cleaning up
   // its own data structures if it destroys the EventBase with unfired events
@@ -445,6 +452,12 @@ void EventBase::runInLoop(Cob&& cob, bool thisIteration) {
   }
 }
 
+void EventBase::runOnDestruction(LoopCallback* callback) {
+  DCHECK(isInEventBaseThread());
+  callback->cancelLoopCallback();
+  onDestructionCallbacks_.push_back(*callback);
+}
+
 bool EventBase::runInEventBaseThread(void (*fn)(void*), void* arg) {
   // Send the message.
   // It will be received by the FunctionRunner in the EventBase's thread.
index fdee813..660b1ab 100644 (file)
@@ -240,6 +240,18 @@ class EventBase : private boost::noncopyable, public TimeoutManager {
 
   void runInLoop(Cob&& c, bool thisIteration = false);
 
+  /**
+   * Adds the given callback to a queue of things run before destruction
+   * of current EventBase.
+   *
+   * This allows users of EventBase that run in it, but don't control it,
+   * to be notified before EventBase gets destructed.
+   *
+   * Note: will be called from the thread that invoked EventBase destructor,
+   *       before the final run of loop callbacks.
+   */
+  void runOnDestruction(LoopCallback* callback);
+
   /**
    * Run the specified function in the EventBase's thread.
    *
@@ -500,6 +512,7 @@ class EventBase : private boost::noncopyable, public TimeoutManager {
   CobTimeout::List pendingCobTimeouts_;
 
   LoopCallbackList loopCallbacks_;
+  LoopCallbackList onDestructionCallbacks_;
 
   // This will be null most of the time, but point to currentCallbacks
   // if we are in the middle of running loop callbacks, such that