1 /*
2  * Distributed under the Boost Software License, Version 1.0.
3  * (See accompanying file LICENSE_1_0.txt or copy at
4  * http://www.boost.org/LICENSE_1_0.txt)
5  *
6  * Copyright (c) 2020 Andrey Semashev
7  */
8 /*!
9  * \file   atomic/detail/futex.hpp
10  *
11  * This header defines wrappers around futex syscall.
12  *
13  * http://man7.org/linux/man-pages/man2/futex.2.html
14  * https://man.openbsd.org/futex
15  */
16 
17 #ifndef BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_
18 #define BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_
19 
20 #include <boost/atomic/detail/config.hpp>
21 
22 #ifdef BOOST_HAS_PRAGMA_ONCE
23 #pragma once
24 #endif
25 
26 #if defined(__linux__) || defined(__OpenBSD__) || defined(__NETBSD__) || defined(__NetBSD__)
27 
28 #include <sys/syscall.h>
29 
30 // Some Android NDKs (Google NDK and older Crystax.NET NDK versions) don't define SYS_futex.
31 #if defined(SYS_futex)
32 #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS_futex
33 #elif defined(__NR_futex)
34 #define BOOST_ATOMIC_DETAIL_SYS_FUTEX __NR_futex
35 #elif defined(SYS___futex)
36 // NetBSD defines SYS___futex, which has slightly different parameters. Basically, it has decoupled timeout and val2 parameters:
37 // int __futex(int *addr1, int op, int val1, const struct timespec *timeout, int *addr2, int val2, int val3);
38 // https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/syscall.h
39 // http://bxr.su/NetBSD/sys/kern/sys_futex.c
40 #define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS___futex
41 #define BOOST_ATOMIC_DETAIL_NETBSD_FUTEX
42 #endif
43 
44 #if defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX)
45 
46 #include <cstddef>
47 #if defined(__linux__)
48 #include <linux/futex.h>
49 #else
50 #include <sys/futex.h>
51 #endif
52 #include <boost/atomic/detail/intptr.hpp>
53 #include <boost/atomic/detail/header.hpp>
54 
55 #define BOOST_ATOMIC_DETAIL_HAS_FUTEX
56 
57 #if defined(FUTEX_PRIVATE_FLAG)
58 #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG FUTEX_PRIVATE_FLAG
59 #else
60 #define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG 0
61 #endif
62 
63 namespace boost {
64 namespace atomics {
65 namespace detail {
66 
67 //! Invokes an operation on the futex
futex_invoke(void * addr1,int op,unsigned int val1,const void * timeout=NULL,void * addr2=NULL,unsigned int val3=0)68 BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, const void* timeout = NULL, void* addr2 = NULL, unsigned int val3 = 0) BOOST_NOEXCEPT
69 {
70 #if !defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX)
71     return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, val3);
72 #else
73     // Pass 0 in val2.
74     return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, 0u, val3);
75 #endif
76 }
77 
78 //! Invokes an operation on the futex
futex_invoke(void * addr1,int op,unsigned int val1,unsigned int val2,void * addr2=NULL,unsigned int val3=0)79 BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, unsigned int val2, void* addr2 = NULL, unsigned int val3 = 0) BOOST_NOEXCEPT
80 {
81 #if !defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX)
82     return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< atomics::detail::uintptr_t >(val2), addr2, val3);
83 #else
84     // Pass NULL in timeout.
85     return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< void* >(NULL), addr2, val2, val3);
86 #endif
87 }
88 
89 //! Checks that the value \c pval is \c expected and blocks
futex_wait(void * pval,unsigned int expected)90 BOOST_FORCEINLINE int futex_wait(void* pval, unsigned int expected) BOOST_NOEXCEPT
91 {
92     return futex_invoke(pval, FUTEX_WAIT, expected);
93 }
94 
95 //! Checks that the value \c pval is \c expected and blocks
futex_wait_private(void * pval,unsigned int expected)96 BOOST_FORCEINLINE int futex_wait_private(void* pval, unsigned int expected) BOOST_NOEXCEPT
97 {
98     return futex_invoke(pval, FUTEX_WAIT | BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG, expected);
99 }
100 
101 //! Wakes the specified number of threads waiting on the futex
futex_signal(void * pval,unsigned int count=1u)102 BOOST_FORCEINLINE int futex_signal(void* pval, unsigned int count = 1u) BOOST_NOEXCEPT
103 {
104     return futex_invoke(pval, FUTEX_WAKE, count);
105 }
106 
107 //! Wakes the specified number of threads waiting on the futex
futex_signal_private(void * pval,unsigned int count=1u)108 BOOST_FORCEINLINE int futex_signal_private(void* pval, unsigned int count = 1u) BOOST_NOEXCEPT
109 {
110     return futex_invoke(pval, FUTEX_WAKE | BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG, count);
111 }
112 
113 //! Wakes all threads waiting on the futex
futex_broadcast(void * pval)114 BOOST_FORCEINLINE int futex_broadcast(void* pval) BOOST_NOEXCEPT
115 {
116     return futex_signal(pval, (~static_cast< unsigned int >(0u)) >> 1);
117 }
118 
119 //! Wakes all threads waiting on the futex
futex_broadcast_private(void * pval)120 BOOST_FORCEINLINE int futex_broadcast_private(void* pval) BOOST_NOEXCEPT
121 {
122     return futex_signal_private(pval, (~static_cast< unsigned int >(0u)) >> 1);
123 }
124 
125 //! Wakes the wake_count threads waiting on the futex pval1 and requeues up to requeue_count of the blocked threads onto another futex pval2
futex_requeue(void * pval1,void * pval2,unsigned int wake_count=1u,unsigned int requeue_count=(~static_cast<unsigned int> (0u))>>1)126 BOOST_FORCEINLINE int futex_requeue(void* pval1, void* pval2, unsigned int wake_count = 1u, unsigned int requeue_count = (~static_cast< unsigned int >(0u)) >> 1) BOOST_NOEXCEPT
127 {
128     return futex_invoke(pval1, FUTEX_REQUEUE, wake_count, requeue_count, pval2);
129 }
130 
131 //! Wakes the wake_count threads waiting on the futex pval1 and requeues up to requeue_count of the blocked threads onto another futex pval2
futex_requeue_private(void * pval1,void * pval2,unsigned int wake_count=1u,unsigned int requeue_count=(~static_cast<unsigned int> (0u))>>1)132 BOOST_FORCEINLINE int futex_requeue_private(void* pval1, void* pval2, unsigned int wake_count = 1u, unsigned int requeue_count = (~static_cast< unsigned int >(0u)) >> 1) BOOST_NOEXCEPT
133 {
134     return futex_invoke(pval1, FUTEX_REQUEUE | BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG, wake_count, requeue_count, pval2);
135 }
136 
137 } // namespace detail
138 } // namespace atomics
139 } // namespace boost
140 
141 #include <boost/atomic/detail/footer.hpp>
142 
143 #endif // defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX)
144 
145 #endif // defined(__linux__) || defined(__OpenBSD__) || defined(__NETBSD__) || defined(__NetBSD__)
146 
147 #endif // BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_
148