From: Stepan Palamarchuk Date: Thu, 5 Jun 2014 05:33:39 +0000 (-0700) Subject: Introduce destruction callbacks X-Git-Tag: v0.22.0~517 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=d816ed8879e9748b1c17de98617b35d5060ec8b0 Introduce destruction callbacks 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 --- diff --git a/folly/io/async/EventBase.cpp b/folly/io/async/EventBase.cpp index 130ba5fb..ab34b7c3 100644 --- a/folly/io/async/EventBase.cpp +++ b/folly/io/async/EventBase.cpp @@ -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. diff --git a/folly/io/async/EventBase.h b/folly/io/async/EventBase.h index fdee8134..660b1abd 100644 --- a/folly/io/async/EventBase.h +++ b/folly/io/async/EventBase.h @@ -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