[BACK]Return to kern_condvar.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / kern

File: [cvs.NetBSD.org] / src / sys / kern / kern_condvar.c (download)

Revision 1.17, Mon Apr 28 20:24:02 2008 UTC (15 years, 11 months ago) by martin
Branch: MAIN
CVS Tags: yamt-pf42-base2, yamt-nfs-mp-base2, hpcarm-cleanup-nbase
Branch point for: wrstuden-revivesa
Changes since 1.16: +2 -9 lines

Remove clause 3 and 4 from TNF licenses

/*	$NetBSD: kern_condvar.c,v 1.17 2008/04/28 20:24:02 martin Exp $	*/

/*-
 * Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Andrew Doran.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Kernel condition variable implementation, modeled after those found in
 * Solaris, a description of which can be found in:
 *
 *	Solaris Internals: Core Kernel Architecture, Jim Mauro and
 *	    Richard McDougall.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_condvar.c,v 1.17 2008/04/28 20:24:02 martin Exp $");

#include <sys/param.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/systm.h>
#include <sys/condvar.h>
#include <sys/sleepq.h>

static u_int	cv_unsleep(lwp_t *, bool);

static syncobj_t cv_syncobj = {
	SOBJ_SLEEPQ_SORTED,
	cv_unsleep,
	sleepq_changepri,
	sleepq_lendpri,
	syncobj_noowner,
};

static const char deadcv[] = "deadcv";

/*
 * cv_init:
 *
 *	Initialize a condition variable for use.
 */
void
cv_init(kcondvar_t *cv, const char *wmesg)
{

	KASSERT(wmesg != NULL);

	cv->cv_wmesg = wmesg;
	cv->cv_waiters = 0;
}

/*
 * cv_destroy:
 *
 *	Tear down a condition variable.
 */
void
cv_destroy(kcondvar_t *cv)
{

#ifdef DIAGNOSTIC
	KASSERT(cv_is_valid(cv));
	cv->cv_wmesg = deadcv;
	cv->cv_waiters = -3;
#endif
}

/*
 * cv_enter:
 *
 *	Look up and lock the sleep queue corresponding to the given
 *	condition variable, and increment the number of waiters.
 */
static inline sleepq_t *
cv_enter(kcondvar_t *cv, kmutex_t *mtx, lwp_t *l)
{
	sleepq_t *sq;

	KASSERT(cv_is_valid(cv));
	KASSERT((l->l_pflag & LP_INTR) == 0 || panicstr != NULL);

	l->l_cv_signalled = 0;
	l->l_kpriority = true;
	sq = sleeptab_lookup(&sleeptab, cv);
	cv->cv_waiters++;
	sleepq_enter(sq, l);
	sleepq_enqueue(sq, cv, cv->cv_wmesg, &cv_syncobj);
	mutex_exit(mtx);

	return sq;
}

/*
 * cv_exit:
 *
 *	After resuming execution, check to see if we have been restarted
 *	as a result of cv_signal().  If we have, but cannot take the
 *	wakeup (because of eg a pending Unix signal or timeout) then try
 *	to ensure that another LWP sees it.  This is necessary because
 *	there may be multiple waiters, and at least one should take the
 *	wakeup if possible.
 */
static inline int
cv_exit(kcondvar_t *cv, kmutex_t *mtx, lwp_t *l, const int error)
{

	mutex_enter(mtx);
	if (__predict_false(error != 0) && l->l_cv_signalled != 0)
		cv_signal(cv);

	KASSERT(cv_is_valid(cv));

	return error;
}

/*
 * cv_unsleep:
 *
 *	Remove an LWP from the condition variable and sleep queue.  This
 *	is called when the LWP has not been awoken normally but instead
 *	interrupted: for example, when a signal is received.  Must be
 *	called with the LWP locked, and must return it unlocked.
 */
static u_int
cv_unsleep(lwp_t *l, bool cleanup)
{
	kcondvar_t *cv;

	cv = (kcondvar_t *)(uintptr_t)l->l_wchan;

	KASSERT(l->l_wchan != NULL);
	KASSERT(lwp_locked(l, l->l_sleepq->sq_mutex));
	KASSERT(cv_is_valid(cv));
	KASSERT(cv->cv_waiters > 0);

	cv->cv_waiters--;
	return sleepq_unsleep(l, cleanup);
}

/*
 * cv_wait:
 *
 *	Wait non-interruptably on a condition variable until awoken.
 */
void
cv_wait(kcondvar_t *cv, kmutex_t *mtx)
{
	lwp_t *l = curlwp;
	sleepq_t *sq;

	KASSERT(mutex_owned(mtx));

	if (sleepq_dontsleep(l)) {
		(void)sleepq_abort(mtx, 0);
		return;
	}

	sq = cv_enter(cv, mtx, l);
	(void)sleepq_block(0, false);
	(void)cv_exit(cv, mtx, l, 0);
}

