* Copyright (c) 1991 Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* The Mach Operating System project at Carnegie-Mellon University.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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
* @(#)kern_lock.c 7.4 (Berkeley) 4/21/91
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
* Authors: Avadis Tevanian, Jr., Michael Wayne Young
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
* Carnegie Mellon requests users of this software to return to
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
* Locking primitives implementation
#define current_thread() ((thread_t)&curproc->p_thread)
* Provide reader/writer sychronization.
* Simple interlock on a bit. Readers first interlock
* increment the reader count, then let go. Writers hold
* the interlock (thus preventing further readers), and
* wait for already-accepted readers to go away.
* The simple-lock routines are the primitives out of which
* the lock package is built. The implementation is left
* to the machine-dependent code.
* A sample implementation of simple locks.
* boolean_t test_and_set(boolean_t *)
* indivisibly sets the boolean to TRUE
* and returns its old value
* and that setting a boolean to FALSE is indivisible.
* simple_lock_init initializes a simple lock. A simple lock
* may only be used for exclusive locks.
while (test_and_set((boolean_t
*)l
))
boolean_t
simple_lock_try(l
)
return (!test_and_set((boolean_t
*)l
));
int lock_wait_time
= 100;
* It is silly to spin on a uni-processor as if we
* thought something magical would happen to the
* want_write bit while we are executing.
* Initialize a lock; required before use.
* Note that clients declare the "struct lock"
* variables and then initialize them, rather
* than getting a new one from this module.
void lock_init(l
, can_sleep
)
bzero(l
, sizeof(lock_data_t
));
simple_lock_init(&l
->interlock
);
l
->can_sleep
= can_sleep
;
l
->thread
= (char *)-1; /* XXX */
void lock_sleepable(l
, can_sleep
)
simple_lock(&l
->interlock
);
l
->can_sleep
= can_sleep
;
simple_unlock(&l
->interlock
);
* Sleep locks. These use the same data structure and algorithm
* as the spin locks, but the process sleeps while it is waiting
* for the lock. These work on uniprocessor systems.
simple_lock(&l
->interlock
);
if (((thread_t
)l
->thread
) == current_thread()) {
simple_unlock(&l
->interlock
);
* Try to acquire the want_write bit.
if ((i
= lock_wait_time
) > 0) {
simple_unlock(&l
->interlock
);
while (--i
> 0 && l
->want_write
)
simple_lock(&l
->interlock
);
if (l
->can_sleep
&& l
->want_write
) {
thread_sleep((int) l
, &l
->interlock
, FALSE
);
simple_lock(&l
->interlock
);
/* Wait for readers (and upgrades) to finish */
while ((l
->read_count
!= 0) || l
->want_upgrade
) {
if ((i
= lock_wait_time
) > 0) {
simple_unlock(&l
->interlock
);
while (--i
> 0 && (l
->read_count
!= 0 ||
simple_lock(&l
->interlock
);
if (l
->can_sleep
&& (l
->read_count
!= 0 || l
->want_upgrade
)) {
thread_sleep((int) l
, &l
->interlock
, FALSE
);
simple_lock(&l
->interlock
);
simple_unlock(&l
->interlock
);
simple_lock(&l
->interlock
);
if (l
->recursion_depth
!= 0)
simple_unlock(&l
->interlock
);
simple_lock(&l
->interlock
);
if (((thread_t
)l
->thread
) == current_thread()) {
simple_unlock(&l
->interlock
);
while (l
->want_write
|| l
->want_upgrade
) {
if ((i
= lock_wait_time
) > 0) {
simple_unlock(&l
->interlock
);
while (--i
> 0 && (l
->want_write
|| l
->want_upgrade
))
simple_lock(&l
->interlock
);
if (l
->can_sleep
&& (l
->want_write
|| l
->want_upgrade
)) {
thread_sleep((int) l
, &l
->interlock
, FALSE
);
simple_lock(&l
->interlock
);
simple_unlock(&l
->interlock
);
* Routine: lock_read_to_write
* Improves a read-only lock to one with
* write permission. If another reader has
* already requested an upgrade to a write lock,
* no lock is held upon return.
* Returns TRUE if the upgrade *failed*.
boolean_t
lock_read_to_write(l
)
simple_lock(&l
->interlock
);
if (((thread_t
)l
->thread
) == current_thread()) {
simple_unlock(&l
->interlock
);
* Someone else has requested upgrade.
* Since we've released a read lock, wake
simple_unlock(&l
->interlock
);
while (l
->read_count
!= 0) {
if ((i
= lock_wait_time
) > 0) {
simple_unlock(&l
->interlock
);
while (--i
> 0 && l
->read_count
!= 0)
simple_lock(&l
->interlock
);
if (l
->can_sleep
&& l
->read_count
!= 0) {
thread_sleep((int) l
, &l
->interlock
, FALSE
);
simple_lock(&l
->interlock
);
simple_unlock(&l
->interlock
);
void lock_write_to_read(l
)
simple_lock(&l
->interlock
);
if (l
->recursion_depth
!= 0)
simple_unlock(&l
->interlock
);
* Routine: lock_try_write
* Tries to get a write lock.
* Returns FALSE if the lock is not held on return.
boolean_t
lock_try_write(l
)
simple_lock(&l
->interlock
);
if (((thread_t
)l
->thread
) == current_thread()) {
simple_unlock(&l
->interlock
);
if (l
->want_write
|| l
->want_upgrade
|| l
->read_count
) {
simple_unlock(&l
->interlock
);
simple_unlock(&l
->interlock
);
* Tries to get a read lock.
* Returns FALSE if the lock is not held on return.
boolean_t
lock_try_read(l
)
simple_lock(&l
->interlock
);
if (((thread_t
)l
->thread
) == current_thread()) {
simple_unlock(&l
->interlock
);
if (l
->want_write
|| l
->want_upgrade
) {
simple_unlock(&l
->interlock
);
simple_unlock(&l
->interlock
);
* Routine: lock_try_read_to_write
* Improves a read-only lock to one with
* write permission. If another reader has
* already requested an upgrade to a write lock,
* the read lock is still held upon return.
* Returns FALSE if the upgrade *failed*.
boolean_t
lock_try_read_to_write(l
)
simple_lock(&l
->interlock
);
if (((thread_t
)l
->thread
) == current_thread()) {
simple_unlock(&l
->interlock
);
simple_unlock(&l
->interlock
);
while (l
->read_count
!= 0) {
thread_sleep((int) l
, &l
->interlock
, FALSE
);
simple_lock(&l
->interlock
);
simple_unlock(&l
->interlock
);
* Allow a process that has a lock for write to acquire it
* recursively (for read, write, or update).
void lock_set_recursive(l
)
simple_lock(&l
->interlock
);
panic("lock_set_recursive: don't have write lock");
l
->thread
= (char *) current_thread();
simple_unlock(&l
->interlock
);
* Prevent a lock from being re-acquired.
void lock_clear_recursive(l
)
simple_lock(&l
->interlock
);
if (((thread_t
) l
->thread
) != current_thread()) {
panic("lock_clear_recursive: wrong thread");
if (l
->recursion_depth
== 0)
l
->thread
= (char *)-1; /* XXX */
simple_unlock(&l
->interlock
);