Exemplo n.º 1
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);
        }
        //
        // 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);
        }
Exemplo n.º 4
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);
        }
Exemplo n.º 5
0
            //
            // Receives a message, activating the specified cancellers.
            //

            internal override bool Receive(out T request, out StRendezvousToken token,
                                           StCancelArgs cargs)
            {
                RecvWaitNode <T, R> wn = null;

                do
                {
                    WaitNode t = top;
                    if (t == null || t.type == ReqType.Receive)
                    {
                        //
                        // The stack is empty or the wait note at the top of the
                        // stack belongs to a receiver thread. In this case, we must
                        // create a wait node and push it onto the stack.
                        //

                        if (wn == null)
                        {
                            if (cargs.Timeout == 0)
                            {
                                request = default(T);
                                token   = new StRendezvousToken(null);
                                return(false);
                            }
                            wn = new RecvWaitNode <T, R>();
                        }
                        wn.next = t;
                        if (Interlocked.CompareExchange <WaitNode>(ref top, wn, t) == t)
                        {
                            break;
                        }
                    }
                    else
                    {
                        //
                        // The top wait node belongs to a sender thread; so, try to
                        // pop it from the stack.
                        //

                        if (Interlocked.CompareExchange <WaitNode>(ref top, t.next, t) == t)
                        {
                            //
                            // Try to lock the associated parker and, if succeed, initiate
                            // the rendezvous with its owner thread.
                            //

                            if (t.TryLock())
                            {
                                SendWaitNode <T, R> swn = (SendWaitNode <T, R>)t;
                                request = swn.request;
                                if (swn.type == ReqType.SendOnly)
                                {
                                    token = new StRendezvousToken(null);
                                    swn.Unpark(StParkStatus.Success);
                                }
                                else
                                {
                                    token = new StRendezvousToken(swn);
                                }
                                return(true);
                            }
                        }
                    }
                } while (true);

                //
                // Park the current thread, activating the specified cancellers.
                //

                int ws = wn.Park(cargs);

                //
                // If succeed, retrive the request from the wait node, build a
                // rendezvous token and return success.
                //

                if (ws == StParkStatus.Success)
                {
                    request = wn.request;
                    token   = new StRendezvousToken(wn.sender);
                    return(true);
                }

                //
                // The receive was cancelled; so, unlink the wait node from
                // the wait queue and return the failure approriately.
                //

                Unlink(wn);
                request = default(T);
                token   = new StRendezvousToken(null);
                StCancelArgs.ThrowIfException(ws);
                return(false);
            }