예제 #1
0
        /// <summary>
        /// Attempts to remove batch from the head of the queue
        /// </summary>
        /// <param name="segment">Removed batch</param>
        /// <returns>True if the batch was removed</returns>
        internal bool TryDequeue(out BatchingQueueSegment <T> segment)
        {
            SpinWait sw = new SpinWait();

            while (true)
            {
                BatchingQueueSegment <T> head = _head;
                if (head == _tail)
                {
                    segment = null;
                    return(false);
                }

                Debug.Assert(head.Next != null);

                if (Interlocked.CompareExchange(ref _head, head.Next, head) == head)
                {
                    SpinWait completionSw = new SpinWait();
                    while (!head.IsNotInWork)
                    {
                        completionSw.SpinOnce();
                    }

                    Interlocked.Add(ref _itemsCount, -head.Count);
                    segment = head;
                    return(true);
                }

                sw.SpinOnceNoSleep();
            }
        }
예제 #2
0
        public void TestSpinWaitSpins()
        {
            SpinWait sw = new SpinWait();

            for (int i = 0; i < 10; i++)
            {
                Assert.AreEqual(i, sw.Count);
                sw.SpinOnceNoSleep();
            }
        }
예제 #3
0
        public void TestSpinWaitNotSleep()
        {
            SpinWait  sw        = new SpinWait();
            Stopwatch stopwatch = Stopwatch.StartNew();

            for (int i = 0; i < 100; i++)
            {
                sw.SpinOnceNoSleep();
            }

            stopwatch.Stop();
            Assert.IsTrue(stopwatch.ElapsedMilliseconds <= 5);
        }
        private bool TryAcquireItemCount()
        {
            SpinWait sw        = new SpinWait();
            int      itemCount = _itemCount;

            while (itemCount > 0 && Interlocked.CompareExchange(ref _itemCount, itemCount - 1, itemCount) != itemCount)
            {
                sw.SpinOnceNoSleep();
                itemCount = _itemCount;
            }

            return(itemCount > 0);
        }
        private bool TryAcquireFillCount()
        {
            SpinWait sw        = new SpinWait();
            int      fillCount = _fillCount;

            while (fillCount < _capacity && Interlocked.CompareExchange(ref _fillCount, fillCount + 1, fillCount) != fillCount)
            {
                sw.SpinOnceNoSleep();
                fillCount = _fillCount;
            }

            return(fillCount < _capacity);
        }
예제 #6
0
        /// <summary>
        /// Reads 'head' and 'tail' atomically
        /// </summary>
        /// <param name="head">Current head of the queue</param>
        /// <param name="tail">Current tail of the queue</param>
        private void GetHeadTailAtomic(out BatchingQueueSegment <T> head, out BatchingQueueSegment <T> tail)
        {
            head = _head;
            tail = _tail;
            SpinWait sw = new SpinWait();

            while (head != _head || tail != _tail)
            {
                sw.SpinOnceNoSleep();
                head = _head;
                tail = _tail;
            }
        }
예제 #7
0
        private void AddCore(PoolElementWrapper <T> element)
        {
            SpinWait sw          = new SpinWait();
            var      headIndexOp = _headIndexOp;

            element.NextIndex = GetHeadIndex(headIndexOp);
            while (Interlocked.CompareExchange(ref _headIndexOp, Repack(element.ThisIndex, headIndexOp), headIndexOp) != headIndexOp)
            {
                sw.SpinOnceNoSleep();
                headIndexOp       = _headIndexOp;
                element.NextIndex = GetHeadIndex(headIndexOp);
            }
        }
예제 #8
0
        private bool TryTakeLockFree()
        {
            SpinWait spin = new SpinWait();
            int      currentCountLocFree = _currentCountLockFree;

            while (currentCountLocFree > 0)
            {
                if (Interlocked.CompareExchange(ref _currentCountLockFree, currentCountLocFree - 1, currentCountLocFree) == currentCountLocFree)
                {
                    return(true);
                }

                spin.SpinOnceNoSleep();
                currentCountLocFree = _currentCountLockFree;
            }

            return(false);
        }
