2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. 2N/A * Nesting-safe RW locking functions. Return 0 when successful, an 2N/A * error number from the E-series when not. 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A * If we allow read-to-write lock migration, there's a potential 2N/A * race condition if two or more threads want to upgrade at the 2N/A * same time. The simple and safe (but crude and possibly costly) 2N/A * method to fix this is to always use exclusive locks, and so 2N/A * that has to be the default. 2N/A * There are two conditions under which it is safe to set 2N/A * 'force_write' to zero for a certain lock structure: 2N/A * (1) The lock will never be subject to migration, or 2N/A * (2) It's OK if the data protected by the lock has changed 2N/A * (a) Every time the lock (read or write) has been 2N/A * acquired (even if the lock already was held by 2N/A * (b) After every call to a function that might have 2N/A * acquired the lock. 2N/A * Only allow changing 'force_write' when it's really safe; i.e., 2N/A * the lock hasn't been destroyed, and there are no readers. 2N/A * Only allow changing 'force_write' when it's really safe; i.e., 2N/A * the lock hasn't been destroyed, and there are no readers. 2N/A /* This shouldn't happen */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A /* Simplest (and probably most common) case: no readers or writers */ 2N/A * Need to know if we're holding a read lock already, and if 2N/A * all other readers are blocked waiting for the mutex. 2N/A * We're already holding a read lock, so 2N/A * if the number of readers equals the number 2N/A * of blocked readers plus one, all other 2N/A * readers are blocked. 2N/A * We're not holding a read lock, so the 2N/A * number of readers should equal the number 2N/A * of blocked readers if all readers are 2N/A /* Wait for reader(s) or writer to finish */ 2N/A * We can stop looping if one of the following holds: 2N/A * - No readers, no writers 2N/A * - No writers (or writer is myself), and one of: 2N/A * - One reader, and it's us 2N/A * - N readers, but all blocked on the mutex 2N/A * Provided that all readers are blocked on the mutex 2N/A * we break a potential dead-lock by acquiring the 2N/A * If 'trylock' is set, tell the caller that we'd have to 2N/A * block to obtain the lock. 2N/A /* If we're also a reader, indicate that we're blocking */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A /* OK to grab the write lock */ 2N/A /* Increment lock depth */ 2N/A /* Set number of writers (doesn't increase with lock depth) */ 2N/A /* No previous reader */ 2N/A * For insertion simplicity, make it the second item 2N/A /* This shouldn't happen */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A /* Wait for writer to complete; writer == myself also OK */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A /* This shouldn't happen */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A /* Find the reader record */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A /* Remove item from list and free it */ 2N/A * First record: copy second to first, and free second 2N/A /* Decomission the first record */ 2N/A /* If there are no readers, wake up any waiting writer */ 2N/A/* Return zero if write lock held by this thread, non-zero otherwise */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A * We're holding the lock, so we should return zero. Since 2N/A * that's what mutex_unlock() does if it succeeds, we just 2N/A * return the value of mutex_unlock(). 2N/A/* Return zero if read lock held by this thread, non-zero otherwise */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A /* Write lock also OK */ 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A * Only proceed if if there are neither readers nor writers 2N/A * other than this thread. Also, no nested locks may be in 2N/A#
endif /* NISDB_MT_DEBUG */ 2N/A * Mark lock destroyed, so that any thread waiting on the mutex 2N/A * will know what's what. Of course, this is a bit iffy, since 2N/A * we're probably being called from a destructor, and the structure 2N/A * where we live will soon cease to exist (i.e., be freed and 2N/A * perhaps re-used). Still, we can only do our best, and give 2N/A * those other threads the best chance possible. 2N/A printf(
"0x%x: Write locked by %d, depth = %d\n",