025341ce900be77529b63e87fb560b8c1870fa73
[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 <typename EventBaseT>
56 inline void EventBaseLoopControllerT<EventBaseT>::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 <typename EventBaseT>
68 inline void EventBaseLoopControllerT<EventBaseT>::cancel() {
69   callback_.cancelLoopCallback();
70 }
71
72 template <typename EventBaseT>
73 inline void EventBaseLoopControllerT<EventBaseT>::runLoop() {
74   if (!eventBaseKeepAlive_) {
75     eventBaseKeepAlive_ = eventBase_->getKeepAliveToken();
76   }
77   if (loopRunner_) {
78     loopRunner_->run([&] { fm_->loopUntilNoReadyImpl(); });
79   } else {
80     fm_->loopUntilNoReadyImpl();
81   }
82   if (!fm_->hasTasks()) {
83     eventBaseKeepAlive_.reset();
84   }
85 }
86
87 template <typename EventBaseT>
88 inline void EventBaseLoopControllerT<EventBaseT>::scheduleThreadSafe(
89     std::function<bool()> func) {
90   /* The only way we could end up here is if
91      1) Fiber thread creates a fiber that awaits (which means we must
92         have already attached, fiber thread wouldn't be running).
93      2) We move the promise to another thread (this move is a memory fence)
94      3) We fulfill the promise from the other thread. */
95   assert(eventBaseAttached_);
96
97   auto alive = aliveWeak_.lock();
98
99   if (func() && alive) {
100     auto aliveWeak = aliveWeak_;
101     eventBase_->runInEventBaseThread([this, aliveWeak]() {
102       if (!aliveWeak.expired()) {
103         runLoop();
104       }
105     });
106   }
107 }
108
109 template <typename EventBaseT>
110 inline void EventBaseLoopControllerT<EventBaseT>::timedSchedule(
111     std::function<void()> func,
112     TimePoint time) {
113   assert(eventBaseAttached_);
114
115   // We want upper bound for the cast, thus we just add 1
116   auto delay_ms =
117       std::chrono::duration_cast<std::chrono::milliseconds>(time - Clock::now())
118           .count() +
119       1;
120   // If clock is not monotonic
121   delay_ms = std::max<decltype(delay_ms)>(delay_ms, 0);
122   eventBase_->tryRunAfterDelay(func, uint32_t(delay_ms));
123 }
124 }
125 } // folly::fibers