]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/sparc/sparc32/sem_waitcommon.c
Fix sparc semaphore implementation after recent changes.
[thirdparty/glibc.git] / sysdeps / sparc / sparc32 / sem_waitcommon.c
1 /* sem_waitcommon -- wait on a semaphore, shared code.
2 Copyright (C) 2003-2015 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Paul Mackerras <paulus@au.ibm.com>, 2003.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C 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 GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
19
20 #include <errno.h>
21 #include <sysdep.h>
22 #include <lowlevellock.h>
23 #include <internaltypes.h>
24 #include <semaphore.h>
25 #include <sys/time.h>
26
27 #include <pthreadP.h>
28 #include <shlib-compat.h>
29 #include <atomic.h>
30
31 /* Wrapper for lll_futex_wait with absolute timeout and error checking.
32 TODO Remove when cleaning up the futex API throughout glibc. */
33 static __always_inline int
34 futex_abstimed_wait (unsigned int* futex, unsigned int expected,
35 const struct timespec* abstime, int private, bool cancel)
36 {
37 int err, oldtype;
38 if (abstime == NULL)
39 {
40 if (cancel)
41 oldtype = __pthread_enable_asynccancel ();
42 err = lll_futex_wait (futex, expected, private);
43 if (cancel)
44 __pthread_disable_asynccancel (oldtype);
45 }
46 else
47 {
48 struct timeval tv;
49 struct timespec rt;
50 int sec, nsec;
51
52 /* Get the current time. */
53 __gettimeofday (&tv, NULL);
54
55 /* Compute relative timeout. */
56 sec = abstime->tv_sec - tv.tv_sec;
57 nsec = abstime->tv_nsec - tv.tv_usec * 1000;
58 if (nsec < 0)
59 {
60 nsec += 1000000000;
61 --sec;
62 }
63
64 /* Already timed out? */
65 if (sec < 0)
66 return ETIMEDOUT;
67
68 /* Do wait. */
69 rt.tv_sec = sec;
70 rt.tv_nsec = nsec;
71 if (cancel)
72 oldtype = __pthread_enable_asynccancel ();
73 err = lll_futex_timed_wait (futex, expected, &rt, private);
74 if (cancel)
75 __pthread_disable_asynccancel (oldtype);
76 }
77 switch (err)
78 {
79 case 0:
80 case -EAGAIN:
81 case -EINTR:
82 case -ETIMEDOUT:
83 return -err;
84
85 case -EFAULT: /* Must have been caused by a glibc or application bug. */
86 case -EINVAL: /* Either due to wrong alignment or due to the timeout not
87 being normalized. Must have been caused by a glibc or
88 application bug. */
89 case -ENOSYS: /* Must have been caused by a glibc bug. */
90 /* No other errors are documented at this time. */
91 default:
92 abort ();
93 }
94 }
95
96 /* Wrapper for lll_futex_wake, with error checking.
97 TODO Remove when cleaning up the futex API throughout glibc. */
98 static __always_inline void
99 futex_wake (unsigned int* futex, int processes_to_wake, int private)
100 {
101 int res = lll_futex_wake (futex, processes_to_wake, private);
102 /* No error. Ignore the number of woken processes. */
103 if (res >= 0)
104 return;
105 switch (res)
106 {
107 case -EFAULT: /* Could have happened due to memory reuse. */
108 case -EINVAL: /* Could be either due to incorrect alignment (a bug in
109 glibc or in the application) or due to memory being
110 reused for a PI futex. We cannot distinguish between the
111 two causes, and one of them is correct use, so we do not
112 act in this case. */
113 return;
114 case -ENOSYS: /* Must have been caused by a glibc bug. */
115 /* No other errors are documented at this time. */
116 default:
117 abort ();
118 }
119 }
120
121
122 /* Set this to true if you assume that, in contrast to current Linux futex
123 documentation, lll_futex_wake can return -EINTR only if interrupted by a
124 signal, not spuriously due to some other reason.
125 TODO Discuss EINTR conditions with the Linux kernel community. For
126 now, we set this to true to not change behavior of semaphores compared
127 to previous glibc builds. */
128 static const int sem_assume_only_signals_cause_futex_EINTR = 1;
129
130 static void
131 __sem_wait_32_finish (struct new_sem *sem);
132
133 static void
134 __sem_wait_cleanup (void *arg)
135 {
136 struct new_sem *sem = (struct new_sem *) arg;
137
138 __sem_wait_32_finish (sem);
139 }
140
141 /* Wait until at least one token is available, possibly with a timeout.
142 This is in a separate function in order to make sure gcc
143 puts the call site into an exception region, and thus the
144 cleanups get properly run. TODO still necessary? Other futex_wait
145 users don't seem to need it. */
146 static int
147 __attribute__ ((noinline))
148 do_futex_wait (struct new_sem *sem, const struct timespec *abstime)
149 {
150 int err;
151
152 err = futex_abstimed_wait (&sem->value, SEM_NWAITERS_MASK, abstime,
153 sem->private, true);
154
155 return err;
156 }
157
158 /* Fast path: Try to grab a token without blocking. */
159 static int
160 __new_sem_wait_fast (struct new_sem *sem, int definitive_result)
161 {
162 unsigned int v;
163 int ret = 0;
164
165 __sparc32_atomic_do_lock24(&sem->pad);
166
167 v = sem->value;
168 if ((v >> SEM_VALUE_SHIFT) == 0)
169 ret = -1;
170 else
171 sem->value = v - (1 << SEM_VALUE_SHIFT);
172
173 __sparc32_atomic_do_unlock24(&sem->pad);
174
175 return ret;
176 }
177
178 /* Slow path that blocks. */
179 static int
180 __attribute__ ((noinline))
181 __new_sem_wait_slow (struct new_sem *sem, const struct timespec *abstime)
182 {
183 unsigned int v;
184 int err = 0;
185
186 __sparc32_atomic_do_lock24(&sem->pad);
187
188 sem->nwaiters++;
189
190 pthread_cleanup_push (__sem_wait_cleanup, sem);
191
192 /* Wait for a token to be available. Retry until we can grab one. */
193 v = sem->value;
194 do
195 {
196 if (!(v & SEM_NWAITERS_MASK))
197 sem->value = v | SEM_NWAITERS_MASK;
198
199 /* If there is no token, wait. */
200 if ((v >> SEM_VALUE_SHIFT) == 0)
201 {
202 __sparc32_atomic_do_unlock24(&sem->pad);
203
204 err = do_futex_wait(sem, abstime);
205 if (err == ETIMEDOUT ||
206 (err == EINTR && sem_assume_only_signals_cause_futex_EINTR))
207 {
208 __set_errno (err);
209 err = -1;
210 goto error;
211 }
212 err = 0;
213
214 __sparc32_atomic_do_lock24(&sem->pad);
215
216 /* We blocked, so there might be a token now. */
217 v = sem->value;
218 }
219 }
220 /* If there is no token, we must not try to grab one. */
221 while ((v >> SEM_VALUE_SHIFT) == 0);
222
223 sem->value = v - (1 << SEM_VALUE_SHIFT);
224
225 __sparc32_atomic_do_unlock24(&sem->pad);
226
227 error:
228 pthread_cleanup_pop (0);
229
230 __sem_wait_32_finish (sem);
231
232 return err;
233 }
234
235 /* Stop being a registered waiter (non-64b-atomics code only). */
236 static void
237 __sem_wait_32_finish (struct new_sem *sem)
238 {
239 __sparc32_atomic_do_lock24(&sem->pad);
240
241 if (--sem->nwaiters == 0)
242 sem->value &= ~SEM_NWAITERS_MASK;
243
244 __sparc32_atomic_do_unlock24(&sem->pad);
245 }