]> git.ipfire.org Git - thirdparty/gcc.git/blame - libstdc++-v3/src/c++11/futex.cc
libstdc++: Support futex waiting on chrono::steady_clock directly
[thirdparty/gcc.git] / libstdc++-v3 / src / c++11 / futex.cc
CommitLineData
eae801ba
TR
1// futex -*- C++ -*-
2
8d9254fc 3// Copyright (C) 2015-2020 Free Software Foundation, Inc.
eae801ba
TR
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
eae801ba 25#include <bits/atomic_futex.h>
8ba7f29e 26#ifdef _GLIBCXX_HAS_GTHREADS
1e3919ac 27#if defined(_GLIBCXX_HAVE_LINUX_FUTEX) && ATOMIC_INT_LOCK_FREE > 1
eae801ba
TR
28#include <chrono>
29#include <climits>
30#include <syscall.h>
31#include <unistd.h>
32#include <sys/time.h>
33#include <errno.h>
34#include <debug/debug.h>
35
01d412ef
MC
36#ifdef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL
37#include <unistd.h>
38#include <sys/syscall.h>
39#endif
40
eae801ba
TR
41// Constants for the wait/wake futex syscall operations
42const unsigned futex_wait_op = 0;
5bad23ce 43const unsigned futex_wait_bitset_op = 9;
01d412ef 44const unsigned futex_clock_monotonic_flag = 0;
5bad23ce
MC
45const unsigned futex_clock_realtime_flag = 256;
46const unsigned futex_bitset_match_any = ~0;
eae801ba
TR
47const unsigned futex_wake_op = 1;
48
5bad23ce
MC
49namespace
50{
51 std::atomic<bool> futex_clock_realtime_unavailable;
01d412ef 52 std::atomic<bool> futex_clock_monotonic_unavailable;
5bad23ce
MC
53}
54
eae801ba
TR
55namespace std _GLIBCXX_VISIBILITY(default)
56{
57_GLIBCXX_BEGIN_NAMESPACE_VERSION
58
59 bool
60 __atomic_futex_unsigned_base::_M_futex_wait_until(unsigned *__addr,
61 unsigned __val,
62 bool __has_timeout, chrono::seconds __s, chrono::nanoseconds __ns)
63 {
64 if (!__has_timeout)
65 {
66 // Ignore whether we actually succeeded to block because at worst,
67 // we will fall back to spin-waiting. The only thing we could do
68 // here on errors is abort.
69 int ret __attribute__((unused));
55089c2b 70 ret = syscall (SYS_futex, __addr, futex_wait_op, __val, nullptr);
9fbd2e55 71 __glibcxx_assert(ret == 0 || errno == EINTR || errno == EAGAIN);
eae801ba
TR
72 return true;
73 }
74 else
75 {
5bad23ce
MC
76 if (!futex_clock_realtime_unavailable.load(std::memory_order_relaxed))
77 {
78 struct timespec rt;
79 rt.tv_sec = __s.count();
80 rt.tv_nsec = __ns.count();
81 if (syscall (SYS_futex, __addr,
82 futex_wait_bitset_op | futex_clock_realtime_flag,
83 __val, &rt, nullptr, futex_bitset_match_any) == -1)
84 {
85 __glibcxx_assert(errno == EINTR || errno == EAGAIN
86 || errno == ETIMEDOUT || errno == ENOSYS);
87 if (errno == ETIMEDOUT)
88 return false;
89 if (errno == ENOSYS)
90 {
91 futex_clock_realtime_unavailable.store(true,
92 std::memory_order_relaxed);
93 // Fall through to legacy implementation if the system
94 // call is unavailable.
95 }
96 else
97 return true;
98 }
99 else
100 return true;
101 }
102
103 // We only get to here if futex_clock_realtime_unavailable was
104 // true or has just been set to true.
eae801ba
TR
105 struct timeval tv;
106 gettimeofday (&tv, NULL);
107 // Convert the absolute timeout value to a relative timeout
108 struct timespec rt;
109 rt.tv_sec = __s.count() - tv.tv_sec;
110 rt.tv_nsec = __ns.count() - tv.tv_usec * 1000;
111 if (rt.tv_nsec < 0)
112 {
113 rt.tv_nsec += 1000000000;
114 --rt.tv_sec;
115 }
116 // Did we already time out?
117 if (rt.tv_sec < 0)
118 return false;
119
120 if (syscall (SYS_futex, __addr, futex_wait_op, __val, &rt) == -1)
121 {
9fbd2e55
JW
122 __glibcxx_assert(errno == EINTR || errno == EAGAIN
123 || errno == ETIMEDOUT);
eae801ba
TR
124 if (errno == ETIMEDOUT)
125 return false;
126 }
127 return true;
128 }
129 }
130
01d412ef
MC
131 bool
132 __atomic_futex_unsigned_base::_M_futex_wait_until_steady(unsigned *__addr,
133 unsigned __val,
134 bool __has_timeout, chrono::seconds __s, chrono::nanoseconds __ns)
135 {
136 if (!__has_timeout)
137 {
138 // Ignore whether we actually succeeded to block because at worst,
139 // we will fall back to spin-waiting. The only thing we could do
140 // here on errors is abort.
141 int ret __attribute__((unused));
142 ret = syscall (SYS_futex, __addr, futex_wait_op, __val, nullptr);
143 __glibcxx_assert(ret == 0 || errno == EINTR || errno == EAGAIN);
144 return true;
145 }
146 else
147 {
148 if (!futex_clock_monotonic_unavailable.load(std::memory_order_relaxed))
149 {
150 struct timespec rt;
151 rt.tv_sec = __s.count();
152 rt.tv_nsec = __ns.count();
153
154 if (syscall (SYS_futex, __addr,
155 futex_wait_bitset_op | futex_clock_monotonic_flag,
156 __val, &rt, nullptr, futex_bitset_match_any) == -1)
157 {
158 __glibcxx_assert(errno == EINTR || errno == EAGAIN
159 || errno == ETIMEDOUT || errno == ENOSYS);
160 if (errno == ETIMEDOUT)
161 return false;
162 else if (errno == ENOSYS)
163 {
164 futex_clock_monotonic_unavailable.store(true,
165 std::memory_order_relaxed);
166 // Fall through to legacy implementation if the system
167 // call is unavailable.
168 }
169 else
170 return true;
171 }
172 }
173
174 // We only get to here if futex_clock_monotonic_unavailable was
175 // true or has just been set to true.
176 struct timespec ts;
177#ifdef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL
178 syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts);
179#else
180 clock_gettime(CLOCK_MONOTONIC, &ts);
181#endif
182 // Convert the absolute timeout value to a relative timeout
183 struct timespec rt;
184 rt.tv_sec = __s.count() - ts.tv_sec;
185 rt.tv_nsec = __ns.count() - ts.tv_nsec;
186 if (rt.tv_nsec < 0)
187 {
188 rt.tv_nsec += 1000000000;
189 --rt.tv_sec;
190 }
191 // Did we already time out?
192 if (rt.tv_sec < 0)
193 return false;
194
195 if (syscall (SYS_futex, __addr, futex_wait_op, __val, &rt) == -1)
196 {
197 __glibcxx_assert(errno == EINTR || errno == EAGAIN
198 || errno == ETIMEDOUT);
199 if (errno == ETIMEDOUT)
200 return false;
201 }
202 return true;
203 }
204 }
205
eae801ba
TR
206 void
207 __atomic_futex_unsigned_base::_M_futex_notify_all(unsigned* __addr)
208 {
209 // This syscall can fail for various reasons, including in situations
210 // in which there is no real error. Thus, we don't bother checking
211 // the error codes. See the futex documentation and glibc for background.
212 syscall (SYS_futex, __addr, futex_wake_op, INT_MAX);
213 }
214
8dcf3d3c 215_GLIBCXX_END_NAMESPACE_VERSION
eae801ba 216}
8ba7f29e
JW
217#endif // defined(_GLIBCXX_HAVE_LINUX_FUTEX) && ATOMIC_INT_LOCK_FREE > 1
218#endif // _GLIBCXX_HAS_GTHREADS