// // Adds an entry to the table for the specified thread. // internal void Add(int tid) { int hi = tid & HASH_TABLE_MASK; ReaderCounter rc = table[hi].entry; do { if (rc.count == 0) { rc.tid = tid; rc.count = 1; return; } } while ((rc = rc.next) != null); rc = new ReaderCounter(tid, table[hi].entry); table[hi].entry = rc; }
// // Looks up the entry belonging to the specified thread. // internal ReaderCounter Lookup(int tid) { ReaderCounter rc = table[tid & HASH_TABLE_MASK].entry; if (rc.tid == tid && rc.count != 0) { return(rc); } while ((rc = rc.next) != null) { if (rc.tid == tid) { return((rc.count != 0) ? rc : null); } } return(null); }
// // Constructors. // internal ReaderCounter(int id, ReaderCounter n) { next = n; tid = id; count = 1; }
// // Tries to enter the lock in upgrade read mode, activating // the specified cancellers. // public bool TryEnterUpgradeableRead(StCancelArgs cargs) { int tid = Thread.CurrentThread.ManagedThreadId; ReaderCounter rc = rdCounts.Lookup(tid); if (!isReentrant) { if (tid == upgrader) { throw new StLockRecursionException("Recursive upgrade not allowed"); } if (tid == writer) { throw new StLockRecursionException("Upgrade after write not allowed"); } if (rc != null) { throw new StLockRecursionException("Upgrade after read not allowed"); } } else { // // If the current thread is the current upgrader, increment the // recursive acquisition counter and return. // if (tid == upgrader) { upCount++; return(true); } // // If the current thread is the current writer, it can also // becomes the upgrader. If it is also a reader, it will be // accounted as reader on the *upgraderIsReader* flag. // if (tid == writer) { upgrader = tid; upCount = 1; if (rc != null) { upgraderIsReader = true; } return(true); } if (rc != null) { throw new StLockRecursionException("Upgrade after read not allowed"); } } // // Acquire the spinlock that protects the r/w lock shared state. // slock.Enter(); // // If the lock isn't in write or upgrade read mode, the // current thread becomes the current upgrader. Then, release // the spinlock and return success. // if (writer == UNOWNED && upgrader == UNOWNED) { upgrader = tid; slock.Exit(); upgraderIsReader = false; upCount = 1; return(true); } // // The upgrade read lock can't be acquired immediately. // So, if a null timeout was specified, return failure. // if (cargs.Timeout == 0) { slock.Exit(); return(false); } // // Create a wait node and insert it in the upgrader's queue. // int sc = (upQueue.IsEmpty && wrQueue.IsEmpty) ? spinCount : 0; WaitNode wn; upQueue.Enqueue(wn = new WaitNode(tid)); // // Release the spinlock and park the current thread activating // the specified cancellers and spinning, if appropriate. // slock.Exit(); int ws = wn.Park(sc, cargs); // // If we acquired the upgrade lock, initialize the recursive // acquisition count and the *upgraderIsReader flag and return // success. // if (ws == StParkStatus.Success) { upCount = 1; upgraderIsReader = false; return(true); } // // The acquire attemptwas cancelled. So, ensure that the // wait node is unlinked from the wait queue and report // the failure appropriately. // if (wn.next != wn) { slock.Enter(); upQueue.Remove(wn); slock.Exit(); } StCancelArgs.ThrowIfException(ws); return(false); }
// // Exits the read lock. // public void ExitRead() { int tid = Thread.CurrentThread.ManagedThreadId; ReaderCounter rc = rdCounts.Lookup(tid); if (rc == null) { throw new StSynchronizationLockException("Mismatched read"); } // // If this is a recursive exit of the read lock, decrement // the recursive acquisition count and return. // if (--rc.count > 0) { return; } // // The read lock was exited by the current thread. So, if the current // thread is also the upgrader clear the *upgraderIsReader* flag // and return, because no other waiter thread can be released. // if (tid == upgrader) { upgraderIsReader = false; return; } // // Acquire the spinlock that protecteds the r/u/w lock shared state. // Then, decrement the number of active readers; if this is not the // unique lock reader, release the spin lock and return. // slock.Enter(); if (--readers > 0) { slock.Exit(); return; } // // Here, we know that the r/u/w lock doesn't have readers. // First, check the current upgarder is waiting to enter the write lock, // and, if so, try to release it. // WaitNode w; if ((w = upgToWrWaiter) != null) { upgToWrWaiter = null; if (w.TryLock()) { writer = w.tid; slock.Exit(); w.Unpark(StParkStatus.Success); return; } } // // If the r/w lock isn't in the upgrade read mode nor in write // mode, try to wake up a waiting writer; failing that, try to // wake up a waiting upgrader and/or all waiting readers. // if (!(upgrader == UNOWNED && writer == UNOWNED && (TryWakeupWriter() || TryWakeupUpgraderAndReaders()))) { slock.Exit(); } }
// // Tries to enter the read lock, activating the // specified cancellers. // public bool TryEnterRead(StCancelArgs cargs) { int tid = Thread.CurrentThread.ManagedThreadId; ReaderCounter rc = rdCounts.Lookup(tid); if (!isReentrant) { if (tid == writer) { throw new StLockRecursionException("Read after write not allowed"); } if (rc != null) { throw new StLockRecursionException("Recursive read not allowed"); } } else { // // If this is a recursive enter, increment the recursive // acquisition counter and return. // if (rc != null) { rc.count++; return(true); } } // // Acquire the spinlock that protected the r/u/w lock shared state. // slock.Enter(); // // If the current thread is the upgrader, it can also enter // the read lock. So, add an entry to the readers table, // release the spinlock and return success. // if (tid == upgrader) { rdCounts.Add(tid); slock.Exit(); upgraderIsReader = true; return(true); } // // The read lock can be entered, if the r/w lock isn't in write // mode and no thread is waiting to enter the writer mode. // If these conditions are met, increment the number of lock // readers, add an entry to the readers table, release the // spinlock and return success. // if (writer == UNOWNED && wrQueue.IsEmpty && upgToWrWaiter == null) { readers++; rdCounts.Add(tid); slock.Exit(); return(true); } // // If the r/w lock is reentrant and the current thread is the // current writer, it can also to become a reader. So, increment // the number of lock readers, add an entry to the readers table, // release the spinlock and return success. // if (isReentrant && tid == writer) { readers++; rdCounts.Add(tid); slock.Exit(); return(true); } // // The current thread can't enter the read lock immediately. // So, if a null timeout was specified, release the spinlock // and return failure. // if (cargs.Timeout == 0) { slock.Exit(); return(false); } // // Create a wait node and insert it in the readers queue. // Compute also the amount of spinning. // int sc = rdQueue.IsEmpty ? spinCount : 0; WaitNode wn; rdQueue.Enqueue(wn = new WaitNode(tid)); // // Release the spinlock and park the current thread, activating // the specified cancellers and spinning if appropriate. // slock.Exit(); int ws = wn.Park(sc, cargs); // // If we entered the read lock, return success. // if (ws == StParkStatus.Success) { return(true); } // // The enter attempt was cancelled. So, ensure that the wait node // is unlinked from the queue and report the failure appropriately. // if (wn.next != wn) { slock.Enter(); rdQueue.Remove(wn); slock.Exit(); } StCancelArgs.ThrowIfException(ws); return(false); }