logging: rename the `DEBUG` log level to `DBG`
[folly.git] / folly / synchronization / Baton.h
1 /*
2  * Copyright 2014-present 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
17 #pragma once
18
19 #include <assert.h>
20 #include <errno.h>
21 #include <stdint.h>
22 #include <atomic>
23 #include <thread>
24
25 #include <folly/Likely.h>
26 #include <folly/detail/Futex.h>
27 #include <folly/detail/MemoryIdler.h>
28 #include <folly/portability/Asm.h>
29
30 namespace folly {
31
32 /// A Baton allows a thread to block once and be awoken. Captures a
33 /// single handoff, and during its lifecycle (from construction/reset
34 /// to destruction/reset) a baton must either be post()ed and wait()ed
35 /// exactly once each, or not at all.
36 ///
37 /// Baton includes no internal padding, and is only 4 bytes in size.
38 /// Any alignment or padding to avoid false sharing is up to the user.
39 ///
40 /// This is basically a stripped-down semaphore that supports only a
41 /// single call to sem_post and a single call to sem_wait.
42 ///
43 /// The non-blocking version (MayBlock == false) provides more speed
44 /// by using only load acquire and store release operations in the
45 /// critical path, at the cost of disallowing blocking and timing out.
46 ///
47 /// The current posix semaphore sem_t isn't too bad, but this provides
48 /// more a bit more speed, inlining, smaller size, a guarantee that
49 /// the implementation won't change, and compatibility with
50 /// DeterministicSchedule.  By having a much more restrictive
51 /// lifecycle we can also add a bunch of assertions that can help to
52 /// catch race conditions ahead of time.
53 template <bool MayBlock = true, template <typename> class Atom = std::atomic>
54 struct Baton {
55   constexpr Baton() noexcept : state_(INIT) {}
56
57   Baton(Baton const&) = delete;
58   Baton& operator=(Baton const&) = delete;
59
60   /// It is an error to destroy a Baton on which a thread is currently
61   /// wait()ing.  In practice this means that the waiter usually takes
62   /// responsibility for destroying the Baton.
63   ~Baton() noexcept {
64     // The docblock for this function says that it can't be called when
65     // there is a concurrent waiter.  We assume a strong version of this
66     // requirement in which the caller must _know_ that this is true, they
67     // are not allowed to be merely lucky.  If two threads are involved,
68     // the destroying thread must actually have synchronized with the
69     // waiting thread after wait() returned.  To convey causality the the
70     // waiting thread must have used release semantics and the destroying
71     // thread must have used acquire semantics for that communication,
72     // so we are guaranteed to see the post-wait() value of state_,
73     // which cannot be WAITING.
74     //
75     // Note that since we only care about a single memory location,
76     // the only two plausible memory orders here are relaxed and seq_cst.
77     assert(state_.load(std::memory_order_relaxed) != WAITING);
78   }
79
80   FOLLY_ALWAYS_INLINE bool ready() const noexcept {
81     auto s = state_.load(std::memory_order_acquire);
82     assert(s == INIT || s == EARLY_DELIVERY);
83     return LIKELY(s == EARLY_DELIVERY);
84   }
85
86   /// Equivalent to destroying the Baton and creating a new one.  It is
87   /// a bug to call this while there is a waiting thread, so in practice
88   /// the waiter will be the one that resets the baton.
89   void reset() noexcept {
90     // See ~Baton for a discussion about why relaxed is okay here
91     assert(state_.load(std::memory_order_relaxed) != WAITING);
92
93     // We use a similar argument to justify the use of a relaxed store
94     // here.  Since both wait() and post() are required to be called
95     // only once per lifetime, no thread can actually call those methods
96     // correctly after a reset() unless it synchronizes with the thread
97     // that performed the reset().  If a post() or wait() on another thread
98     // didn't synchronize, then regardless of what operation we performed
99     // here there would be a race on proper use of the Baton's spec
100     // (although not on any particular load and store).  Put another way,
101     // we don't need to synchronize here because anybody that might rely
102     // on such synchronization is required by the baton rules to perform
103     // an additional synchronization that has the desired effect anyway.
104     //
105     // There is actually a similar argument to be made about the
106     // constructor, in which the fenceless constructor initialization
107     // of state_ is piggybacked on whatever synchronization mechanism
108     // distributes knowledge of the Baton's existence
109     state_.store(INIT, std::memory_order_relaxed);
110   }
111
112   /// Causes wait() to wake up.  For each lifetime of a Baton (where a
113   /// lifetime starts at construction or reset() and ends at
114   /// destruction or reset()) there can be at most one call to post(),
115   /// in the single poster version.  Any thread may call post().
116   void post() noexcept {
117     if (!MayBlock) {
118       /// Spin-only version
119       ///
120       assert([&] {
121         auto state = state_.load(std::memory_order_relaxed);
122         return (state == INIT || state == EARLY_DELIVERY);
123       }());
124       state_.store(EARLY_DELIVERY, std::memory_order_release);
125       return;
126     }
127
128     /// May-block versions
129     ///
130     uint32_t before = state_.load(std::memory_order_acquire);
131
132     assert(before == INIT || before == WAITING || before == TIMED_OUT);
133
134     if (before == INIT &&
135         state_.compare_exchange_strong(
136             before,
137             EARLY_DELIVERY,
138             std::memory_order_release,
139             std::memory_order_relaxed)) {
140       return;
141     }
142
143     assert(before == WAITING || before == TIMED_OUT);
144
145     if (before == TIMED_OUT) {
146       return;
147     }
148
149     assert(before == WAITING);
150     state_.store(LATE_DELIVERY, std::memory_order_release);
151     state_.futexWake(1);
152   }
153
154   /// Waits until post() has been called in the current Baton lifetime.
155   /// May be called at most once during a Baton lifetime (construction
156   /// |reset until destruction|reset).  If post is called before wait in
157   /// the current lifetime then this method returns immediately.
158   ///
159   /// The restriction that there can be at most one wait() per lifetime
160   /// could be relaxed somewhat without any perf or size regressions,
161   /// but by making this condition very restrictive we can provide better
162   /// checking in debug builds.
163   FOLLY_ALWAYS_INLINE void wait() noexcept {
164     if (try_wait()) {
165       return;
166     }
167
168     waitSlow();
169   }
170
171   /// Similar to wait, but doesn't block the thread if it hasn't been posted.
172   ///
173   /// try_wait has the following semantics:
174   /// - It is ok to call try_wait any number times on the same baton until
175   ///   try_wait reports that the baton has been posted.
176   /// - It is ok to call timed_wait or wait on the same baton if try_wait
177   ///   reports that baton hasn't been posted.
178   /// - If try_wait indicates that the baton has been posted, it is invalid to
179   ///   call wait, try_wait or timed_wait on the same baton without resetting
180   ///
181   /// @return       true if baton has been posted, false othewise
182   FOLLY_ALWAYS_INLINE bool try_wait() const noexcept {
183     return ready();
184   }
185
186   /// Similar to wait, but with a timeout. The thread is unblocked if the
187   /// timeout expires.
188   /// Note: Only a single call to wait/try_wait_for/try_wait_until is allowed
189   /// during a baton's life-cycle (from ctor/reset to dtor/reset). In other
190   /// words, after try_wait_for the caller can't invoke
191   /// wait/try_wait/try_wait_for/try_wait_until
192   /// again on the same baton without resetting it.
193   ///
194   /// @param  timeout       Time until which the thread can block
195   /// @return               true if the baton was posted to before timeout,
196   ///                       false otherwise
197   template <typename Rep, typename Period>
198   FOLLY_ALWAYS_INLINE bool try_wait_for(
199       const std::chrono::duration<Rep, Period>& timeout) noexcept {
200     static_assert(
201         MayBlock, "Non-blocking Baton does not support try_wait_for.");
202
203     if (try_wait()) {
204       return true;
205     }
206
207     auto deadline = std::chrono::steady_clock::now() + timeout;
208     return tryWaitUntilSlow(deadline);
209   }
210
211   /// Similar to wait, but with a deadline. The thread is unblocked if the
212   /// deadline expires.
213   /// Note: Only a single call to wait/try_wait_for/try_wait_until is allowed
214   /// during a baton's life-cycle (from ctor/reset to dtor/reset). In other
215   /// words, after try_wait_until the caller can't invoke
216   /// wait/try_wait/try_wait_for/try_wait_until
217   /// again on the same baton without resetting it.
218   ///
219   /// @param  deadline      Time until which the thread can block
220   /// @return               true if the baton was posted to before deadline,
221   ///                       false otherwise
222   template <typename Clock, typename Duration>
223   FOLLY_ALWAYS_INLINE bool try_wait_until(
224       const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
225     static_assert(
226         MayBlock, "Non-blocking Baton does not support try_wait_until.");
227
228     if (try_wait()) {
229       return true;
230     }
231
232     return tryWaitUntilSlow(deadline);
233   }
234
235   /// Alias to try_wait_for. Deprecated.
236   template <typename Rep, typename Period>
237   FOLLY_ALWAYS_INLINE bool timed_wait(
238       const std::chrono::duration<Rep, Period>& timeout) noexcept {
239     return try_wait_for(timeout);
240   }
241
242   /// Alias to try_wait_until. Deprecated.
243   template <typename Clock, typename Duration>
244   FOLLY_ALWAYS_INLINE bool timed_wait(
245       const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
246     return try_wait_until(deadline);
247   }
248
249  private:
250   enum State : uint32_t {
251     INIT = 0,
252     EARLY_DELIVERY = 1,
253     WAITING = 2,
254     LATE_DELIVERY = 3,
255     TIMED_OUT = 4,
256   };
257
258   enum {
259     // Must be positive.  If multiple threads are actively using a
260     // higher-level data structure that uses batons internally, it is
261     // likely that the post() and wait() calls happen almost at the same
262     // time.  In this state, we lose big 50% of the time if the wait goes
263     // to sleep immediately.  On circa-2013 devbox hardware it costs about
264     // 7 usec to FUTEX_WAIT and then be awoken (half the t/iter as the
265     // posix_sem_pingpong test in BatonTests).  We can improve our chances
266     // of EARLY_DELIVERY by spinning for a bit, although we have to balance
267     // this against the loss if we end up sleeping any way.  Spins on this
268     // hw take about 7 nanos (all but 0.5 nanos is the pause instruction).
269     // We give ourself 300 spins, which is about 2 usec of waiting.  As a
270     // partial consolation, since we are using the pause instruction we
271     // are giving a speed boost to the colocated hyperthread.
272     PreBlockAttempts = 300,
273   };
274
275   // Spin for "some time" (see discussion on PreBlockAttempts) waiting
276   // for a post.
277   //
278   // @return       true if we received an early delivery during the wait,
279   //               false otherwise. If the function returns true then
280   //               state_ is guaranteed to be EARLY_DELIVERY
281   bool spinWaitForEarlyDelivery() noexcept {
282     static_assert(
283         PreBlockAttempts > 0,
284         "isn't this assert clearer than an uninitialized variable warning?");
285     for (int i = 0; i < PreBlockAttempts; ++i) {
286       if (try_wait()) {
287         return true;
288       }
289
290       // The pause instruction is the polite way to spin, but it doesn't
291       // actually affect correctness to omit it if we don't have it.
292       // Pausing donates the full capabilities of the current core to
293       // its other hyperthreads for a dozen cycles or so
294       asm_volatile_pause();
295     }
296
297     return false;
298   }
299
300   FOLLY_NOINLINE void waitSlow() noexcept {
301     if (spinWaitForEarlyDelivery()) {
302       assert(state_.load(std::memory_order_acquire) == EARLY_DELIVERY);
303       return;
304     }
305
306     if (!MayBlock) {
307       while (!try_wait()) {
308         std::this_thread::yield();
309       }
310       return;
311     }
312
313     // guess we have to block :(
314     uint32_t expected = INIT;
315     if (!state_.compare_exchange_strong(expected, WAITING)) {
316       // CAS failed, last minute reprieve
317       assert(expected == EARLY_DELIVERY);
318       return;
319     }
320
321     while (true) {
322       detail::MemoryIdler::futexWait(state_, WAITING);
323
324       // state_ is the truth even if FUTEX_WAIT reported a matching
325       // FUTEX_WAKE, since we aren't using type-stable storage and we
326       // don't guarantee reuse.  The scenario goes like this: thread
327       // A's last touch of a Baton is a call to wake(), which stores
328       // LATE_DELIVERY and gets an unlucky context switch before delivering
329       // the corresponding futexWake.  Thread B sees LATE_DELIVERY
330       // without consuming a futex event, because it calls futexWait
331       // with an expected value of WAITING and hence doesn't go to sleep.
332       // B returns, so the Baton's memory is reused and becomes another
333       // Baton (or a reuse of this one).  B calls futexWait on the new
334       // Baton lifetime, then A wakes up and delivers a spurious futexWake
335       // to the same memory location.  B's futexWait will then report a
336       // consumed wake event even though state_ is still WAITING.
337       //
338       // It would be possible to add an extra state_ dance to communicate
339       // that the futexWake has been sent so that we can be sure to consume
340       // it before returning, but that would be a perf and complexity hit.
341       uint32_t s = state_.load(std::memory_order_acquire);
342       assert(s == WAITING || s == LATE_DELIVERY);
343
344       if (s == LATE_DELIVERY) {
345         return;
346       }
347       // retry
348     }
349   }
350
351   template <typename Clock, typename Duration>
352   FOLLY_NOINLINE bool tryWaitUntilSlow(
353       const std::chrono::time_point<Clock, Duration>& deadline) noexcept {
354     if (spinWaitForEarlyDelivery()) {
355       assert(state_.load(std::memory_order_acquire) == EARLY_DELIVERY);
356       return true;
357     }
358
359     // guess we have to block :(
360     uint32_t expected = INIT;
361     if (!state_.compare_exchange_strong(expected, WAITING)) {
362       // CAS failed, last minute reprieve
363       assert(expected == EARLY_DELIVERY);
364       return true;
365     }
366
367     while (true) {
368       auto rv = state_.futexWaitUntil(WAITING, deadline);
369       if (rv == folly::detail::FutexResult::TIMEDOUT) {
370         state_.store(TIMED_OUT, std::memory_order_release);
371         return false;
372       }
373
374       uint32_t s = state_.load(std::memory_order_acquire);
375       assert(s == WAITING || s == LATE_DELIVERY);
376       if (s == LATE_DELIVERY) {
377         return true;
378       }
379     }
380   }
381
382   detail::Futex<Atom> state_;
383 };
384
385 } // namespace folly