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. */ |
38 | static __always_inline int |
39 | futex_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. */ |
50 | static __always_inline int |
51 | futex_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. */ |
74 | static __always_inline int |
75 | futex_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. */ |
102 | static __always_inline int |
103 | futex_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. */ |
127 | static __always_inline int |
128 | futex_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. */ |
156 | static __always_inline int |
157 | futex_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. */ |
163 | static __always_inline int |
164 | futex_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. */ |
196 | static __always_inline int |
197 | futex_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. */ |
232 | static __always_inline void |
233 | futex_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 | |