Remove runInLoop
[folly.git] / folly / fibers / EventBaseLoopController-inl.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 #include <folly/Memory.h>
17 #include <folly/fibers/EventBaseLoopController.h>
18
19 namespace folly {
20 namespace fibers {
21
22 template <typename EventBaseT>
23 inline EventBaseLoopControllerT<EventBaseT>::EventBaseLoopControllerT()
24     : callback_(*this), aliveWeak_(destructionCallback_.getWeak()) {}
25
26 template <typename EventBaseT>
27 inline EventBaseLoopControllerT<EventBaseT>::~EventBaseLoopControllerT() {
28   callback_.cancelLoopCallback();
29   eventBaseKeepAlive_.reset();
30 }
31
32 template <typename EventBaseT>
33 inline void EventBaseLoopControllerT<EventBaseT>::attachEventBase(
34     EventBaseT& eventBase) {
35   if (eventBase_ != nullptr) {
36     LOG(ERROR) << "Attempt to reattach EventBase to LoopController";
37   }
38
39   eventBase_ = &eventBase;
40   eventBase_->runOnDestruction(&destructionCallback_);
41
42   eventBaseAttached_ = true;
43
44   if (awaitingScheduling_) {
45     schedule();
46   }
47 }
48
49 template <typename EventBaseT>
50 inline void EventBaseLoopControllerT<EventBaseT>::setFiberManager(
51     FiberManager* fm) {
52   fm_ = fm;
53 }
54
55 template <>
56 inline void EventBaseLoopControllerT<folly::EventBase>::schedule() {
57   if (eventBase_ == nullptr) {
58     // In this case we need to postpone scheduling.
59     awaitingScheduling_ = true;
60   } else {
61     // Schedule it to run in current iteration.
62     eventBase_->runInLoop(&callback_, true);
63     awaitingScheduling_ = false;
64   }
65 }
66
67 template <>
68 inline void EventBaseLoopControllerT<folly::VirtualEventBase>::schedule() {
69   if (eventBase_ == nullptr) {
70     // In this case we need to postpone scheduling.
71     awaitingScheduling_ = true;
72   } else {
73     // Schedule it to run in current iteration.
74
75     if (!eventBaseKeepAlive_) {
76       eventBaseKeepAlive_ = eventBase_->getKeepAliveToken();
77     }
78     eventBase_->getEventBase().runInLoop(&callback_, true);
79     awaitingScheduling_ = false;
80   }
81 }
82
83 template <typename EventBaseT>
84 inline void EventBaseLoopControllerT<EventBaseT>::cancel() {
85   callback_.cancelLoopCallback();
86 }
87
88 template <typename EventBaseT>
89 inline void EventBaseLoopControllerT<EventBaseT>::runLoop() {
90   if (!eventBaseKeepAlive_) {
91     // runLoop can be called twice if both schedule() and scheduleThreadSafe()
92     // were called.
93     if (!fm_->hasTasks()) {
94       return;
95     }
96     eventBaseKeepAlive_ = eventBase_->getKeepAliveToken();
97   }
98   if (loopRunner_) {
99     loopRunner_->run([&] { fm_->loopUntilNoReadyImpl(); });
100   } else {
101     fm_->loopUntilNoReadyImpl();
102   }
103   if (!fm_->hasTasks()) {
104     eventBaseKeepAlive_.reset();
105   }
106 }
107
108 template <typename EventBaseT>
109 inline void EventBaseLoopControllerT<EventBaseT>::scheduleThreadSafe(
110     std::function<bool()> func) {
111   /* The only way we could end up here is if
112      1) Fiber thread creates a fiber that awaits (which means we must
113         have already attached, fiber thread wouldn't be running).
114      2) We move the promise to another thread (this move is a memory fence)
115      3) We fulfill the promise from the other thread. */
116   assert(eventBaseAttached_);
117
118   auto alive = aliveWeak_.lock();
119
120   if (func() && alive) {
121     auto aliveWeak = aliveWeak_;
122     eventBase_->runInEventBaseThread([this, aliveWeak]() {
123       if (!aliveWeak.expired()) {
124         runLoop();
125       }
126     });
127   }
128 }
129
130 template <typename EventBaseT>
131 inline void EventBaseLoopControllerT<EventBaseT>::timedSchedule(
132     std::function<void()> func,
133     TimePoint time) {
134   assert(eventBaseAttached_);
135
136   // We want upper bound for the cast, thus we just add 1
137   auto delay_ms =
138       std::chrono::duration_cast<std::chrono::milliseconds>(time - Clock::now())
139           .count() +
140       1;
141   // If clock is not monotonic
142   delay_ms = std::max<decltype(delay_ms)>(delay_ms, 0);
143   eventBase_->tryRunAfterDelay(func, uint32_t(delay_ms));
144 }
145 }
146 } // folly::fibers