1/* futex operations for glibc-internal use. Linux version.
2 Copyright (C) 2014-2019 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 <sysdep-cancel.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/* See sysdeps/nptl/futex-internal.h for details. */
50static __always_inline int
51futex_wait (unsigned int *futex_word, unsigned int expected, int private)
52{
53 int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
54 switch (err)
55 {
56 case 0:
57 case -EAGAIN:
58 case -EINTR:
59 return -err;
60
61 case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
62 case -EFAULT: /* Must have been caused by a glibc or application bug. */
63 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
64 being normalized. Must have been caused by a glibc or
65 application bug. */
66 case -ENOSYS: /* Must have been caused by a glibc bug. */
67 /* No other errors are documented at this time. */
68 default:
69 futex_fatal_error ();
70 }
71}
72
73/* See sysdeps/nptl/futex-internal.h for details. */
74static __always_inline int
75futex_wait_cancelable (unsigned int *futex_word, unsigned int expected,
76 int private)
77{
78 int oldtype;
79 oldtype = __pthread_enable_asynccancel ();
80 int err = lll_futex_timed_wait (futex_word, expected, NULL, private);
81 __pthread_disable_asynccancel (oldtype);
82 switch (err)
83 {
84 case 0:
85 case -EAGAIN:
86 case -EINTR:
87 return -err;
88
89 case -ETIMEDOUT: /* Cannot have happened as we provided no timeout. */
90 case -EFAULT: /* Must have been caused by a glibc or application bug. */
91 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
92 being normalized. Must have been caused by a glibc or
93 application bug. */
94 case -ENOSYS: /* Must have been caused by a glibc bug. */
95 /* No other errors are documented at this time. */
96 default:
97 futex_fatal_error ();
98 }
99}
100
101/* See sysdeps/nptl/futex-internal.h for details. */
102static __always_inline int
103futex_reltimed_wait (unsigned int *futex_word, unsigned int expected,
104 const struct timespec *reltime, int private)
105{
106 int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
107 switch (err)
108 {
109 case 0:
110 case -EAGAIN:
111 case -EINTR:
112 case -ETIMEDOUT:
113 return -err;
114
115 case -EFAULT: /* Must have been caused by a glibc or application bug. */
116 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
117 being normalized. Must have been caused by a glibc or
118 application bug. */
119 case -ENOSYS: /* Must have been caused by a glibc bug. */
120 /* No other errors are documented at this time. */
121 default:
122 futex_fatal_error ();
123 }
124}
125
126/* See sysdeps/nptl/futex-internal.h for details. */
127static __always_inline int
128futex_reltimed_wait_cancelable (unsigned int *futex_word,
129 unsigned int expected,
130 const struct timespec *reltime, int private)
131{
132 int oldtype;
133 oldtype = LIBC_CANCEL_ASYNC ();
134 int err = lll_futex_timed_wait (futex_word, expected, reltime, private);
135 LIBC_CANCEL_RESET (oldtype);
136 switch (err)
137 {
138 case 0:
139 case -EAGAIN:
140 case -EINTR:
141 case -ETIMEDOUT:
142 return -err;
143
144 case -EFAULT: /* Must have been caused by a glibc or application bug. */
145 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
146 being normalized. Must have been caused by a glibc or
147 application bug. */
148 case -ENOSYS: /* Must have been caused by a glibc bug. */
149 /* No other errors are documented at this time. */
150 default:
151 futex_fatal_error ();
152 }
153}
154
155/* See sysdeps/nptl/futex-internal.h for details. */
156static __always_inline int
157futex_abstimed_supported_clockid (clockid_t clockid)
158{
159 return lll_futex_supported_clockid (clockid);
160}
161
162/* See sysdeps/nptl/futex-internal.h for details. */
163static __always_inline int
164futex_abstimed_wait (unsigned int *futex_word, unsigned int expected,
165 clockid_t clockid,
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_clock_wait_bitset (futex_word, expected,
173 clockid, abstime,
174 private);
175 switch (err)
176 {
177 case 0:
178 case -EAGAIN:
179 case -EINTR:
180 case -ETIMEDOUT:
181 return -err;
182
183 case -EFAULT: /* Must have been caused by a glibc or application bug. */
184 case -EINVAL: /* Either due to wrong alignment, unsupported
185 clockid or due to the timeout not being
186 normalized. Must have been caused by a glibc or
187 application bug. */
188 case -ENOSYS: /* Must have been caused by a glibc bug. */
189 /* No other errors are documented at this time. */
190 default:
191 futex_fatal_error ();
192 }
193}
194
195/* See sysdeps/nptl/futex-internal.h for details. */
196static __always_inline int
197futex_abstimed_wait_cancelable (unsigned int *futex_word,
198 unsigned int expected,
199 clockid_t clockid,
200 const struct timespec *abstime, int private)
201{
202 /* Work around the fact that the kernel rejects negative timeout values
203 despite them being valid. */
204 if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
205 return ETIMEDOUT;
206 int oldtype;
207 oldtype = __pthread_enable_asynccancel ();
208 int err = lll_futex_clock_wait_bitset (futex_word, expected,
209 clockid, abstime,
210 private);
211 __pthread_disable_asynccancel (oldtype);
212 switch (err)
213 {
214 case 0:
215 case -EAGAIN:
216 case -EINTR:
217 case -ETIMEDOUT:
218 return -err;
219
220 case -EFAULT: /* Must have been caused by a glibc or application bug. */
221 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
222 being normalized. Must have been caused by a glibc or
223 application bug. */
224 case -ENOSYS: /* Must have been caused by a glibc bug. */
225 /* No other errors are documented at this time. */
226 default:
227 futex_fatal_error ();
228 }
229}
230
231/* See sysdeps/nptl/futex-internal.h for details. */
232static __always_inline void
233futex_wake (unsigned int *futex_word, int processes_to_wake, int private)
234{
235 int res = lll_futex_wake (futex_word, processes_to_wake, private);
236 /* No error. Ignore the number of woken processes. */
237 if (res >= 0)
238 return;
239 switch (res)
240 {
241 case -EFAULT: /* Could have happened due to memory reuse. */
242 case -EINVAL: /* Could be either due to incorrect alignment (a bug in
243 glibc or in the application) or due to memory being
244 reused for a PI futex. We cannot distinguish between the
245 two causes, and one of them is correct use, so we do not
246 act in this case. */
247 return;
248 case -ENOSYS: /* Must have been caused by a glibc bug. */
249 /* No other errors are documented at this time. */
250 default:
251 futex_fatal_error ();
252 }
253}
254
255#endif /* futex-internal.h */
256