.\" $NetBSD: rwlock.9,v 1.19 2018/12/10 20:12:36 jdolecek Exp $
.\"
.\" Copyright (c) 2006, 2007, 2009 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.
.\"
.Dd December 10, 2018
.Dt RWLOCK 9
.Os
.Sh NAME
.Nm rw ,
.Nm rw_init ,
.Nm rw_destroy ,
.Nm rw_enter ,
.Nm rw_exit ,
.Nm rw_tryenter ,
.Nm rw_tryupgrade ,
.Nm rw_downgrade ,
.Nm rw_read_held ,
.Nm rw_write_held ,
.Nm rw_lock_held
.Nd reader / writer lock primitives
.Sh SYNOPSIS
.In sys/rwlock.h
.Ft void
.Fn rw_init "krwlock_t *rw"
.Ft void
.Fn rw_destroy "krwlock_t *rw"
.Ft void
.Fn rw_enter "krwlock_t *rw" "const krw_t op"
.Ft void
.Fn rw_exit "krwlock_t *rw"
.Ft int
.Fn rw_tryenter "krwlock_t *rw" "const krw_t op"
.Ft int
.Fn rw_tryupgrade "krwlock_t *rw"
.Ft void
.Fn rw_downgrade "krwlock_t *rw"
.Ft int
.Fn rw_read_held "krwlock_t *rw"
.Ft int
.Fn rw_write_held "krwlock_t *rw"
.Ft int
.Fn rw_lock_held "krwlock_t *rw"
.Pp
.Cd "options DIAGNOSTIC"
.Cd "options LOCKDEBUG"
.Sh DESCRIPTION
Reader / writer locks (RW locks) are used in the kernel to synchronize access
to an object among LWPs (lightweight processes) and soft interrupt handlers.
.Pp
In addition to the capabilities provided by mutexes, RW locks distinguish
between read (shared) and write (exclusive) access.
.Pp
RW locks are in one of three distinct states at any given time:
.Bl -tag -width cdosrunrundo
.It Dv Unlocked
The lock is not held.
.It Dv Read locked
The lock holders intend to read the protected object.
Multiple callers may hold a RW lock with
.Dq read intent
simultaneously.
.It Dv Write locked
The lock holder intends to update the protected object.
Only one caller may hold a RW lock with
.Dq write intent .
.El
.Pp
The
.Vt krwlock_t
type provides storage for the RW lock object.
This should be treated as an opaque object and not examined directly by
consumers.
.Pp
Note that these interfaces must not be used from a hardware
interrupt handler.
.Sh OPTIONS AND MACROS
.Bl -tag -width abcd
.It Cd "options DIAGNOSTIC"
.Pp
Kernels compiled with the
.Dv DIAGNOSTIC
option perform basic sanity checks on RW lock operations.
.It Cd "options LOCKDEBUG"
.Pp
Kernels compiled with the
.Dv LOCKDEBUG
option perform potentially CPU intensive sanity checks
on RW lock operations.
.El
.Sh FUNCTIONS
.Bl -tag -width abcd
.It Fn rw_init "rw"
.Pp
Initialize a lock for use.
No other operations can be performed on the lock until it has been
initialized.
.It Fn rw_destroy "rw"
.Pp
Release resources used by a lock.
The lock may not be used after it has been destroyed.
.It Fn rw_enter "rw" "op"
.Pp
If
.Dv RW_READER
is specified as the argument to
.Fa op ,
acquire a read lock.
If the lock is write held, the caller will block and not return until the
hold is acquired.
Callers must not recursively acquire read locks.
.Pp
If
.Dv RW_WRITER
is specified, acquire a write lock.
If the lock is already held, the caller will block and not return until the
hold is acquired.
.Pp
RW locks and other types of locks must always be acquired in a
consistent order with respect to each other.
Otherwise, the potential for system deadlock exists.
.It Fn rw_exit "rw"
.Pp
Release a lock.
The lock must have been previously acquired by the caller.
.It Fn rw_tryenter "rw" "op"
.Pp
Try to acquire a lock, but do not block if the lock is already held.
If the lock is acquired successfully, return non-zero.
Otherwise, return zero.
.Pp
Valid arguments to
.Fa op
are
.Dv RW_READER
or
.Dv RW_WRITER .
.It Fn rw_tryupgrade "rw"
.Pp
Try to upgrade a lock from one read hold to a write hold.
If the lock is upgraded successfully, returns non-zero.
Otherwise, returns zero.
.It Fn rw_downgrade "rw"
.Pp
Downgrade a lock from a write hold to a read hold.
.It Fn rw_write_held "rw"
.Pp
Return non-zero if write lock is held by current lwp.
Otherwise, return zero.
.It Fn rw_read_held "rw"
Returns non-zero if read lock is held by any lwp.
Otherwise, return zero.
.It Fn rw_lock_held "rw"
.Pp
Returns non-zero if either read or write lock is held by any lwp.
Otherwise, return zero.
.Pp
Functions
.Fn rw_write_held ,
.Fn rw_read_held ,
and
.Fn rw_lock_held
must never be used to make locking decisions at run time:
they are provided only for diagnostic purposes.
They are also not atomic, hence they should only be used to assert
that lock is held.
The only exception is
.Fn rw_write_held ,
which can be also used safely to assert that write lock is NOT currently
held by current lwp.
.El
.Sh PERFORMANCE CONSIDERATIONS
RW locks are subject to high cache contention on multiprocessor systems,
and scale poorly when the write:read ratio is not strongly in favour of
readers.
Ideally, RW locks should only be used in settings when the following three
conditions are met:
.Bl -bullet
.It
The data object(s) protected by the RW lock are read much more frequently
than written.
.It
The read-side hold time for the RW lock is long (in the order of thousands
of processor clock cycles).
.It
Strong synchronization semantics are required: there is no scope for
lockless, lazy or optimistic synchronization.
.El
.Pp
Generally speaking, it is better to organise code paths and/or
data flows such that fewer and weaker synchronization points are required
to ensure correct operation.
.Sh CODE REFERENCES
The core of the RW lock implementation is in
.Pa sys/kern/kern_rwlock.c .
.Pp
The header file
.Pa sys/sys/rwlock.h
describes the public interface, and interfaces that machine-dependent
code must provide to support RW locks.
.Sh SEE ALSO
.Xr membar_ops 3 ,
.Xr lockstat 8 ,
.Xr condvar 9 ,
.Xr mutex 9
.Rs
.%A Jim Mauro
.%A Richard McDougall
.%T Solaris Internals: Core Kernel Architecture
.%I Prentice Hall
.%D 2001
.%O ISBN 0-13-022496-0
.Re
.Sh HISTORY
The RW lock primitives first appeared in
.Nx 5.0 .