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 <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. */ |
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 | /* The Linux kernel supports relative timeouts measured against the |
50 | CLOCK_MONOTONIC clock. */ |
51 | static __always_inline bool |
52 | futex_supports_exact_relative_timeouts (void) |
53 | { |
54 | return true; |
55 | } |
56 | |
57 | /* See sysdeps/nptl/futex-internal.h for details. */ |
58 | static __always_inline int |
59 | futex_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. */ |
82 | static __always_inline int |
83 | futex_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. */ |
110 | static __always_inline int |
111 | futex_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. */ |
135 | static __always_inline int |
136 | futex_reltimed_wait_cancelable (unsigned int *futex_word, |
137 | unsigned int expected, |
138 | const struct timespec *reltime, int private) |
139 | { |
140 | int oldtype; |
141 | oldtype = LIBC_CANCEL_ASYNC (); |
142 | int err = lll_futex_timed_wait (futex_word, expected, reltime, private); |
143 | LIBC_CANCEL_RESET (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. */ |
164 | static __always_inline int |
165 | futex_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. */ |
194 | static __always_inline int |
195 | futex_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. */ |
228 | static __always_inline void |
229 | futex_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 | |