// // Constructors. // public StStreamBlockingQueue(int capacity) { if (capacity <= 0) { throw new ArgumentOutOfRangeException("capacity"); } items = new T[length = capacity]; waitQueue = new WaitNodeQueue <T>(); slock = new SpinLock(LONG_CRITICAL_SECTION_SPINS); }
// // Constructors. // public StReadUpgradeWriteLock(StLockRecursionPolicy policy, int sc) { slock = new SpinLock(SPIN_LOCK_SPINS); rdQueue = new WaitNodeQueue(); wrQueue = new WaitNodeQueue(); upQueue = new WaitNodeQueue(); rdCounts = new ReaderCounterTable(); // writer = upgrader = UNOWNED; isReentrant = (policy == StLockRecursionPolicy.SupportsRecursion); spinCount = Platform.IsMultiProcessor ? sc : 0; }
// // Waits until read the specified number of data items from // the stream queue, activating the specified cancellers. // public int Read(T[] buffer, int offset, int count, StCancelArgs cargs) { // // If this is a zero length read, return immediately. // if (count == 0) { return(0); } // // Initialize the local variables and acquire the queue's lock. // int remaining = count; WaitNodeQueue <T> wl = new WaitNodeQueue <T>(); int toCopy; slock.Enter(); // // If the queue's buffer is empty, check if the current thread // must wait. // if (available == 0) { goto CheckForWait; } // // Compute the number of data items that we can read // from the queue's buffer and perform the transfer. // if ((toCopy = remaining) > available) { toCopy = available; } int h = head; int tillEnd; if ((tillEnd = length - h) >= toCopy) { Array.Copy(items, h, buffer, offset, toCopy); if ((h += toCopy) >= length) { h = 0; } } else { int fromBegin = toCopy - tillEnd; Array.Copy(items, h, buffer, offset, tillEnd); Array.Copy(items, 0, buffer, offset + tillEnd, fromBegin); h = fromBegin; } // // Adjust counters and indexes. // head = h; available -= toCopy; remaining -= toCopy; offset += toCopy; // // If we have still data items to read and there are waiting // writers, transfer the data directly from our buffer to the // waiting writers' buffers. // if (remaining != 0 && !waitQueue.IsEmpty) { do { WaitNode <T> wrw = waitQueue.head; // // Compute the number of data items to transfer to the // waiting writer's buffer and perform the transfer. // if ((toCopy = remaining) > wrw.remaining) { toCopy = wrw.remaining; } Array.Copy(wrw.buffer, wrw.offset, buffer, offset, toCopy); wrw.remaining -= toCopy; wrw.offset += toCopy; remaining -= toCopy; offset += toCopy; // // If the write operation was completed, try to release // the writer thread. // if (wrw.remaining == 0) { waitQueue.Dequeue(); if (wrw.TryLock() && !wrw.UnparkInProgress(StParkStatus.Success)) { wl.Add(wrw); } } else { // // The read is completed, so break the loop. // break; } } while (remaining != 0 && !waitQueue.IsEmpty); } // // If there is available space in the queue's buffer and // waiting writers, try to fill the queue's buffer with // the data of the waiting writers. // if (available < length && !waitQueue.IsEmpty) { do { WaitNode <T> wrw = waitQueue.head; // // Compute the number of data items to copy from the writer's // buffer to the queue's buffer and perform the transfer. // if ((toCopy = wrw.remaining) > (length - available)) { toCopy = length - available; } int t = tail; if ((tillEnd = length - t) >= toCopy) { Array.Copy(wrw.buffer, wrw.offset, items, t, toCopy); if ((t += toCopy) >= length) { t = 0; } } else { int fromBegin = toCopy - tillEnd; Array.Copy(wrw.buffer, wrw.offset, items, t, tillEnd); Array.Copy(wrw.buffer, wrw.offset + tillEnd, items, 0, fromBegin); t = fromBegin; } // // Update counters and indexes. // tail = t; available += toCopy; wrw.remaining -= toCopy; wrw.offset += toCopy; // // If the writer completed its write, release it. // if (wrw.remaining == 0) { waitQueue.Dequeue(); if (wrw.TryLock() && !wrw.UnparkInProgress(StParkStatus.Success)) { wl.Add(wrw); } } else { // // The queue's buffer is full, so break the loop. // break; } } while (available < length && !waitQueue.IsEmpty); } CheckForWait: // // If the read operation was not completed, the current thread // must wait if it didn't read all data items and 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 released waiters. // slock.Exit(); wl.UnparkAll(); // // If the read was completed or the thread specified a null // timeout, return the number of read data items. // if (!mustWait) { return(count - remaining); } // // Park the current thread, activating the specified cancellers. // int ws = wn.Park(cargs); // // If succeed, return the number of data items transferred. // if (ws == StParkStatus.Success) { return(count); } // // The read was cancelled due to timeout, alert or interrupt. // If the wait block is still inserted in the wait queue, acquire // the queue's lock and remove it from the queue. // if (wn.next != wn) { slock.Enter(); waitQueue.Remove(wn); slock.Exit(); } int c; if ((c = count - wn.remaining) != 0) { StCancelArgs.PostponeCancellation(ws); return(c); } // // No data items were transferred, so report the failure // appropriately. // StCancelArgs.ThrowIfException(ws); return(0); }
// // 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 wakeup a waiting upgrader and/or all the waiting readers. // private bool TryWakeupUpgraderAndReaders() { // // Initialize the wakeup list and the released flag. // WaitNodeQueue wl = new WaitNodeQueue(); bool released = false; // // If the upgraders wait queue isn't empty, try to wakeup a // waiting upgrader to enter the lock in upgradeable read mode. // if (!upQueue.IsEmpty) { do { WaitNode w = upQueue.Dequeue(); if (w.TryLock()) { // // Make the locked waiter as the current upgrader and // add its wait node to the wake up list. // upgrader = w.tid; wl.AddToWakeList(w); released = true; break; } } while (!upQueue.IsEmpty); } // // Even when the r/w lock state changes to the upgrade read mode, // all the waiting readers can enter the read lock. // while (!rdQueue.IsEmpty) { WaitNode w = rdQueue.Dequeue(); if (w.TryLock()) { // // Account for one more active reader, add the respective // entry to the reader owners table and add the waiting reader // to the wake up list. // readers++; rdCounts.Add(w.tid); wl.AddToWakeList(w); released = true; } } // // If no thread was released, return false holding the // spinlock; otherwise, release the spinlock, unpark all // threads inserted in the wake up list and return true. // if (!released) { return false; } slock.Exit(); wl.UnparkAll(); return true; }
// // Tries to wakeup a waiting upgrader and/or all the waiting readers. // private bool TryWakeupUpgraderAndReaders() { // // Initialize the wakeup list and the released flag. // WaitNodeQueue wl = new WaitNodeQueue(); bool released = false; // // If the upgraders wait queue isn't empty, try to wakeup a // waiting upgrader to enter the lock in upgradeable read mode. // if (!upQueue.IsEmpty) { do { WaitNode w = upQueue.Dequeue(); if (w.TryLock()) { // // Make the locked waiter as the current upgrader and // add its wait node to the wake up list. // upgrader = w.tid; wl.AddToWakeList(w); released = true; break; } } while (!upQueue.IsEmpty); } // // Even when the r/w lock state changes to the upgrade read mode, // all the waiting readers can enter the read lock. // while (!rdQueue.IsEmpty) { WaitNode w = rdQueue.Dequeue(); if (w.TryLock()) { // // Account for one more active reader, add the respective // entry to the reader owners table and add the waiting reader // to the wake up list. // readers++; rdCounts.Add(w.tid); wl.AddToWakeList(w); released = true; } } // // If no thread was released, return false holding the // spinlock; otherwise, release the spinlock, unpark all // threads inserted in the wake up list and return true. // if (!released) { return(false); } slock.Exit(); wl.UnparkAll(); return(true); }