Example #1
0
        //
        // 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);
        }
Example #3
0
        //
        // 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);
        }