/* sem_waitcommon -- wait on a semaphore, shared code. Copyright (C) 2003-2016 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Paul Mackerras , 2003. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see . */ #include #include #include #include #include #include #include #include #include static void __sem_wait_32_finish (struct new_sem *sem); static void __sem_wait_cleanup (void *arg) { struct new_sem *sem = (struct new_sem *) arg; __sem_wait_32_finish (sem); } /* Wait until at least one token is available, possibly with a timeout. This is in a separate function in order to make sure gcc puts the call site into an exception region, and thus the cleanups get properly run. TODO still necessary? Other futex_wait users don't seem to need it. */ static int __attribute__ ((noinline)) do_futex_wait (struct new_sem *sem, const struct timespec *abstime) { int err; err = futex_abstimed_wait_cancelable (&sem->value, SEM_NWAITERS_MASK, abstime, sem->private); return err; } /* Fast path: Try to grab a token without blocking. */ static int __new_sem_wait_fast (struct new_sem *sem, int definitive_result) { unsigned int v; int ret = 0; __sparc32_atomic_do_lock24(&sem->pad); v = sem->value; if ((v >> SEM_VALUE_SHIFT) == 0) ret = -1; else sem->value = v - (1 << SEM_VALUE_SHIFT); __sparc32_atomic_do_unlock24(&sem->pad); return ret; } /* Slow path that blocks. */ static int __attribute__ ((noinline)) __new_sem_wait_slow (struct new_sem *sem, const struct timespec *abstime) { unsigned int v; int err = 0; __sparc32_atomic_do_lock24(&sem->pad); sem->nwaiters++; pthread_cleanup_push (__sem_wait_cleanup, sem); /* Wait for a token to be available. Retry until we can grab one. */ v = sem->value; do { if (!(v & SEM_NWAITERS_MASK)) sem->value = v | SEM_NWAITERS_MASK; /* If there is no token, wait. */ if ((v >> SEM_VALUE_SHIFT) == 0) { __sparc32_atomic_do_unlock24(&sem->pad); err = do_futex_wait(sem, abstime); if (err == ETIMEDOUT || err == EINTR) { __set_errno (err); err = -1; goto error; } err = 0; __sparc32_atomic_do_lock24(&sem->pad); /* We blocked, so there might be a token now. */ v = sem->value; } } /* If there is no token, we must not try to grab one. */ while ((v >> SEM_VALUE_SHIFT) == 0); sem->value = v - (1 << SEM_VALUE_SHIFT); __sparc32_atomic_do_unlock24(&sem->pad); error: pthread_cleanup_pop (0); __sem_wait_32_finish (sem); return err; } /* Stop being a registered waiter (non-64b-atomics code only). */ static void __sem_wait_32_finish (struct new_sem *sem) { __sparc32_atomic_do_lock24(&sem->pad); if (--sem->nwaiters == 0) sem->value &= ~SEM_NWAITERS_MASK; __sparc32_atomic_do_unlock24(&sem->pad); }