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