예제 #9
0
        private bool TryTakeCore(out PoolElementWrapper <T> element)
        {
            SpinWait sw          = new SpinWait();
            var      headIndexOp = _headIndexOp;

            while (GetHeadIndex(headIndexOp) >= 0)
            {
                var headElem = _dataArray.GetItemSafe(GetHeadIndex(headIndexOp));
                if (headElem != null && Interlocked.CompareExchange(ref _headIndexOp, Repack(headElem.NextIndex, headIndexOp), headIndexOp) == headIndexOp)
                {
                    element           = headElem;
                    element.NextIndex = -1;
                    return(true);
                }

                sw.SpinOnceNoSleep();

                headIndexOp = _headIndexOp;
            }

            element = null;
            return(false);
        }
예제 #10
0
        /// <summary>
        /// Attempts to create the next BatchingQueueSegment in the Linked List structure
        /// </summary>
        /// <returns>true - segments created and can be read through <see cref="Next"/> property, false - no new segment created due to already created next segment</returns>
        internal bool Grow()
        {
            int reservedIndexWithFinalizationMark = _reservedIndexWithFinalizationMark;

            if (IsSegmentFinalized(reservedIndexWithFinalizationMark))
            {
                return(false);
            }

            bool     result     = false;
            var      newSegment = _preallocatedNext ?? new BatchingQueueSegment <T>(Capacity, unchecked (_batchId + 1));
            SpinWait sw         = new SpinWait();

            try { }
            finally
            {
                reservedIndexWithFinalizationMark = _reservedIndexWithFinalizationMark;
                while (!IsSegmentFinalized(reservedIndexWithFinalizationMark))
                {
                    if (Interlocked.CompareExchange(ref _reservedIndexWithFinalizationMark, SetSegmentFinalized(reservedIndexWithFinalizationMark), reservedIndexWithFinalizationMark) == reservedIndexWithFinalizationMark)
                    {
                        result = Interlocked.CompareExchange(ref _next, newSegment, null) == null;
                        TurboContract.Assert(result, "New segment update failed");
                        break;
                    }
                    sw.SpinOnceNoSleep();
                    reservedIndexWithFinalizationMark = _reservedIndexWithFinalizationMark;
                }
            }

            if (result && Capacity >= SegmentPreallocationCapacityThreshold)
            {
                newSegment.PreallocateNextSegment();
            }

            return(result);
        }
