c67d2cb29edb1b6cf8452423b78e6cee5ee7a023
[folly.git] / folly / io / async / VirtualEventBase.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #pragma once
18
19 #include <future>
20
21 #include <folly/Baton.h>
22 #include <folly/Executor.h>
23 #include <folly/io/async/EventBase.h>
24
25 namespace folly {
26
27 /**
28  * VirtualEventBase implements a light-weight view onto existing EventBase.
29  *
30  * Multiple VirtualEventBases can be backed by a single EventBase. Similarly
31  * to EventBase, VirtualEventBase implements loopKeepAlive() functionality,
32  * which allows callbacks holding KeepAlive token to keep EventBase looping
33  * until they are complete.
34  *
35  * VirtualEventBase destructor blocks until all its KeepAliveTokens are released
36  * and all tasks scheduled through it are complete. EventBase destructor also
37  * blocks until all VirtualEventBases backed by it are released.
38  */
39 class VirtualEventBase : public folly::Executor, public folly::TimeoutManager {
40  public:
41   explicit VirtualEventBase(EventBase& evb);
42
43   VirtualEventBase(const VirtualEventBase&) = delete;
44   VirtualEventBase& operator=(const VirtualEventBase&) = delete;
45
46   ~VirtualEventBase();
47
48   EventBase& getEventBase() {
49     return evb_;
50   }
51
52   /**
53    * Adds the given callback to a queue of things run before destruction
54    * of current VirtualEventBase.
55    *
56    * This allows users of VirtualEventBase that run in it, but don't control it,
57    * to be notified before VirtualEventBase gets destructed.
58    *
59    * Note: this will be called from the loop of the EventBase, backing this
60    * VirtualEventBase
61    */
62   void runOnDestruction(EventBase::LoopCallback* callback);
63
64   /**
65    * @see EventBase::runInLoop
66    */
67   template <typename F>
68   void runInLoop(F&& f, bool thisIteration = false) {
69     evb_.runInLoop(std::forward<F>(f), thisIteration);
70   }
71
72   /**
73    * VirtualEventBase destructor blocks until all tasks scheduled through its
74    * runInEventBaseThread are complete.
75    *
76    * @see EventBase::runInEventBaseThread
77    */
78   template <typename F>
79   void runInEventBaseThread(F&& f) {
80     // KeepAlive token has to be released in the EventBase thread. If
81     // runInEventBaseThread() fails, we can't extract the KeepAlive token
82     // from the callback to properly release it.
83     CHECK(evb_.runInEventBaseThread([
84       keepAliveToken = getKeepAliveToken(),
85       f = std::forward<F>(f)
86     ]() mutable { f(); }));
87   }
88
89   HHWheelTimer& timer() {
90     return evb_.timer();
91   }
92
93   void attachTimeoutManager(
94       AsyncTimeout* obj,
95       TimeoutManager::InternalEnum internal) override {
96     evb_.attachTimeoutManager(obj, internal);
97   }
98
99   void detachTimeoutManager(AsyncTimeout* obj) override {
100     evb_.detachTimeoutManager(obj);
101   }
102
103   bool scheduleTimeout(AsyncTimeout* obj, TimeoutManager::timeout_type timeout)
104       override {
105     return evb_.scheduleTimeout(obj, timeout);
106   }
107
108   void cancelTimeout(AsyncTimeout* obj) override {
109     evb_.cancelTimeout(obj);
110   }
111
112   void bumpHandlingTime() override {
113     evb_.bumpHandlingTime();
114   }
115
116   bool isInTimeoutManagerThread() override {
117     return evb_.isInTimeoutManagerThread();
118   }
119
120   /**
121    * @see runInEventBaseThread
122    */
123   void add(folly::Func f) override {
124     runInEventBaseThread(std::move(f));
125   }
126
127   /**
128    * Returns you a handle which prevents VirtualEventBase from being destroyed.
129    * KeepAlive handle can be released from EventBase loop only.
130    */
131   KeepAlive getKeepAliveToken() override {
132     if (evb_.inRunningEventBaseThread()) {
133       ++loopKeepAliveCount_;
134     } else {
135       ++loopKeepAliveCountAtomic_;
136     }
137     return makeKeepAlive();
138   }
139
140   bool inRunningEventBaseThread() const {
141     return evb_.inRunningEventBaseThread();
142   }
143
144  protected:
145   void keepAliveRelease() override {
146     DCHECK(getEventBase().inRunningEventBaseThread());
147     if (loopKeepAliveCountAtomic_.load()) {
148       loopKeepAliveCount_ += loopKeepAliveCountAtomic_.exchange(0);
149     }
150     DCHECK(loopKeepAliveCount_ > 0);
151     if (--loopKeepAliveCount_ == 0) {
152       destroyImpl();
153     }
154   }
155
156  private:
157   friend class EventBase;
158
159   std::future<void> destroy();
160   void destroyImpl();
161
162   using LoopCallbackList = EventBase::LoopCallback::List;
163
164   EventBase& evb_;
165
166   ssize_t loopKeepAliveCount_{0};
167   std::atomic<ssize_t> loopKeepAliveCountAtomic_{0};
168   std::promise<void> destroyPromise_;
169   std::future<void> destroyFuture_{destroyPromise_.get_future()};
170   KeepAlive loopKeepAlive_;
171
172   KeepAlive evbLoopKeepAlive_;
173
174   folly::Synchronized<LoopCallbackList> onDestructionCallbacks_;
175 };
176 }