rwlock.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "lint.h"
#include "thr_uberdata.h"
#define TRY_FLAG 0x10
#define READ_LOCK 0
#define WRITE_LOCK 1
/*
*/
static readlock_t *
{
else {
nlocks = 1;
}
return (readlockp);
}
if (remembered != NULL) {
return (remembered);
}
/*
* No entry available. Allocate more space, converting the single
* readlock_t entry into an array of readlock_t entries if necessary.
*/
/*
* Initial allocation of the readlock_t array.
* Convert the single entry into an array.
*/
/*
* The single readlock_t becomes the first entry in the array.
*/
/*
* Return the next available entry in the array.
*/
return (readlockp);
}
/*
* Reallocate the array, double the size each time.
*/
nlocks * sizeof (readlock_t));
/*
* Return the next available entry in the newly allocated array.
*/
return (readlockp);
}
/*
* Free the array of rwlocks held for reading.
*/
void
{
ulwp->ul_rdlocks = 0;
}
/*
* Check if a reader version of the lock is held by the current thread.
* rw_read_is_held() is private to libc.
*/
int
{
/* quick answer */
return (0);
} else if (rwlp->rwlock_readers <= 0) {
return (0);
}
/*
* The lock is held for reading by some thread.
* Search our array of rwlocks held for reading for a match.
*/
else {
nlocks = 1;
}
return (0);
}
/*
* Check if a writer version of the lock is held by the current thread.
* rw_write_is_held() is private to libc.
*/
int
{
/* USYNC_THREAD */
}
/* ARGSUSED2 */
int
{
return (EINVAL);
/*
* Once reinitialized, we can no longer be holding a read or write lock.
* We can do nothing about other threads that are holding read locks.
*/
if (rw_read_is_held(rwlp))
rwlp->rwlock_readers = 0;
return (0);
}
int
{
/*
* Once destroyed, we can no longer be holding a read or write lock.
* We can do nothing about other threads that are holding read locks.
*/
if (rw_read_is_held(rwlp))
rwlp->rwlock_magic = 0;
return (0);
}
/*
* Wake up the next thread sleeping on the rwlock queue and then
* drop the queue lock. Return non-zero if we wake up someone.
*
* This is called whenever a thread releases the lock and whenever a
* thread successfully or unsuccessfully attempts to acquire the lock.
* (Basically, whenever the state of the queue might have changed.)
*
* We wake up at most one thread. If there are more threads to be
* awakened, the next one will be waked up by the thread we wake up.
* This ensures that queued threads will acquire the lock in priority
* order and that queued writers will take precedence over queued
* readers of the same priority.
*/
static int
{
int more;
/*
* The lock is free or at least is available to readers
* and there are (or might be) waiters on the queue.
*/
if (rwlp->rwlock_readers != 0 &&
rwlp->rwlock_mwaiters = 0;
rwlp->rwlock_mwaiters = 0;
else {
(void) __lwp_unpark(lwpid);
return (1);
}
}
}
return (0);
}
/*
* Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
* and trywrlock for process-shared (USYNC_PROCESS) rwlocks.
*
* Note: if the lock appears to be contended we call __lwp_rwlock_rdlock()
* or __lwp_rwlock_wrlock() holding the mutex. These return with the mutex
* released, and if they need to sleep will release the mutex first. In the
* event of a spurious wakeup, these will return EAGAIN (because it is much
* easier for us to re-acquire the mutex here).
*/
int
{
int try_flag;
int error = 0;
if (!try_flag) {
}
do {
break;
/*
* We are a reader.
*/
if ((*rwstate & ~URW_READERS_MASK) == 0) {
(*rwstate)++;
} else if (try_flag) {
if (*rwstate & URW_WRITE_LOCKED) {
(void) _private_mutex_unlock(
} else {
/*
* We have a higher priority than any
* queued waiters, or the waiters bit
* may be inaccurate. Only the kernel
* knows for sure.
*/
rwlp->rwlock_mowner = 0;
rwlp->rwlock_mownerpid = 0;
}
} else {
rwlp->rwlock_mowner = 0;
rwlp->rwlock_mownerpid = 0;
}
} else {
/*
* We are a writer.
*/
if (*rwstate == 0) {
} else if (try_flag) {
if (*rwstate & URW_WRITE_LOCKED) {
(void) _private_mutex_unlock(
} else {
/*
* The waiters bit may be inaccurate.
* Only the kernel knows for sure.
*/
rwlp->rwlock_mowner = 0;
rwlp->rwlock_mownerpid = 0;
}
} else {
rwlp->rwlock_mowner = 0;
rwlp->rwlock_mownerpid = 0;
}
}
if (error == 0) {
if (rd_wr == WRITE_LOCK) {
}
if (!try_flag) {
}
} else if (!try_flag) {
}
return (error);
}
/*
* Code for unlock of process-shared (USYNC_PROCESS) rwlocks.
*
* Note: if the lock appears to have waiters we call __lwp_rwlock_unlock()
* holding the mutex. This returns with the mutex still held (for us to
* release).
*/
int
{
int error = 0;
return (error);
/* Reset flag used to suggest caller yields. */
*waked = 0;
/* Our right to unlock was checked in __rw_unlock(). */
if (*rwstate & URW_WRITE_LOCKED) {
rwlp->rwlock_owner = 0;
rwlp->rwlock_ownerpid = 0;
}
if ((*rwstate & ~URW_READERS_MASK) == 0) {
/* Simple multiple readers, no waiters case. */
if (*rwstate > 0)
(*rwstate)--;
} else if (!(*rwstate & URW_HAS_WAITERS)) {
/* Simple no waiters case (i.e. was write locked). */
*rwstate = 0;
} else {
/*
* We appear to have waiters so we must call into the kernel.
* If there are waiters a full handoff will occur (rwstate
* will be updated, and one or more threads will be awoken).
*/
/* Suggest caller yields. */
*waked = 1;
}
if (error) {
} else {
}
return (error);
}
/*
* Common code for rdlock, timedrdlock, wrlock, timedwrlock, tryrdlock,
* and trywrlock for process-private (USYNC_THREAD) rwlocks.
*/
int
{
int try_flag;
int error = 0;
/*
* Optimize for the case of having only a single thread.
* (Most likely a traditional single-threaded application.)
* We don't need the protection of queue_lock() in this case.
* We need to defer signals, however (the other form of concurrency).
*/
if (rwlp->rwlock_readers < 0 ||
if (try_flag)
return (EBUSY);
/*
* Sombody other than ourself owns the lock. (If we
* owned the lock, either for reading or writing, we
* would already have returned EDEADLK in our caller.)
* This can happen only in the child of fork1() when
* some now-defunct thread was holding the lock when
* the fork1() was executed by the current thread.
* In this case, we just fall into the long way
* to block, either forever or with a timeout.
*/
} else {
rwlp->rwlock_readers++;
else {
}
return (0);
}
}
if (!try_flag) {
}
/*
* Do it the long way.
*/
while (error == 0) {
if (rwlp->rwlock_readers < 0 ||
/* EMPTY */; /* somebody holds the lock */
else if (!rwlp->rwlock_mwaiters)
break; /* no queued waiters */
rwlp->rwlock_mwaiters = 0;
break; /* no queued waiters */
} else {
if (rd_wr == WRITE_LOCK) {
/*
* We defer to a queued thread that has
* a higher priority than ours.
*/
break;
} else {
/*
* We defer to a queued thread that has
* a higher priority than ours or that
* is a writer whose priority equals ours.
*/
break;
}
}
/*
* We are about to block.
* If we're doing a trylock, return EBUSY instead.
*/
if (try_flag) {
break;
}
/*
* Enqueue writers ahead of readers of the
* same priority.
*/
error = 0;
set_parking_flag(self, 0);
}
if (error == 0) {
rwlp->rwlock_readers++;
else {
/* make it look like we acquired the embedded mutex */
}
if (!try_flag) {
}
} else if (!try_flag) {
}
return (error);
}
int
{
int error;
/*
* If we already hold a readers lock on this rwlock,
* just increment our reference count and return.
*/
return (EAGAIN);
return (0);
}
/*
* If we hold the writer lock, bail out.
*/
if (rw_write_is_held(rwlp)) {
if (self->ul_error_detection)
"calling thread owns the writer lock");
return (EDEADLK);
}
else /* user-level */
if (error == 0) {
if (rwsp)
}
return (error);
}
int
{
}
void
{
}
#pragma weak pthread_rwlock_reltimedrdlock_np = \
int
{
int error;
return (error);
}
int
{
int error;
return (error);
}
int
{
int error;
/*
* If we hold a readers lock on this rwlock, bail out.
*/
if (rw_read_is_held(rwlp)) {
if (self->ul_error_detection)
"calling thread owns the readers lock");
return (EDEADLK);
}
/*
* If we hold the writer lock, bail out.
*/
if (rw_write_is_held(rwlp)) {
if (self->ul_error_detection)
"calling thread owns the writer lock");
return (EDEADLK);
}
} else { /* user-level */
}
}
return (error);
}
int
{
}
void
{
}
#pragma weak pthread_rwlock_reltimedwrlock_np = \
int
{
int error;
return (error);
}
int
{
int error;
return (error);
}
int
{
int error;
if (rwsp)
/*
* If we already hold a readers lock on this rwlock,
* just increment our reference count and return.
*/
return (EAGAIN);
return (0);
}
else /* user-level */
if (error == 0)
else if (rwsp)
return (error);
}
int
{
int error;
if (rwsp)
} else { /* user-level */
}
if (rwsp) {
if (error)
else
}
return (error);
}
int
{
int waked;
/* fetch the lock count once; it may change underfoot */
/* munge it from rwstate */
if (lock_count & URW_WRITE_LOCKED)
lock_count = -1;
else
}
if (lock_count < 0) {
/*
* Since the writer lock is held, we'd better be
* holding it, else we cannot legitimately be here.
*/
if (!rw_write_is_held(rwlp)) {
if (self->ul_error_detection)
"writer lock held, "
"but not by the calling thread");
return (EPERM);
}
if (rwsp->rw_wrlock_begin_hold)
rwsp->rw_wrlock_begin_hold = 0;
}
} else if (lock_count > 0) {
/*
* A readers lock is held; if we don't hold one, bail out.
*/
if (self->ul_error_detection)
"readers lock held, "
"but not by the calling thread");
return (EPERM);
}
/*
* If we hold more than one readers lock on this rwlock,
* just decrement our reference count and return.
*/
return (0);
}
} else {
/*
* This is a usage error.
* No thread should release an unowned lock.
*/
if (self->ul_error_detection)
return (EPERM);
}
/*
* In the case of having only a single thread, we don't
* need the protection of queue_lock() (this parallels
* the optimization made in rwlock_lock(), above).
* As in rwlock_lock(), we need to defer signals.
*/
if (rwlp->rwlock_readers > 0) {
rwlp->rwlock_readers--;
} else {
rwlp->rwlock_readers = 0;
/* make it look like we released the embedded mutex */
rwlp->rwlock_mowner = 0;
}
waked = 0;
} else { /* multithreaded */
if (rwlp->rwlock_readers > 0) {
rwlp->rwlock_readers--;
} else {
rwlp->rwlock_readers = 0;
/* make it look like we released the embedded mutex */
rwlp->rwlock_mowner = 0;
}
}
/*
* Yield to the thread we just waked up, just in case we might
* be about to grab the rwlock again immediately upon return.
* This is pretty weak but it helps on a uniprocessor and also
* when cpu affinity has assigned both ourself and the other
* thread to the same CPU. Note that lwp_yield() will yield
* the processor only if the writer is at the same or higher
* priority than ourself. This provides more balanced program
* behavior; it doesn't guarantee acquisition of the lock by
* the pending writer.
*/
if (waked)
lwp_yield();
return (0);
}
void
{
(void) __rw_unlock(rwlp);
}