diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/thread/pthread_cond_timedwait.c | 62 | 
1 files changed, 22 insertions, 40 deletions
| diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c index 7aaba954..52e306b2 100644 --- a/src/thread/pthread_cond_timedwait.c +++ b/src/thread/pthread_cond_timedwait.c @@ -10,12 +10,10 @@   * degenerate list of one member.   *   * Waiter lists attached to the condition variable itself are - * protected by the lock on the cv. Detached waiter lists are - * protected by the associated mutex. The hand-off between protections - * is handled by a "barrier" lock in each node, which disallows - * signaled waiters from making forward progress to the code that will - * access the list using the mutex until the list is in a consistent - * state and the cv lock as been released. + * protected by the lock on the cv. Detached waiter lists are never + * modified again, but can only be traversed in reverse order, and are + * protected by the "barrier" locks in each node, which are unlocked + * in turn to control wake order.   *   * Since process-shared cond var semantics do not necessarily allow   * one thread to see another's automatic storage (they may be in @@ -58,7 +56,7 @@ enum {  static void unwait(void *arg)  { -	struct waiter *node = arg, *p; +	struct waiter *node = arg;  	if (node->shared) {  		pthread_cond_t *c = node->cond; @@ -91,49 +89,34 @@ static void unwait(void *arg)  			if (a_fetch_add(node->notify, -1)==1)  				__wake(node->notify, 1, 1);  		} +	} else { +		/* Lock barrier first to control wake order. */ +		lock(&node->barrier);  	}  	node->mutex_ret = pthread_mutex_lock(node->mutex);  	if (oldstate == WAITING) return; -	/* If the mutex can't be locked, we're in big trouble because -	 * it's all that protects access to the shared list state. -	 * In order to prevent catastrophic stack corruption from -	 * unsynchronized access, simply deadlock. */ -	if (node->mutex_ret && node->mutex_ret != EOWNERDEAD) -		for (;;) lock(&(int){0}); - -	/* Wait until control of the list has been handed over from -	 * the cv lock (signaling thread) to the mutex (waiters). */ -	lock(&node->barrier); -  	/* If this thread was requeued to the mutex, undo the extra  	 * waiter count that was added to the mutex. */  	if (node->requeued) a_dec(&node->mutex->_m_waiters); -	/* Find a thread to requeue to the mutex, starting from the -	 * end of the list (oldest waiters). */ -	for (p=node; p->next; p=p->next); -	if (p==node) p=node->prev; -	for (; p && p->requeued; p=p->prev); -	if (p==node) p=node->prev; -	if (p) { -		p->requeued = 1; +	/* Unlock the barrier that's holding back the next waiter, +	 * and either wake it or requeue it to the mutex. */ +	if (node->prev) { +		unlock(&node->prev->barrier); +		node->prev->requeued = 1;  		a_inc(&node->mutex->_m_waiters);  		/* The futex requeue command cannot requeue from  		 * private to shared, so for process-shared mutexes,  		 * simply wake the target. */  		int wake = node->mutex->_m_type & 128; -		__syscall(SYS_futex, &p->state, FUTEX_REQUEUE|128, +		__syscall(SYS_futex, &node->prev->state, FUTEX_REQUEUE|128,  			wake, 1, &node->mutex->_m_lock) != -EINVAL -		|| __syscall(SYS_futex, &p->state, FUTEX_REQUEUE, +		|| __syscall(SYS_futex, &node->prev->state, FUTEX_REQUEUE,  			0, 1, &node->mutex->_m_lock);  	} - -	/* Remove this thread from the list. */ -	if (node->next) node->next->prev = node->prev; -	if (node->prev) node->prev->next = node->next;  }  int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts) @@ -181,7 +164,7 @@ int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict  int __private_cond_signal(pthread_cond_t *c, int n)  { -	struct waiter *p, *q=0; +	struct waiter *p, *first=0;  	int ref = 0, cur;  	lock(&c->_c_lock); @@ -196,7 +179,7 @@ int __private_cond_signal(pthread_cond_t *c, int n)  			p->notify = &ref;  		} else {  			n--; -			if (!q) q=p; +			if (!first) first=p;  		}  	}  	/* Split the list, leaving any remainder on the cv. */ @@ -214,12 +197,11 @@ int __private_cond_signal(pthread_cond_t *c, int n)  	 * signaled threads to proceed. */  	while ((cur = ref)) __wait(&ref, 0, cur, 1); -	/* Wake the first signaled thread and unlock the per-waiter -	 * barriers preventing their forward progress. */ -	for (p=q; p; p=q) { -		q = p->prev; -		if (!p->next) __wake(&p->state, 1, 1); -		unlock(&p->barrier); +	/* Allow first signaled waiter, if any, to proceed. */ +	if (first) { +		__wake(&first->state, 1, 1); +		unlock(&first->barrier);  	} +  	return 0;  } | 
