/* Copyright (C) 2008 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by David Bartley , 2008. 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include DECLARE_INLINE_SYSCALL (int, lwp_cond_wait, cond_t *cv, mutex_t *mp, struct timespec *tsp, int check_park); struct _condvar_cleanup_buffer { int oldtype; cond_t *cond; mutex_t *mutex; int recursive; uint8_t old_mutex_rcount; }; void __attribute__ ((visibility ("hidden"))) __condvar_cleanup (void *arg) { struct _condvar_cleanup_buffer *cbuffer = (struct _condvar_cleanup_buffer *) arg; mutex_t *mutex = cbuffer->mutex; /* Note: we don't whether the mutex was unlocked by the kernel or not. */ int errval = 0; while (1) { if (MUTEX_IS_OWNER (mutex)) { /* The lock is held by us (we didn't get signaled). */ break; } else { /* The mutex was unlocked so we claim it, */ errval = mutex_lock (mutex); break; } } /* Restore the mutex_rcount. */ if (errval == 0) mutex->mutex_rcount = cbuffer->old_mutex_rcount; /* The condition variable is no longer using the mutex. */ atomic_decrement (&mutex->mutex_cond_waiters); } int __cond_reltimedwait_internal (cond, mutex, reltime, cancel) cond_t *cond; mutex_t *mutex; struct timespec *reltime; int cancel; { struct _pthread_cleanup_buffer buffer; struct _condvar_cleanup_buffer cbuffer = { .recursive = 0 }; /* Reject invalid timeouts. */ if (PAST_TIMESPEC (reltime)) return ETIME; else if (INVALID_TIMESPEC (reltime)) return EINVAL; if ((mutex->mutex_type & LOCK_ERRORCHECK) && MUTEX_NOT_OWNER (mutex)) { /* Error checking: lock not held by this thread. */ return EPERM; } else if ((mutex->mutex_type & LOCK_RECURSIVE) && MUTEX_IS_OWNER (mutex) && mutex->mutex_rcount > 0) { /* Recursively held lock. XXX: Using recursive mutexes with condition variables is undefined; we do what sun's libc does, namely fully release the lock. */ cbuffer.recursive = 1; cbuffer.old_mutex_rcount = mutex->mutex_rcount; mutex->mutex_rcount = 0; } /* Mark the mutex as still in use. */ atomic_increment (&mutex->mutex_cond_waiters); /* Prepare structure passed to cancellation handler. */ cbuffer.cond = cond; cbuffer.mutex = mutex; if (cancel) { /* Before we block we enable cancellation. Therefore we have to install a cancellation handler. */ __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer); /* Enable asynchronous cancellation. Required by the standard. */ cbuffer.oldtype = __pthread_enable_asynccancel (); } int errval = INLINE_SYSCALL (lwp_cond_wait, 4, cond, mutex, reltime, 1); if (cancel) { /* Disable asynchronous cancellation. */ __pthread_disable_asynccancel (cbuffer.oldtype); /* The cancellation handling is back to normal, remove the handler. */ __pthread_cleanup_pop (&buffer, 0); } /* Re-acquire the lock. The docs say we must always re-acquire so we don't use __mutex_timedlock. Note that even if the above wait fails the kernel always unlocks the mutex. */ int errval2 = mutex_lock (mutex); if (errval2 == EOWNERDEAD) errval = errval2; else if (errval == 0) errval = errval2; /* Restore the mutex_rcount. */ if ((errval2 == 0 || errval2 == EOWNERDEAD) && cbuffer.recursive) mutex->mutex_rcount = cbuffer.old_mutex_rcount; /* The condition variable is no longer using the mutex. */ atomic_decrement (&mutex->mutex_cond_waiters); return errval; } int cond_reltimedwait (cond, mutex, reltime) cond_t *cond; mutex_t *mutex; struct timespec *reltime; { return __cond_reltimedwait_internal (cond, mutex, reltime, 1); } weak_alias (cond_reltimedwait, _lwp_cond_reltimedwait)