2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <folly/Memory.h>
17 #include <folly/fibers/EventBaseLoopController.h>
22 inline EventBaseLoopController::EventBaseLoopController()
23 : callback_(*this), aliveWeak_(destructionCallback_.getWeak()) {}
25 inline EventBaseLoopController::~EventBaseLoopController() {
26 callback_.cancelLoopCallback();
27 eventBaseKeepAlive_.reset();
30 inline void EventBaseLoopController::attachEventBase(EventBase& eventBase) {
31 attachEventBase(eventBase.getVirtualEventBase());
34 inline void EventBaseLoopController::attachEventBase(
35 VirtualEventBase& eventBase) {
36 if (eventBase_ != nullptr) {
37 LOG(ERROR) << "Attempt to reattach EventBase to LoopController";
40 eventBase_ = &eventBase;
41 eventBase_->runOnDestruction(&destructionCallback_);
43 eventBaseAttached_ = true;
45 if (awaitingScheduling_) {
50 inline void EventBaseLoopController::setFiberManager(FiberManager* fm) {
54 inline void EventBaseLoopController::schedule() {
55 if (eventBase_ == nullptr) {
56 // In this case we need to postpone scheduling.
57 awaitingScheduling_ = true;
59 // Schedule it to run in current iteration.
61 if (!eventBaseKeepAlive_) {
62 eventBaseKeepAlive_ = eventBase_->getKeepAliveToken();
64 eventBase_->getEventBase().runInLoop(&callback_, true);
65 awaitingScheduling_ = false;
69 inline void EventBaseLoopController::cancel() {
70 callback_.cancelLoopCallback();
73 inline void EventBaseLoopController::runLoop() {
74 if (!eventBaseKeepAlive_) {
75 // runLoop can be called twice if both schedule() and scheduleThreadSafe()
77 if (!fm_->hasTasks()) {
80 eventBaseKeepAlive_ = eventBase_->getKeepAliveToken();
83 loopRunner_->run([&] { fm_->loopUntilNoReadyImpl(); });
85 fm_->loopUntilNoReadyImpl();
87 if (!fm_->hasTasks()) {
88 eventBaseKeepAlive_.reset();
92 inline void EventBaseLoopController::scheduleThreadSafe(
93 std::function<bool()> func) {
94 /* The only way we could end up here is if
95 1) Fiber thread creates a fiber that awaits (which means we must
96 have already attached, fiber thread wouldn't be running).
97 2) We move the promise to another thread (this move is a memory fence)
98 3) We fulfill the promise from the other thread. */
99 assert(eventBaseAttached_);
101 auto alive = aliveWeak_.lock();
103 if (func() && alive) {
104 auto aliveWeak = aliveWeak_;
105 eventBase_->runInEventBaseThread([this, aliveWeak]() {
106 if (!aliveWeak.expired()) {
113 inline void EventBaseLoopController::timedSchedule(
114 std::function<void()> func,
116 assert(eventBaseAttached_);
118 // We want upper bound for the cast, thus we just add 1
120 std::chrono::duration_cast<std::chrono::milliseconds>(time - Clock::now())
123 // If clock is not monotonic
124 delay_ms = std::max<decltype(delay_ms)>(delay_ms, 0);
125 eventBase_->tryRunAfterDelay(func, uint32_t(delay_ms));
127 } // namespace fibers