/*
 * cv_wait_sig:
 *
 *	Wait on a condition variable until a awoken or a signal is received. 
 *	Will also return early if the process is exiting.  Returns zero if
 *	awoken normallly, ERESTART if a signal was received and the system
 *	call is restartable, or EINTR otherwise.
 */
int
cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx)
{
	lwp_t *l = curlwp;
	sleepq_t *sq;
	int error;

	KASSERT(mutex_owned(mtx));

	if (sleepq_dontsleep(l))
		return sleepq_abort(mtx, 0);

	sq = cv_enter(cv, mtx, l);
	error = sleepq_block(0, true);
	return cv_exit(cv, mtx, l, error);
}

/*
 * cv_timedwait:
 *
 *	Wait on a condition variable until awoken or the specified timeout
 *	expires.  Returns zero if awoken normally or EWOULDBLOCK if the
 *	timeout expired.
 */
int
cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int timo)
{
	lwp_t *l = curlwp;
	sleepq_t *sq;
	int error;

	KASSERT(mutex_owned(mtx));

	if (sleepq_dontsleep(l))
		return sleepq_abort(mtx, 0);

	sq = cv_enter(cv, mtx, l);
	error = sleepq_block(timo, false);
	return cv_exit(cv, mtx, l, error);
}

/*
 * cv_timedwait_sig:
 *
 *	Wait on a condition variable until a timeout expires, awoken or a
 *	signal is received.  Will also return early if the process is
 *	exiting.  Returns zero if awoken normallly, EWOULDBLOCK if the
 *	timeout expires, ERESTART if a signal was received and the system
 *	call is restartable, or EINTR otherwise.
 */
int
cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mtx, int timo)
{
	lwp_t *l = curlwp;
	sleepq_t *sq;
	int error;

	KASSERT(mutex_owned(mtx));

	if (sleepq_dontsleep(l))
		return sleepq_abort(mtx, 0);

	sq = cv_enter(cv, mtx, l);
	error = sleepq_block(timo, true);
	return cv_exit(cv, mtx, l, error);
}

/*
 * cv_signal:
 *
 *	Wake the highest priority LWP waiting on a condition variable.
 *	Must be called with the interlocking mutex held.
 */
void
cv_signal(kcondvar_t *cv)
{
	lwp_t *l;
	sleepq_t *sq;

	KASSERT(cv_is_valid(cv));

	if (cv->cv_waiters == 0)
		return;

	/*
	 * cv->cv_waiters may be stale and have dropped to zero, but
	 * while holding the interlock (the mutex passed to cv_wait()
	 * and similar) we will see non-zero values when it matters.
	 */

	sq = sleeptab_lookup(&sleeptab, cv);
	if (cv->cv_waiters != 0) {
		cv->cv_waiters--;
		l = sleepq_wake(sq, cv, 1);
		l->l_cv_signalled = 1;
	} else
		sleepq_unlock(sq);

	KASSERT(cv_is_valid(cv));
}

/*
 * cv_broadcast:
 *
 *	Wake all LWPs waiting on a condition variable.  Must be called
 *	with the interlocking mutex held.
 */
void
cv_broadcast(kcondvar_t *cv)
{
	sleepq_t *sq;
	u_int cnt;

	KASSERT(cv_is_valid(cv));

	if (cv->cv_waiters == 0)
		return;

	sq = sleeptab_lookup(&sleeptab, cv);
	if ((cnt = cv->cv_waiters) != 0) {
		cv->cv_waiters = 0;
		sleepq_wake(sq, cv, cnt);
	} else
		sleepq_unlock(sq);

	KASSERT(cv_is_valid(cv));
}

/*
 * cv_wakeup:
 *
 *	Wake all LWPs waiting on a condition variable.  For cases
 *	where the address may be waited on by mtsleep()/tsleep().
 *	Not a documented call.
 */
void
cv_wakeup(kcondvar_t *cv)
{
	sleepq_t *sq;

	KASSERT(cv_is_valid(cv));

	sq = sleeptab_lookup(&sleeptab, cv);
	cv->cv_waiters = 0;
	sleepq_wake(sq, cv, (u_int)-1);

	KASSERT(cv_is_valid(cv));
}

/*
 * cv_has_waiters:
 *
 *	For diagnostic assertions: return non-zero if a condition
 *	variable has waiters.
 */
bool
cv_has_waiters(kcondvar_t *cv)
{

	/* No need to interlock here */
	return cv->cv_waiters != 0;
}

/*
 * cv_is_valid:
 *
 *	For diagnostic assertions: return non-zero if a condition
 *	variable appears to be valid.  No locks need be held.
 */
bool
cv_is_valid(kcondvar_t *cv)
{

	if (cv->cv_wmesg == deadcv || cv->cv_wmesg == NULL)
		return false;
	if ((cv->cv_waiters & 0xff000000) != 0) {
		/* Arbitrary: invalid number of waiters. */
		return false;
	}
	return cv->cv_waiters >= 0;
}