Multi-producer multi-consumer queue with optional blocking
[folly.git] / folly / detail / Futex.h
1 /*
2  * Copyright 2013 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 <atomic>
20 #include <limits>
21 #include <assert.h>
22 #include <errno.h>
23 #include <linux/futex.h>
24 #include <sys/syscall.h>
25 #include <unistd.h>
26 #include <boost/noncopyable.hpp>
27
28 namespace folly { namespace detail {
29
30 /**
31  * Futex is an atomic 32 bit unsigned integer that provides access to the
32  * futex() syscall on that value.  It is templated in such a way that it
33  * can interact properly with DeterministicSchedule testing.
34  *
35  * If you don't know how to use futex(), you probably shouldn't be using
36  * this class.  Even if you do know how, you should have a good reason
37  * (and benchmarks to back you up).
38  */
39 template <template <typename> class Atom = std::atomic>
40 struct Futex : Atom<uint32_t>, boost::noncopyable {
41
42   explicit Futex(uint32_t init = 0) : Atom<uint32_t>(init) {}
43
44   /** Puts the thread to sleep if this->load() == expected.  Returns true when
45    *  it is returning because it has consumed a wake() event, false for any
46    *  other return (signal, this->load() != expected, or spurious wakeup). */
47   bool futexWait(uint32_t expected, uint32_t waitMask = -1);
48
49   /** Wakens up to count waiters where (waitMask & wakeMask) != 0,
50    *  returning the number of awoken threads. */
51   int futexWake(int count = std::numeric_limits<int>::max(),
52                 uint32_t wakeMask = -1);
53 };
54
55 template <>
56 inline bool Futex<std::atomic>::futexWait(uint32_t expected,
57                                           uint32_t waitMask) {
58   assert(sizeof(*this) == sizeof(int));
59   int rv = syscall(SYS_futex,
60                    this, /* addr1 */
61                    FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG, /* op */
62                    expected, /* val */
63                    nullptr, /* timeout */
64                    nullptr, /* addr2 */
65                    waitMask); /* val3 */
66   assert(rv == 0 || (errno == EWOULDBLOCK || errno == EINTR));
67   return rv == 0;
68 }
69
70 template <>
71 inline int Futex<std::atomic>::futexWake(int count, uint32_t wakeMask) {
72   assert(sizeof(*this) == sizeof(int));
73   int rv = syscall(SYS_futex,
74                    this, /* addr1 */
75                    FUTEX_WAKE_BITSET | FUTEX_PRIVATE_FLAG, /* op */
76                    count, /* val */
77                    nullptr, /* timeout */
78                    nullptr, /* addr2 */
79                    wakeMask); /* val3 */
80   assert(rv >= 0);
81   return rv;
82 }
83
84 }}