123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- /*++
- Copyright (c) 2016 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- rwlock.c
- Abstract:
- This module implements support for read/write locks.
- Author:
- Evan Green 28-Apr-2015
- Environment:
- User Mode
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "osbasep.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define OS_RWLOCK_UNLOCKED 0
- #define OS_RWLOCK_WRITE_LOCKED ((ULONG)-1)
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- OspAcquireReadWriteLockForRead (
- POS_RWLOCK Lock,
- ULONG TimeoutInMilliseconds
- );
- KSTATUS
- OspAcquireReadWriteLockForWrite (
- POS_RWLOCK Lock,
- ULONG TimeoutInMilliseconds
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- OS_API
- VOID
- OsRwLockInitialize (
- POS_RWLOCK Lock,
- ULONG Flags
- )
- /*++
- Routine Description:
- This routine initializes a read/write lock.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- Flags - Supplies a bitfield of flags governing the lock behavior.
- Return Value:
- None.
- --*/
- {
- RtlZeroMemory(Lock, sizeof(OS_RWLOCK));
- Lock->Attributes = Flags;
- return;
- }
- OS_API
- KSTATUS
- OsRwLockRead (
- POS_RWLOCK Lock
- )
- /*++
- Routine Description:
- This routine acquires the read/write lock for read access. Multiple readers
- can acquire the lock simultaneously, but any writes that try to acquire
- the lock while it's held for read will block. Readers that try to
- acquire the lock while it's held for write will also block.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- Return Value:
- Status code.
- --*/
- {
- return OspAcquireReadWriteLockForRead(Lock, SYS_WAIT_TIME_INDEFINITE);
- }
- OS_API
- KSTATUS
- OsRwLockReadTimed (
- POS_RWLOCK Lock,
- ULONG TimeoutInMilliseconds
- )
- /*++
- Routine Description:
- This routine acquires the read/write lock for read access just like the
- read lock function, except that this function will return after the
- specified deadline if the lock could not be acquired.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- TimeoutInMilliseconds - Supplies the duration to wait in milliseconds.
- Return Value:
- Status code.
- --*/
- {
- return OspAcquireReadWriteLockForRead(Lock, TimeoutInMilliseconds);
- }
- OS_API
- KSTATUS
- OsRwLockTryRead (
- POS_RWLOCK Lock
- )
- /*++
- Routine Description:
- This routine performs a single attempt at acquiring the lock for read
- access.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_RESOURCE_IN_USE if the lock is already held.
- --*/
- {
- ULONG NewState;
- ULONG OldState;
- OldState = Lock->State;
- if (OldState != OS_RWLOCK_WRITE_LOCKED) {
- NewState = RtlAtomicCompareExchange32(&(Lock->State),
- OldState + 1,
- OldState);
- if (NewState == OldState) {
- return STATUS_SUCCESS;
- }
- }
- return STATUS_RESOURCE_IN_USE;
- }
- OS_API
- KSTATUS
- OsRwLockWrite (
- POS_RWLOCK Lock
- )
- /*++
- Routine Description:
- This routine acquires the read/write lock for write access. The lock can
- only be acquired for write access if there are no readers and no other
- writers.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- Return Value:
- Status code.
- --*/
- {
- return OspAcquireReadWriteLockForWrite(Lock, SYS_WAIT_TIME_INDEFINITE);
- }
- OS_API
- KSTATUS
- OsRwLockWriteTimed (
- POS_RWLOCK Lock,
- ULONG TimeoutInMilliseconds
- )
- /*++
- Routine Description:
- This routine acquires the read/write lock for write access just like the
- write lock function, except that this function will return after the
- specified deadline if the lock could not be acquired.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- TimeoutInMilliseconds - Supplies the timeout to wait in milliseconds.
- Return Value:
- Status code.
- --*/
- {
- return OspAcquireReadWriteLockForWrite(Lock, TimeoutInMilliseconds);
- }
- OS_API
- KSTATUS
- OsRwLockTryWrite (
- POS_RWLOCK Lock
- )
- /*++
- Routine Description:
- This routine performs a single attempt at acquiring the lock for write
- access.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_RESOURCE_IN_USE if the lock is already held.
- --*/
- {
- ULONG OldState;
- OldState = Lock->State;
- if (OldState == OS_RWLOCK_UNLOCKED) {
- OldState = RtlAtomicCompareExchange32(&(Lock->State),
- OS_RWLOCK_WRITE_LOCKED,
- OldState);
- if (OldState == OS_RWLOCK_UNLOCKED) {
- Lock->WriterThreadId = OsGetThreadId();
- return 0;
- }
- }
- return STATUS_RESOURCE_IN_USE;
- }
- OS_API
- KSTATUS
- OsRwLockUnlock (
- POS_RWLOCK Lock
- )
- /*++
- Routine Description:
- This routine unlocks a read/write lock that's been acquired by this thread
- for either read or write.
- Arguments:
- Lock - Supplies a pointer to the read/write lock.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_PERMISSION_DENIED if the lock is not held or was not held by this
- thread.
- --*/
- {
- ULONG Count;
- POS_RWLOCK LockInternal;
- ULONG NewState;
- ULONG OldState;
- ULONG Operation;
- LockInternal = (POS_RWLOCK)Lock;
- OldState = LockInternal->State;
- if (OldState == OS_RWLOCK_UNLOCKED) {
- return STATUS_PERMISSION_DENIED;
- }
- //
- // If this lock is held by a writer, make sure that this thread is that
- // writer, then set it to unlocked.
- //
- if (OldState == OS_RWLOCK_WRITE_LOCKED) {
- if (LockInternal->WriterThreadId != OsGetThreadId()) {
- return STATUS_PERMISSION_DENIED;
- }
- LockInternal->WriterThreadId = 0;
- LockInternal->State = OS_RWLOCK_UNLOCKED;
- //
- // The lock is held by a reader.
- //
- } else {
- while (OldState > OS_RWLOCK_UNLOCKED) {
- NewState = RtlAtomicCompareExchange32(&(LockInternal->State),
- OldState - 1,
- OldState);
- if (NewState == OldState) {
- break;
- }
- OldState = NewState;
- }
- if (OldState == 0) {
- return STATUS_PERMISSION_DENIED;
- //
- // If there are still other readers, don't release the writers.
- //
- } else if (OldState > 1) {
- return STATUS_SUCCESS;
- }
- }
- //
- // Wake anyone blocking (chaos ensues).
- //
- if ((LockInternal->PendingReaders != 0) ||
- (LockInternal->PendingWriters != 0)) {
- Count = MAX_ULONG;
- Operation = UserLockWake;
- if ((LockInternal->Attributes & OS_RWLOCK_SHARED) == 0) {
- Operation |= USER_LOCK_PRIVATE;
- }
- OsUserLock(&(LockInternal->State), Operation, &Count, 0);
- }
- return STATUS_SUCCESS;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- OspAcquireReadWriteLockForRead (
- POS_RWLOCK Lock,
- ULONG TimeoutInMilliseconds
- )
- /*++
- Routine Description:
- This routine acquires the given read/write lock for read access.
- Arguments:
- Lock - Supplies a pointer to the lock to acquire.
- TimeoutInMilliseconds - Supplies the timeout to wait in milliseconds.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS KernelStatus;
- ULONG NewState;
- ULONG OldState;
- ULONG Operation;
- UINTN ThreadId;
- ThreadId = OsGetThreadId();
- if (ThreadId == Lock->WriterThreadId) {
- return STATUS_DEADLOCK;
- }
- while (TRUE) {
- OldState = Lock->State;
- if (OldState != OS_RWLOCK_WRITE_LOCKED) {
- NewState = RtlAtomicCompareExchange32(&(Lock->State),
- OldState + 1,
- OldState);
- //
- // If the old value wasn't write locked, then the reader was
- // successfully added.
- //
- if (NewState == OldState) {
- break;
- }
- //
- // The lock is already acquired for write access.
- //
- } else {
- Operation = UserLockWait;
- if ((Lock->Attributes & OS_RWLOCK_SHARED) == 0) {
- Operation |= USER_LOCK_PRIVATE;
- }
- RtlAtomicAdd32(&(Lock->PendingReaders), 1);
- KernelStatus = OsUserLock(&(Lock->State),
- Operation,
- &OldState,
- TimeoutInMilliseconds);
- RtlAtomicAdd32(&(Lock->PendingReaders), -1);
- if (KernelStatus == STATUS_TIMEOUT) {
- return KernelStatus;
- }
- }
- }
- return STATUS_SUCCESS;
- }
- KSTATUS
- OspAcquireReadWriteLockForWrite (
- POS_RWLOCK Lock,
- ULONG TimeoutInMilliseconds
- )
- /*++
- Routine Description:
- This routine acquires the given read/write lock for write access.
- Arguments:
- Lock - Supplies a pointer to the lock to acquire.
- TimeoutInMilliseconds - Supplies the timeout to wait in milliseconds.
- Return Value:
- 0 on success.
- Returns an error number on failure.
- --*/
- {
- KSTATUS KernelStatus;
- ULONG OldState;
- ULONG Operation;
- UINTN ThreadId;
- ThreadId = OsGetThreadId();
- if (ThreadId == Lock->WriterThreadId) {
- return STATUS_DEADLOCK;
- }
- while (TRUE) {
- OldState = Lock->State;
- if (OldState == OS_RWLOCK_UNLOCKED) {
- OldState = RtlAtomicCompareExchange32(&(Lock->State),
- OS_RWLOCK_WRITE_LOCKED,
- OldState);
- //
- // If the old value was unlocked, then this thread successfully
- // got the write lock.
- //
- if (OldState == OS_RWLOCK_UNLOCKED) {
- Lock->WriterThreadId = ThreadId;
- break;
- }
- //
- // The lock is already acquired for read or write access.
- //
- } else {
- Operation = UserLockWait;
- if ((Lock->Attributes & OS_RWLOCK_SHARED) == 0) {
- Operation |= USER_LOCK_PRIVATE;
- }
- RtlAtomicAdd32(&(Lock->PendingWriters), 1);
- KernelStatus = OsUserLock(&(Lock->State),
- Operation,
- &OldState,
- TimeoutInMilliseconds);
- RtlAtomicAdd32(&(Lock->PendingWriters), -1);
- if (KernelStatus == STATUS_TIMEOUT) {
- return KernelStatus;
- }
- }
- }
- return STATUS_SUCCESS;
- }
|