예제 #11
0
        /// <summary>
        /// Exists the semaphore the specified number of times
        /// </summary>
        /// <param name="releaseCount">The number of times to exit the semaphore</param>
        public void Release(int releaseCount)
        {
            if (_isDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (releaseCount < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(releaseCount), "releaseCount should be positive");
            }
            if (_maxCount - CurrentCount < releaseCount)
            {
                throw new SemaphoreFullException();
            }

            int waiterAndWaitCountDiff = 0;
            int releaseCountForWait    = 0;
            int releaseCountLocFree    = releaseCount;

            if (_waitCount > 0)
            {
                waiterAndWaitCountDiff = GetWaiterAndWaitCountDiffAtomic();
                releaseCountForWait    = Math.Min(releaseCount, waiterAndWaitCountDiff); // Приоритет waiter'ам
                releaseCountLocFree    = releaseCount - releaseCountForWait;
            }

            TurboContract.Assert(releaseCountForWait >= 0, conditionString: "releaseCountForWait >= 0");
            TurboContract.Assert(releaseCountLocFree >= 0, conditionString: "releaseCountLocFree >= 0");
            TurboContract.Assert(releaseCountForWait + releaseCountLocFree == releaseCount, conditionString: "releaseCountForWait + releaseCountLocFree == releaseCount");

            // Сначала возврат в lockFree
            if (releaseCountLocFree > 0)
            {
                int currentCountLocFree = Interlocked.Add(ref _currentCountLockFree, releaseCountLocFree);
                TurboContract.Assert(currentCountLocFree > 0, conditionString: "currentCountLocFree > 0");
            }

            // Теперь возврат для waiter'ов. Если число waiter'ов увеличилось, то тоже нужно зайти в lock
            if (releaseCountForWait > 0 || (_waitCount > 0 && GetWaiterAndWaitCountDiffAtomic() > waiterAndWaitCountDiff))
            {
                lock (_lockObj)
                {
                    int waitCount               = _waitCount;
                    int currentCountForWait     = _currentCountForWait;
                    int nextCurrentCountForWait = currentCountForWait + releaseCountForWait;

                    // В идеале _waitCount == _currentCountForWait. Если нет, то предпринимаем действия
                    if (nextCurrentCountForWait > waitCount && releaseCountForWait > 0)
                    {
                        // Если слотов оказывается больше, то избыток возвращаем в _currentCountLocFree
                        int countForReturnToLockFree = Math.Min(releaseCountForWait, nextCurrentCountForWait - waitCount);
                        int currentCountLocFree      = Interlocked.Add(ref _currentCountLockFree, countForReturnToLockFree);
                        TurboContract.Assert(currentCountLocFree > 0, conditionString: "currentCountLocFree > 0");
                        releaseCountForWait -= countForReturnToLockFree;
                        releaseCountLocFree += countForReturnToLockFree;
                    }
                    else if (nextCurrentCountForWait < waitCount)
                    {
                        // Если меньше, то пытаемся захватить себе обратно

                        // Не можем забрать больше, чем было добавлено этим вызовом Release
                        int maxToRequestFromLockFree = Math.Min(releaseCountLocFree, waitCount - nextCurrentCountForWait);

                        if (maxToRequestFromLockFree > 0)
                        {
                            SpinWait spin = new SpinWait();
                            int      currentCountLocFree        = _currentCountLockFree;
                            int      countToRequestFromLockFree = Math.Min(currentCountLocFree, maxToRequestFromLockFree);
                            while (countToRequestFromLockFree > 0)
                            {
                                TurboContract.Assert(currentCountLocFree - countToRequestFromLockFree >= 0, conditionString: "currentCountLocFree - countToRequestFromLockFree >= 0");
                                if (Interlocked.CompareExchange(ref _currentCountLockFree, currentCountLocFree - countToRequestFromLockFree, currentCountLocFree) == currentCountLocFree)
                                {
                                    releaseCountForWait += countToRequestFromLockFree;
                                    releaseCountLocFree -= countToRequestFromLockFree;
                                    break;
                                }

                                spin.SpinOnceNoSleep();
                                currentCountLocFree        = _currentCountLockFree;
                                countToRequestFromLockFree = Math.Min(currentCountLocFree, maxToRequestFromLockFree);
                            }
                        }
                    }

                    TurboContract.Assert(releaseCountForWait >= 0, conditionString: "releaseCountForWait >= 0");
                    TurboContract.Assert(releaseCountLocFree >= 0, conditionString: "releaseCountLocFree >= 0");
                    TurboContract.Assert(releaseCountForWait + releaseCountLocFree == releaseCount, conditionString: "releaseCountForWait + releaseCountLocFree == releaseCount");

                    if (releaseCountForWait > 0)
                    {
                        TurboContract.Assert(_currentCountForWait == currentCountForWait, conditionString: "_currentCountForWait == currentCountForWait");

                        currentCountForWait += releaseCountForWait;
                        TurboContract.Assert(currentCountForWait > 0, conditionString: "currentCountForWait > 0");

                        int waitersToNotify = Math.Min(currentCountForWait, waitCount);
                        for (int i = 0; i < waitersToNotify; i++)
                        {
                            Monitor.Pulse(_lockObj);
                        }

                        _currentCountForWait = currentCountForWait;
                    }
                }
            }
        }