1/* futex operations for glibc-internal use. Linux version.
2 Copyright (C) 2014-2016 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19#ifndef FUTEX_INTERNAL_H
20#define FUTEX_INTERNAL_H
21
22#include <sysdeps/nptl/futex-internal.h>
23#include <errno.h>
24#include <lowlevellock-futex.h>
25#include <nptl/pthreadP.h>
26
27/* See sysdeps/nptl/futex-internal.h for documentation; this file only
28 contains Linux-specific comments.
29
30 The Linux kernel treats provides absolute timeouts based on the
31 CLOCK_REALTIME clock and relative timeouts measured against the
32 CLOCK_MONOTONIC clock.
33
34 We expect a Linux kernel version of 2.6.22 or more recent (since this
35 version, EINTR is not returned on spurious wake-ups anymore). */
36
37/* FUTEX_SHARED is always supported by the Linux kernel. */
38static __always_inline int
39futex_supports_pshared (int pshared)
40{
41 if (__glibc_likely (pshared == PTHREAD_PROCESS_PRIVATE))
42 return 0;
43 else if (pshared == PTHREAD_PROCESS_SHARED)
44 return 0;
45 else
46 return EINVAL;
47}
48
49/* The Linux kernel supports relative timeouts measured against the
50 CLOCK_MONOTONIC clock. */
51static __always_inline bool
52futex_supports_exact_relative_timeouts (void)
53{
54 return true;
55}
56
57/* See sysdeps/nptl/futex-internal.h for details. */
58static __always_inline int
59futex_wait (unsigned int *futex_word, unsigned int expected, int private)
60{
61 int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
62 switch (err)
63 {
64 case 0:
65 case -EAGAIN:
66 case -EINTR:
67 return -err;
68
69 case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
70 case -EFAULT: /* Must have been caused by a glibc or application bug. */
71 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
72 being normalized. Must have been caused by a glibc or
73 application bug. */
74 case -ENOSYS: /* Must have been caused by a glibc bug. */
75 /* No other errors are documented at this time. */
76 default:
77 futex_fatal_error ();
78 }
79}
80
81/* See sysdeps/nptl/futex-internal.h for details. */
82static __always_inline int
83futex_wait_cancelable (unsigned int *futex_word, unsigned int expected,
84 int private)
85{
86 int oldtype;
87 oldtype = __pthread_enable_asynccancel ();
88 int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
89 __pthread_disable_asynccancel (oldtype);
90 switch (err)
91 {
92 case 0:
93 case -EAGAIN:
94 case -EINTR:
95 return -err;
96
97 case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
98 case -EFAULT: /* Must have been caused by a glibc or application bug. */
99 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
100 being normalized. Must have been caused by a glibc or
101 application bug. */
102 case -ENOSYS: /* Must have been caused by a glibc bug. */
103 /* No other errors are documented at this time. */
104 default:
105 futex_fatal_error ();
106 }
107}
108
109/* See sysdeps/nptl/futex-internal.h for details. */
110static __always_inline int
111futex_reltimed_wait (unsigned int *futex_word, unsigned int expected,
112 const struct timespec *reltime, int private)
113{
114 int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
115 switch (err)
116 {
117 case 0:
118 case -EAGAIN:
119 case -EINTR:
120 case -ETIMEDOUT:
121 return -err;
122
123 case -EFAULT: /* Must have been caused by a glibc or application bug. */
124 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
125 being normalized. Must have been caused by a glibc or
126 application bug. */
127 case -ENOSYS: /* Must have been caused by a glibc bug. */
128 /* No other errors are documented at this time. */
129 default:
130 futex_fatal_error ();
131 }
132}
133
134/* See sysdeps/nptl/futex-internal.h for details. */
135static __always_inline int
136futex_reltimed_wait_cancelable (unsigned int *futex_word,
137 unsigned int expected,
138 const struct timespec *reltime, int private)
139{
140 int oldtype;
141 oldtype = __pthread_enable_asynccancel ();
142 int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
143 __pthread_disable_asynccancel (oldtype);
144 switch (err)
145 {
146 case 0:
147 case -EAGAIN:
148 case -EINTR:
149 case -ETIMEDOUT:
150 return -err;
151
152 case -EFAULT: /* Must have been caused by a glibc or application bug. */
153 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
154 being normalized. Must have been caused by a glibc or
155 application bug. */
156 case -ENOSYS: /* Must have been caused by a glibc bug. */
157 /* No other errors are documented at this time. */
158 default:
159 futex_fatal_error ();
160 }
161}
162
163/* See sysdeps/nptl/futex-internal.h for details. */
164static __always_inline int
165futex_abstimed_wait (unsigned int *futex_word, unsigned int expected,
166 const struct timespec *abstime, int private)
167{
168 /* Work around the fact that the kernel rejects negative timeout values
169 despite them being valid. */
170 if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
171 return ETIMEDOUT;
172 int err = lll_futex_timed_wait_bitset (futex_word, expected, abstime,
173 FUTEX_CLOCK_REALTIME, private);
174 switch (err)
175 {
176 case 0:
177 case -EAGAIN:
178 case -EINTR:
179 case -ETIMEDOUT:
180 return -err;
181
182 case -EFAULT: /* Must have been caused by a glibc or application bug. */
183 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
184 being normalized. Must have been caused by a glibc or
185 application bug. */
186 case -ENOSYS: /* Must have been caused by a glibc bug. */
187 /* No other errors are documented at this time. */
188 default:
189 futex_fatal_error ();
190 }
191}
192
193/* See sysdeps/nptl/futex-internal.h for details. */
194static __always_inline int
195futex_abstimed_wait_cancelable (unsigned int *futex_word,
196 unsigned int expected,
197 const struct timespec *abstime, int private)
198{
199 /* Work around the fact that the kernel rejects negative timeout values
200 despite them being valid. */
201 if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
202 return ETIMEDOUT;
203 int oldtype;
204 oldtype = __pthread_enable_asynccancel ();
205 int err = lll_futex_timed_wait_bitset (futex_word, expected, abstime,
206 FUTEX_CLOCK_REALTIME, private);
207 __pthread_disable_asynccancel (oldtype);
208 switch (err)
209 {
210 case 0:
211 case -EAGAIN:
212 case -EINTR:
213 case -ETIMEDOUT:
214 return -err;
215
216 case -EFAULT: /* Must have been caused by a glibc or application bug. */
217 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
218 being normalized. Must have been caused by a glibc or
219 application bug. */
220 case -ENOSYS: /* Must have been caused by a glibc bug. */
221 /* No other errors are documented at this time. */
222 default:
223 futex_fatal_error ();
224 }
225}
226
227/* See sysdeps/nptl/futex-internal.h for details. */
228static __always_inline void
229futex_wake (unsigned int *futex_word, int processes_to_wake, int private)
230{
231 int res = lll_futex_wake (futex_word, processes_to_wake, private);
232 /* No error. Ignore the number of woken processes. */
233 if (res >= 0)
234 return;
235 switch (res)
236 {
237 case -EFAULT: /* Could have happened due to memory reuse. */
238 case -EINVAL: /* Could be either due to incorrect alignment (a bug in
239 glibc or in the application) or due to memory being
240 reused for a PI futex. We cannot distinguish between the
241 two causes, and one of them is correct use, so we do not
242 act in this case. */
243 return;
244 case -ENOSYS: /* Must have been caused by a glibc bug. */
245 /* No other errors are documented at this time. */
246 default:
247 futex_fatal_error ();
248 }
249}
250
251#endif /* futex-internal.h */
252