(folly) have Try::moveFromTry take an lvalue
[folly.git] / folly / experimental / fibers / Baton.cpp
1 /*
2  * Copyright 2015 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 "Baton.h"
17
18 #include <folly/detail/MemoryIdler.h>
19
20 namespace folly { namespace fibers {
21
22 void Baton::wait() {
23   wait([](){});
24 }
25
26 bool Baton::timed_wait(TimeoutController::Duration timeout) {
27   return timed_wait(timeout, [](){});
28 }
29
30 void Baton::waitThread() {
31   if (spinWaitForEarlyPost()) {
32     assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
33     return;
34   }
35
36   auto fiber = waitingFiber_.load();
37
38   if (LIKELY(fiber == NO_WAITER &&
39              waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
40     do {
41       folly::detail::MemoryIdler::futexWait(futex_.futex, THREAD_WAITING);
42       fiber = waitingFiber_.load(std::memory_order_relaxed);
43     } while (fiber == THREAD_WAITING);
44   }
45
46   if (LIKELY(fiber == POSTED)) {
47     return;
48   }
49
50   // Handle errors
51   if (fiber == TIMEOUT) {
52     throw std::logic_error("Thread baton can't have timeout status");
53   }
54   if (fiber == THREAD_WAITING) {
55     throw std::logic_error("Other thread is already waiting on this baton");
56   }
57   throw std::logic_error("Other fiber is already waiting on this baton");
58 }
59
60 bool Baton::spinWaitForEarlyPost() {
61   static_assert(PreBlockAttempts > 0,
62       "isn't this assert clearer than an uninitialized variable warning?");
63   for (int i = 0; i < PreBlockAttempts; ++i) {
64     if (try_wait()) {
65       // hooray!
66       return true;
67     }
68     // The pause instruction is the polite way to spin, but it doesn't
69     // actually affect correctness to omit it if we don't have it.
70     // Pausing donates the full capabilities of the current core to
71     // its other hyperthreads for a dozen cycles or so
72     asm_volatile_pause();
73   }
74
75   return false;
76 }
77
78 bool Baton::timedWaitThread(TimeoutController::Duration timeout) {
79   if (spinWaitForEarlyPost()) {
80     assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
81     return true;
82   }
83
84   auto fiber = waitingFiber_.load();
85
86   if (LIKELY(fiber == NO_WAITER &&
87              waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
88     auto deadline = TimeoutController::Clock::now() + timeout;
89     do {
90       const auto wait_rv =
91         futex_.futex.futexWaitUntil(THREAD_WAITING, deadline);
92       if (wait_rv == folly::detail::FutexResult::TIMEDOUT) {
93         return false;
94       }
95       fiber = waitingFiber_.load(std::memory_order_relaxed);
96     } while (fiber == THREAD_WAITING);
97   }
98
99   if (LIKELY(fiber == POSTED)) {
100     return true;
101   }
102
103   // Handle errors
104   if (fiber == TIMEOUT) {
105     throw std::logic_error("Thread baton can't have timeout status");
106   }
107   if (fiber == THREAD_WAITING) {
108     throw std::logic_error("Other thread is already waiting on this baton");
109   }
110   throw std::logic_error("Other fiber is already waiting on this baton");
111 }
112
113 void Baton::post() {
114   postHelper(POSTED);
115 }
116
117 void Baton::postHelper(intptr_t new_value) {
118   auto fiber = waitingFiber_.load();
119
120   do {
121     if (fiber == THREAD_WAITING) {
122       assert(new_value == POSTED);
123
124       return postThread();
125     }
126
127     if (fiber == POSTED || fiber == TIMEOUT) {
128       return;
129     }
130   } while (!waitingFiber_.compare_exchange_weak(fiber, new_value));
131
132   if (fiber != NO_WAITER) {
133     reinterpret_cast<Fiber*>(fiber)->setData(0);
134   }
135 }
136
137 bool Baton::try_wait() {
138   auto state = waitingFiber_.load();
139   return state == POSTED;
140 }
141
142 void Baton::postThread() {
143   auto expected = THREAD_WAITING;
144
145   if (!waitingFiber_.compare_exchange_strong(expected, POSTED)) {
146     return;
147   }
148
149   futex_.futex.futexWake(1);
150 }
151
152 void Baton::reset() {
153   waitingFiber_.store(NO_WAITER, std::memory_order_relaxed);;
154 }
155
156 }}