FreeBSD/sys/kern/kern_rwlock.c
/*
 * This function is called if the first try at releasing a write lock failed.
 * This means that one of the 2 waiter bits must be set indicating that at
 * least one thread is waiting on this lock.
 */
This function is called if the first try at releasing a write lock failed.
This means that one of the 2 waiter bits must be set indicating that at
least one thread is waiting on this lock.
This function is called if the first try at releasing a write lock failed . This means that one of the 2 waiter bits must be set indicating that at least one thread is waiting on this lock .
void
__rw_wunlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file,
    int line)
{
	struct rwlock *rw;
	struct turnstile *ts;
	uintptr_t v;
	int queue;
	if (SCHEDULER_STOPPED())
		return;
	rw = rwlock2rw(c);
	if (rw_wlocked(rw) && rw_recursed(rw)) {
		rw->rw_recurse--;
		if (LOCK_LOG_TEST(&rw->lock_object, 0))
			CTR2(KTR_LOCK, "%s: %p unrecursing", __func__, rw);
		return;
	}
	KASSERT(rw->rw_lock & (RW_LOCK_READ_WAITERS | RW_LOCK_WRITE_WAITERS),
	    ("%s: neither of the waiter flags are set", __func__));
	if (LOCK_LOG_TEST(&rw->lock_object, 0))
		CTR2(KTR_LOCK, "%s: %p contested", __func__, rw);
	turnstile_chain_lock(&rw->lock_object);
	ts = turnstile_lookup(&rw->lock_object);
	MPASS(ts != NULL);
	v = RW_UNLOCKED;
	if (rw->rw_lock & RW_LOCK_WRITE_WAITERS) {
		queue = TS_EXCLUSIVE_QUEUE;
		v |= (rw->rw_lock & RW_LOCK_READ_WAITERS);
	} else
		queue = TS_SHARED_QUEUE;
	if (LOCK_LOG_TEST(&rw->lock_object, 0))
		CTR3(KTR_LOCK, "%s: %p waking up %s waiters", __func__, rw,
		    queue == TS_SHARED_QUEUE ? "read" : "write");
	turnstile_broadcast(ts, queue);
	atomic_store_rel_ptr(&rw->rw_lock, v);
	turnstile_unpend(ts, TS_EXCLUSIVE_LOCK);
	turnstile_chain_unlock(&rw->lock_object);
}
void __rw_wunlock_hard ( volatile uintptr_t * c , uintptr_t tid , const char * file , int line ) { struct rwlock * rw ; struct turnstile * ts ; uintptr_t v ; int queue ; if ( SCHEDULER_STOPPED ( ) ) return ; rw = rwlock2rw ( c ) ; if ( rw_wlocked ( rw ) && rw_recursed ( rw ) ) { rw -> rw_recurse -- ; if ( LOCK_LOG_TEST ( & rw -> lock_object , 0 ) ) CTR2 ( KTR_LOCK , " " , __func__ , rw ) ; return ; } KASSERT ( rw -> rw_lock & ( RW_LOCK_READ_WAITERS | RW_LOCK_WRITE_WAITERS ) , ( " " , __func__ ) ) ; if ( LOCK_LOG_TEST ( & rw -> lock_object , 0 ) ) CTR2 ( KTR_LOCK , " " , __func__ , rw ) ; turnstile_chain_lock ( & rw -> lock_object ) ; ts = turnstile_lookup ( & rw -> lock_object ) ; MPASS ( ts != NULL ) ; v = RW_UNLOCKED ; if ( rw -> rw_lock & RW_LOCK_WRITE_WAITERS ) { queue = TS_EXCLUSIVE_QUEUE ; v |= ( rw -> rw_lock & RW_LOCK_READ_WAITERS ) ; } else queue = TS_SHARED_QUEUE ; if ( LOCK_LOG_TEST ( & rw -> lock_object , 0 ) ) CTR3 ( KTR_LOCK , " " , __func__ , rw , queue == TS_SHARED_QUEUE ? " " : " " ) ; turnstile_broadcast ( ts , queue ) ; atomic_store_rel_ptr ( & rw -> rw_lock , v ) ; turnstile_unpend ( ts , TS_EXCLUSIVE_LOCK ) ; turnstile_chain_unlock ( & rw -> lock_object ) ; }
This function is called if the first try at releasing a write lock failed.
This function is called if the first try at releasing a write lock failed .
/*
	 * Use the same algo as sx locks for now.  Prefer waking up shared
	 * waiters if we have any over writers.  This is probably not ideal.
	 *
	 * 'v' is the value we are going to write back to rw_lock.  If we
	 * have waiters on both queues, we need to preserve the state of
	 * the waiter flag for the queue we don't wake up.  For now this is
	 * hardcoded for the algorithm mentioned above.
	 *
	 * In the case of both readers and writers waiting we wakeup the
	 * readers but leave the RW_LOCK_WRITE_WAITERS flag set.  If a
	 * new writer comes in before a reader it will claim the lock up
	 * above.  There is probably a potential priority inversion in
	 * there that could be worked around either by waking both queues
	 * of waiters or doing some complicated lock handoff gymnastics.
	 */ /* Wake up all waiters for the specific queue. */