folly copyright 2015 -> copyright 2016
[folly.git] / folly / experimental / fibers / Baton-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/experimental/fibers/Fiber.h>
17 #include <folly/experimental/fibers/FiberManager.h>
18
19 namespace folly { namespace fibers {
20
21 inline Baton::Baton() : Baton(NO_WAITER) {
22   assert(Baton(NO_WAITER).futex_.futex == static_cast<uint32_t>(NO_WAITER));
23   assert(Baton(POSTED).futex_.futex == static_cast<uint32_t>(POSTED));
24   assert(Baton(TIMEOUT).futex_.futex == static_cast<uint32_t>(TIMEOUT));
25   assert(Baton(THREAD_WAITING).futex_.futex ==
26          static_cast<uint32_t>(THREAD_WAITING));
27
28   assert(futex_.futex.is_lock_free());
29   assert(waitingFiber_.is_lock_free());
30 }
31
32 template <typename F>
33 void Baton::wait(F&& mainContextFunc) {
34   auto fm = FiberManager::getFiberManagerUnsafe();
35   if (!fm || !fm->activeFiber_) {
36     mainContextFunc();
37     return waitThread();
38   }
39
40   return waitFiber(*fm, std::forward<F>(mainContextFunc));
41 }
42
43 template <typename F>
44 void Baton::waitFiber(FiberManager& fm, F&& mainContextFunc) {
45   auto& waitingFiber = waitingFiber_;
46   auto f = [&mainContextFunc, &waitingFiber](Fiber& fiber) mutable {
47     auto baton_fiber = waitingFiber.load();
48     do {
49       if (LIKELY(baton_fiber == NO_WAITER)) {
50         continue;
51       } else if (baton_fiber == POSTED || baton_fiber == TIMEOUT) {
52         fiber.setData(0);
53         break;
54       } else {
55         throw std::logic_error("Some Fiber is already waiting on this Baton.");
56       }
57     } while(!waitingFiber.compare_exchange_weak(
58               baton_fiber,
59               reinterpret_cast<intptr_t>(&fiber)));
60
61     mainContextFunc();
62   };
63
64   fm.awaitFunc_ = std::ref(f);
65   fm.activeFiber_->preempt(Fiber::AWAITING);
66 }
67
68 template <typename F>
69 bool Baton::timed_wait(TimeoutController::Duration timeout,
70                        F&& mainContextFunc) {
71   auto fm = FiberManager::getFiberManagerUnsafe();
72
73   if (!fm || !fm->activeFiber_) {
74     mainContextFunc();
75     return timedWaitThread(timeout);
76   }
77
78   auto& baton = *this;
79   bool canceled = false;
80   auto timeoutFunc = [&baton, &canceled]() mutable {
81     baton.postHelper(TIMEOUT);
82     canceled = true;
83   };
84
85   auto id = fm->timeoutManager_->registerTimeout(
86     std::ref(timeoutFunc), timeout);
87
88   waitFiber(*fm, std::move(mainContextFunc));
89
90   auto posted = waitingFiber_ == POSTED;
91
92   if (!canceled) {
93     fm->timeoutManager_->cancel(id);
94   }
95
96   return posted;
97 }
98
99 template<typename C, typename D>
100 bool Baton::timed_wait(const std::chrono::time_point<C,D>& timeout) {
101   auto now = C::now();
102
103   if (LIKELY(now <= timeout)) {
104     return timed_wait(
105         std::chrono::duration_cast<std::chrono::milliseconds>(timeout - now));
106   } else {
107     return timed_wait(TimeoutController::Duration(0));
108   }
109 }
110
111
112 }}