VirtualEventBase
[folly.git] / folly / io / async / VirtualEventBase.h
1 /*
2  * Copyright 2016 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 <folly/Baton.h>
20 #include <folly/Executor.h>
21 #include <folly/io/async/EventBase.h>
22
23 namespace folly {
24
25 /**
26  * VirtualEventBase implements a light-weight view onto existing EventBase.
27  *
28  * Multiple VirtualEventBases can be backed by a single EventBase. Similarly
29  * to EventBase, VirtualEventBase implements loopKeepAlive() functionality,
30  * which allows callbacks holding LoopKeepAlive token to keep EventBase looping
31  * until they are complete.
32  *
33  * VirtualEventBase destructor blocks until all its KeepAliveTokens are released
34  * and all tasks scheduled through it are complete. EventBase destructor also
35  * blocks until all VirtualEventBases backed by it are released.
36  */
37 class VirtualEventBase : public folly::Executor, public folly::TimeoutManager {
38  public:
39   explicit VirtualEventBase(EventBase& evb);
40
41   VirtualEventBase(const VirtualEventBase&) = delete;
42   VirtualEventBase& operator=(const VirtualEventBase&) = delete;
43
44   ~VirtualEventBase();
45
46   EventBase& getEventBase() {
47     return evb_;
48   }
49
50   /**
51    * Adds the given callback to a queue of things run before destruction
52    * of current VirtualEventBase.
53    *
54    * This allows users of VirtualEventBase that run in it, but don't control it,
55    * to be notified before VirtualEventBase gets destructed.
56    *
57    * Note: this will be called from the loop of the EventBase, backing this
58    * VirtualEventBase
59    */
60   void runOnDestruction(EventBase::LoopCallback* callback);
61
62   /**
63    * @see EventBase::runInLoop
64    */
65   template <typename F>
66   void runInLoop(F&& f, bool thisIteration = false) {
67     evb_.runInLoop(std::forward<F>(f), thisIteration);
68   }
69
70   /**
71    * VirtualEventBase destructor blocks until all tasks scheduled through its
72    * runInEventBaseThread are complete.
73    *
74    * @see EventBase::runInEventBaseThread
75    */
76   template <typename F>
77   void runInEventBaseThread(F&& f) {
78     // LoopKeepAlive token has to be released in the EventBase thread. If
79     // runInEventBaseThread() fails, we can't extract the LoopKeepAlive token
80     // from the callback to properly release it.
81     CHECK(evb_.runInEventBaseThread([
82       keepAlive = loopKeepAliveAtomic(),
83       f = std::forward<F>(f)
84     ]() mutable { f(); }));
85   }
86
87   void attachTimeoutManager(
88       AsyncTimeout* obj,
89       TimeoutManager::InternalEnum internal) override {
90     evb_.attachTimeoutManager(obj, internal);
91   }
92
93   void detachTimeoutManager(AsyncTimeout* obj) override {
94     evb_.detachTimeoutManager(obj);
95   }
96
97   bool scheduleTimeout(AsyncTimeout* obj, TimeoutManager::timeout_type timeout)
98       override {
99     return evb_.scheduleTimeout(obj, timeout);
100   }
101
102   void cancelTimeout(AsyncTimeout* obj) override {
103     evb_.cancelTimeout(obj);
104   }
105
106   void bumpHandlingTime() override {
107     evb_.bumpHandlingTime();
108   }
109
110   bool isInTimeoutManagerThread() override {
111     return evb_.isInTimeoutManagerThread();
112   }
113
114   /**
115    * @see runInEventBaseThread
116    */
117   void add(folly::Func f) override {
118     runInEventBaseThread(std::move(f));
119   }
120
121   struct LoopKeepAliveDeleter {
122     void operator()(VirtualEventBase* evb) {
123       DCHECK(evb->getEventBase().inRunningEventBaseThread());
124       if (evb->loopKeepAliveCountAtomic_.load()) {
125         evb->loopKeepAliveCount_ += evb->loopKeepAliveCountAtomic_.exchange(0);
126       }
127       DCHECK(evb->loopKeepAliveCount_ > 0);
128       if (--evb->loopKeepAliveCount_ == 0) {
129         evb->loopKeepAliveBaton_.post();
130       }
131     }
132   };
133   using LoopKeepAlive = std::unique_ptr<VirtualEventBase, LoopKeepAliveDeleter>;
134
135   /**
136    * Returns you a handle which prevents VirtualEventBase from being destroyed.
137    * LoopKeepAlive handle can be released from EventBase loop only.
138    *
139    * loopKeepAlive() can be called from EventBase thread only.
140    */
141   LoopKeepAlive loopKeepAlive() {
142     DCHECK(evb_.isInEventBaseThread());
143     ++loopKeepAliveCount_;
144     return LoopKeepAlive(this);
145   }
146
147   /**
148    * Thread-safe version of loopKeepAlive()
149    */
150   LoopKeepAlive loopKeepAliveAtomic() {
151     if (evb_.inRunningEventBaseThread()) {
152       return loopKeepAlive();
153     }
154     ++loopKeepAliveCountAtomic_;
155     return LoopKeepAlive(this);
156   }
157
158  private:
159   using LoopCallbackList = EventBase::LoopCallback::List;
160
161   EventBase& evb_;
162
163   ssize_t loopKeepAliveCount_{0};
164   std::atomic<ssize_t> loopKeepAliveCountAtomic_{0};
165   folly::Baton<> loopKeepAliveBaton_;
166   LoopKeepAlive loopKeepAlive_;
167
168   EventBase::LoopKeepAlive evbLoopKeepAlive_;
169
170   folly::Synchronized<LoopCallbackList> onDestructionCallbacks_;
171 };
172 }