// // Writes the specified number of data items to the // stream blocking queue, activating the specified cancellers. // public int Write(T[] buffer, int offset, int count, StCancelArgs cargs) { // // If this is a zero length write, return immediately. // if (count == 0) { return(0); } // // Initialize the local variables. // int remaining = count; WaitNodeQueue <T> wl = new WaitNodeQueue <T>(); int toCopy; // // Acquire the queue's lock. // slock.Enter(); // // If the queue's buffer is full, check if the current // thread must wait. // if (available == length) { goto CheckForWait; } // // If there are waiting readers (i.e., the buffer is empty), // transfer data items transferring directly to the waiting // readers' buffers. // if (!waitQueue.IsEmpty) { do { WaitNode <T> rdw = waitQueue.head; // // Compute the number of data items to transfer to the // waiting reader's buffer and perform the transfer. // if ((toCopy = rdw.remaining) > remaining) { toCopy = remaining; } Array.Copy(buffer, offset, rdw.buffer, rdw.offset, toCopy); rdw.remaining -= toCopy; rdw.offset += toCopy; remaining -= toCopy; offset += toCopy; // // If the waiting reader completes its read operation, // remove its wait node from the wait list and try to // lock the associated parker. // if (rdw.remaining == 0) { waitQueue.Dequeue(); if (rdw.TryLock() && !rdw.UnparkInProgress(StParkStatus.Success)) { wl.Add(rdw); } } else { // // The data items are not enough to satisfy the waiting // reader that is at front of wait queue, so break the loop. // break; } } while (remaining != 0 && !waitQueue.IsEmpty); } // // If we have still data items to write and there is free space // in the queue's buffer, transfer the appropriate number of data // items the queue's buffer. // if (remaining != 0 && available < length) { // // Compute the number of data items that can be copied // to the queue's buffer and perform the transfer. // if ((toCopy = remaining) > (length - available)) { toCopy = length - available; } int t = tail; int tillEnd; if ((tillEnd = length - t) >= toCopy) { Array.Copy(buffer, offset, items, t, toCopy); if ((t += toCopy) >= length) { t = 0; } } else { int fromBegin = toCopy - tillEnd; Array.Copy(buffer, offset, items, t, tillEnd); Array.Copy(buffer, offset + tillEnd, items, 0, fromBegin); t = fromBegin; } // // Update counters and indexes. // tail = t; available += toCopy; remaining -= toCopy; offset += toCopy; } CheckForWait: // // If there are still data items to write, the current thread must // wait if a null timeout wasn't specified. // WaitNode <T> wn = null; bool mustWait; if (mustWait = (remaining != 0 && cargs.Timeout != 0)) { waitQueue.Enqueue(wn = new WaitNode <T>(buffer, offset, remaining)); } // // Release the queue's lock and unpark the readers threads // release above. // slock.Exit(); wl.UnparkAll(); // // If the current thread doesn't need to wait, return the // number of written data items. // if (!mustWait) { return(count - remaining); } // // Park the current thread, activating the specified cancellers. // int ws = wn.Park(cargs); // // If the write was completed, return the number of written // data items. // if (ws == StParkStatus.Success) { return(count); } // // The write was cancelled due to timeout, alert or interrupt. // If the wait node is still inserted in the wait queue, acquire // the queue's lock and unlink it. // if (wn.next != wn) { slock.Enter(); waitQueue.Remove(wn); slock.Exit(); } // // If at least a data item was written, ignore the cancellation // and return the number of transferred data items. // int c; if ((c = count - wn.remaining) != 0) { StCancelArgs.PostponeCancellation(ws); return(c); } // // No data items were written; so, report the failure appropriately. // StCancelArgs.ThrowIfException(ws); return(0); }
// // Tries to enter the write lock, activating the specified // cancellers. // public bool TryEnterWrite(StCancelArgs cargs) { int tid = Thread.CurrentThread.ManagedThreadId; if (!isReentrant) { if (tid == writer) { throw new StLockRecursionException("Recursive enter write not allowed"); } if (rdCounts.Lookup(tid) != null) { throw new StLockRecursionException("Write after read not allowed"); } } else { // // If this is a recursive enter, increment the recursive acquisition // counter and return success. // if (tid == writer) { wrCount++; return(true); } } // // Acquire the spinlock that protects the r/w lock shared state. // slock.Enter(); // // If the write lock can be entered - this is, there are no lock // readers, no lock writer or upgrader or the current thread is the // upgrader -, enter the write lock, release the spinlock and // return success. // if (readers == 0 && writer == UNOWNED && (upgrader == tid || upgrader == UNOWNED)) { writer = tid; slock.Exit(); wrCount = 1; return(true); } // // If the current thread isn't the current upgrader but is reader, // release the spinlock and throw the appropriate exception. // if (tid != upgrader && rdCounts.Lookup(tid) != null) { slock.Exit(); throw new StLockRecursionException("Write after read not allowed"); } // // The write lock can't be entered 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 writers queue. // If the current thread isn't the current upgrader, the wait // node is inserted in the writer's queue; otherwise, the wait // node becomes referenced by the *upgradeToWriteWaiter* field. // int sc; WaitNode wn = new WaitNode(tid); if (tid == upgrader) { upgToWrWaiter = wn; sc = spinCount; } else { sc = wrQueue.IsEmpty ? spinCount : 0; wrQueue.Enqueue(wn); } // // Release spin the lock and park the current thread, activating // the specified cancellers and spinning, if appropriate. // slock.Exit(); int ws = wn.Park(sc, cargs); // // If the thread entered the write lock, initialize the recursive // acquisition counter and return success. // if (ws == StParkStatus.Success) { wrCount = 1; return(true); } // // The enter attempted was cancelled. So, if the wait node was // already remove from the respective queue, report the failure // appropriately. Otherwise, unlink the wait node and, if appropriate, // taking into account the other waiters. // if (wn.next == wn) { goto ReportFailure; } slock.Enter(); if (wn.next != wn) { if (wn == upgToWrWaiter) { upgToWrWaiter = null; } else { wrQueue.Remove(wn); // // If the writers queue becomes empty, it is possible that there // is a waiting upgrader or waiting reader threads that can now // proceed. // if (writer == UNOWNED && upgrader == UNOWNED && wrQueue.IsEmpty && TryWakeupUpgraderAndReaders()) { goto ReportFailure; } } } slock.Exit(); ReportFailure: StCancelArgs.ThrowIfException(ws); return(false); }
// // 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); }
// // 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); }