// // Tries to wakeup a waiting writer. // // NOTE: This method is called with the spinlock held // and when the r/w lock is in the NonEntered state. // If a waiter is woken, the method releases the spin lock // and returns true; otherwise, the method returns false and // with the spinlock held. // private bool TryWakeupWriter() { while (!wrQueue.IsEmpty) { WaitNode w = wrQueue.Dequeue(); if (w.TryLock()) { // // Become the waiter the next owner of the write lock, // release the spinlock, unpark the waiter and return true. // writer = w.tid; slock.Exit(); w.Unpark(StParkStatus.Success); return(true); } } // // No writer can be released; so, return false with the // spinlock held. // return(false); }
// // 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); }