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