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.
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.
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.
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/>. */
22 #include <lowlevellock.h>
23 #include <internaltypes.h>
24 #include <semaphore.h>
28 #include <shlib-compat.h>
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
)
41 oldtype
= __pthread_enable_asynccancel ();
42 err
= lll_futex_wait (futex
, expected
, private);
44 __pthread_disable_asynccancel (oldtype
);
52 /* Get the current time. */
53 __gettimeofday (&tv
, NULL
);
55 /* Compute relative timeout. */
56 sec
= abstime
->tv_sec
- tv
.tv_sec
;
57 nsec
= abstime
->tv_nsec
- tv
.tv_usec
* 1000;
64 /* Already timed out? */
72 oldtype
= __pthread_enable_asynccancel ();
73 err
= lll_futex_timed_wait (futex
, expected
, &rt
, private);
75 __pthread_disable_asynccancel (oldtype
);
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
89 case -ENOSYS
: /* Must have been caused by a glibc bug. */
90 /* No other errors are documented at this time. */
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)
101 int res
= lll_futex_wake (futex
, processes_to_wake
, private);
102 /* No error. Ignore the number of woken processes. */
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
114 case -ENOSYS
: /* Must have been caused by a glibc bug. */
115 /* No other errors are documented at this time. */
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;
131 __sem_wait_32_finish (struct new_sem
*sem
);
134 __sem_wait_cleanup (void *arg
)
136 struct new_sem
*sem
= (struct new_sem
*) arg
;
138 __sem_wait_32_finish (sem
);
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. */
147 __attribute__ ((noinline
))
148 do_futex_wait (struct new_sem
*sem
, const struct timespec
*abstime
)
152 err
= futex_abstimed_wait (&sem
->value
, SEM_NWAITERS_MASK
, abstime
,
158 /* Fast path: Try to grab a token without blocking. */
160 __new_sem_wait_fast (struct new_sem
*sem
, int definitive_result
)
165 __sparc32_atomic_do_lock24(&sem
->pad
);
168 if ((v
>> SEM_VALUE_SHIFT
) == 0)
171 sem
->value
= v
- (1 << SEM_VALUE_SHIFT
);
173 __sparc32_atomic_do_unlock24(&sem
->pad
);
178 /* Slow path that blocks. */
180 __attribute__ ((noinline
))
181 __new_sem_wait_slow (struct new_sem
*sem
, const struct timespec
*abstime
)
186 __sparc32_atomic_do_lock24(&sem
->pad
);
190 pthread_cleanup_push (__sem_wait_cleanup
, sem
);
192 /* Wait for a token to be available. Retry until we can grab one. */
196 if (!(v
& SEM_NWAITERS_MASK
))
197 sem
->value
= v
| SEM_NWAITERS_MASK
;
199 /* If there is no token, wait. */
200 if ((v
>> SEM_VALUE_SHIFT
) == 0)
202 __sparc32_atomic_do_unlock24(&sem
->pad
);
204 err
= do_futex_wait(sem
, abstime
);
205 if (err
== ETIMEDOUT
||
206 (err
== EINTR
&& sem_assume_only_signals_cause_futex_EINTR
))
214 __sparc32_atomic_do_lock24(&sem
->pad
);
216 /* We blocked, so there might be a token now. */
220 /* If there is no token, we must not try to grab one. */
221 while ((v
>> SEM_VALUE_SHIFT
) == 0);
223 sem
->value
= v
- (1 << SEM_VALUE_SHIFT
);
225 __sparc32_atomic_do_unlock24(&sem
->pad
);
228 pthread_cleanup_pop (0);
230 __sem_wait_32_finish (sem
);
235 /* Stop being a registered waiter (non-64b-atomics code only). */
237 __sem_wait_32_finish (struct new_sem
*sem
)
239 __sparc32_atomic_do_lock24(&sem
->pad
);
241 if (--sem
->nwaiters
== 0)
242 sem
->value
&= ~SEM_NWAITERS_MASK
;
244 __sparc32_atomic_do_unlock24(&sem
->pad
);