// // Tries to add immediatelly a data item to the queue. // public override bool TryAdd(T di) { // // If the queue is full, return failure immediatelly. // if (count == length) { return(false); } // // The queue seems non-full; so, acquire the queue's lock. // qlock.Enter(); // // When the queue's buffer has free slots, it can't have threads // blocked by the add operation; so, if there are waiters, they // were blocked by the take operation. // if (count < length) { if (!waitQueue.IsEmpty) { // // Try to deliver the data item directly to a waiting thread. // do { WaitNode w = waitQueue.Dequeue(); StParker pk = w.parker; if (pk.TryLock()) { // // Release the queue's lock, pass the data item through // the wait node, unpark the waiter thread and return // success. // qlock.Exit(); w.channel = di; pk.Unpark(w.waitKey); return(true); } } while (!waitQueue.IsEmpty); } // // There is at least a free slot on the queue; So, copy the // data item to the queue's buffer, unlock the queue and // return success. // items[tail] = di; if (++tail == length) { tail = 0; } count++; qlock.Exit(); return(true); } // // The queue's buffer is full, so return false. // qlock.Exit(); 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 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); }