Ejemplo n.º 1
0
        //These methods require a lock on SyncRoot. They will not block regular per segment accessors (for long)

        /// <summary>
        /// Enumerates all segments in _CurrentRange and locking them before yielding them and resleasing the lock afterwards
        /// The order in which the segments are returned is undefined.
        /// Lock SyncRoot before using this enumerable.
        /// </summary>
        /// <returns></returns>
        internal IEnumerable <Segment <TStored, TSearch> > EnumerateAmorphLockedSegments(bool forReading)
        {
            //if segments are locked a queue will be created to try them later
            //this is so that we can continue with other not locked segments.
            Queue <Segment <TStored, TSearch> > lockedSegmentIxs = null;

            for (int i = 0, end = _CurrentRange.Count; i != end; ++i)
            {
                var segment = _CurrentRange.GetSegmentByIndex(i);

                if (segment.Lock(forReading))
                {
                    try { yield return(segment); }
                    finally { segment.Release(forReading); }
                }
                else
                {
                    if (lockedSegmentIxs == null)
                    {
                        lockedSegmentIxs = new Queue <Segment <TStored, TSearch> >();
                    }

                    lockedSegmentIxs.Enqueue(segment);
                }
            }

            if (lockedSegmentIxs != null)
            {
                var ctr = lockedSegmentIxs.Count;

                while (lockedSegmentIxs.Count != 0)
                {
                    //once we retried them all and we are still not done.. wait a bit.
                    if (ctr-- == 0)
                    {
                        new System.Threading.ManualResetEvent(false).WaitOne(0);
                        ctr = lockedSegmentIxs.Count;
                    }

                    var segment = lockedSegmentIxs.Dequeue();

                    if (segment.Lock(forReading))
                    {
                        try { yield return(segment); }
                        finally { segment.Release(forReading); }
                    }
                    else
                    {
                        lockedSegmentIxs.Enqueue(segment);
                    }
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Adjusts the segmentation to the new segment count
        /// </summary>
        /// <param name="newSegmentCount">The new number of segments to use. This must be a power of 2.</param>
        /// <param name="segmentSize">The number of item slots to reserve in each segment.</param>
        void SetSegmentation(Int32 newSegmentCount, Int32 segmentSize)
        {
            //Variables to detect a bad hash.
            var totalNewSegmentSize = 0;
            var largestSegmentSize  = 0;

            //Segment<TStored,TSearch> largestSegment = null;

            lock (SyncRoot)
            {
#if DEBUG
                //<<<<<<<<<<<<<<<<<<<< debug <<<<<<<<<<<<<<<<<<<<<<<<
                //{
                //    int minSize = _CurrentRange.GetSegmentByIndex(0)._List.Length;
                //    int maxSize = minSize;

                //    for (int i = 1, end = _CurrentRange.Count; i < end; ++i)
                //    {
                //        int currentSize = _CurrentRange.GetSegmentByIndex(i)._List.Length;

                //        if (currentSize < minSize)
                //            minSize = currentSize;

                //        if (currentSize > maxSize)
                //            maxSize = currentSize;
                //    }

                //    System.Diagnostics.Debug.Assert(maxSize <= 8 * minSize, "Probably a bad hash");
                //}
                //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#endif
                unchecked
                {
                    //create the new range
                    Segmentrange <TStored, TSearch> newRange = CreateSegmentRange(newSegmentCount, segmentSize);

                    //increase total allocated space now. We can do this safely
                    //because at this point _AssessSegmentationPending flag will be true,
                    //preventing an immediate reassesment.
                    Interlocked.Increment(ref _AllocatedSpace);

                    //lock all new segments
                    //we are going to release these locks while we migrate the items from the
                    //old (current) range to the new range.
                    for (int i = 0, end = newRange.Count; i != end; ++i)
                    {
                        newRange.GetSegmentByIndex(i).LockForWriting();
                    }

                    //set new (completely locked) range
                    Interlocked.Exchange(ref _NewRange, newRange);


                    //calculate the step sizes for our switch points
                    var currentSwitchPointStep = (UInt32)(1 << _CurrentRange.Shift);
                    var newSwitchPointStep     = (UInt32)(1 << newRange.Shift);

                    //position in new range up from where the new segments are locked
                    var newLockedPoint = (UInt32)0;

                    //At this moment _SwitchPoint should be 0
                    var switchPoint = (UInt32)_SwitchPoint;

                    do
                    {
                        Segment <TStored, TSearch> currentSegment;

                        do
                        {
                            //aquire segment to migrate
                            currentSegment = _CurrentRange.GetSegment(switchPoint);

                            //lock segment
                            currentSegment.LockForWriting();

                            if (currentSegment.IsAlive)
                            {
                                break;
                            }

                            currentSegment.ReleaseForWriting();
                        }while (true);

                        //migrate all items in the segment to the new range
                        TStored currentKey;

                        int it = -1;

                        while ((it = currentSegment.GetNextItem(it, out currentKey, this)) >= 0)
                        {
                            var currentKeyHash = this.GetItemHashCode(ref currentKey);

                            //get the new segment. this is already locked.
                            var newSegment = _NewRange.GetSegment(currentKeyHash);

                            TStored dummyKey;
                            newSegment.InsertItem(ref currentKey, out dummyKey, this);
                        }

                        //substract allocated space from allocated space count and trash segment.
                        currentSegment.Bye(this);
                        currentSegment.ReleaseForWriting();

                        if (switchPoint == 0 - currentSwitchPointStep)
                        {
                            //we are about to wrap _SwitchPoint arround.
                            //We have migrated all items from the entire table to the
                            //new range.
                            //replace current with new before advancing, otherwise
                            //we would create a completely blocked table.
                            Interlocked.Exchange(ref _CurrentRange, newRange);
                        }

                        //advance _SwitchPoint
                        switchPoint = (UInt32)Interlocked.Increment(ref _SwitchPoint);

                        //release lock of new segments upto the point where we can still add new items
                        //during this migration.
                        while (true)
                        {
                            var nextNewLockedPoint = newLockedPoint + newSwitchPointStep;

                            if (nextNewLockedPoint > switchPoint || nextNewLockedPoint == 0)
                            {
                                break;
                            }

                            var newSegment = newRange.GetSegment(newLockedPoint);
                            newSegment.Trim(this);

                            var newSegmentSize = newSegment._Count;

                            totalNewSegmentSize += newSegmentSize;

                            if (newSegmentSize > largestSegmentSize)
                            {
                                largestSegmentSize = newSegmentSize;
                                //largestSegment = newSegment;
                            }

                            newSegment.ReleaseForWriting();
                            newLockedPoint = nextNewLockedPoint;
                        }
                    }while (switchPoint != 0);

                    //unlock any remaining new segments
                    while (newLockedPoint != 0)
                    {
                        var newSegment = newRange.GetSegment(newLockedPoint);
                        newSegment.Trim(this);

                        var newSegmentSize = newSegment._Count;

                        totalNewSegmentSize += newSegmentSize;

                        if (newSegmentSize > largestSegmentSize)
                        {
                            largestSegmentSize = newSegmentSize;
                            //largestSegment = newSegment;
                        }

                        newSegment.ReleaseForWriting();
                        newLockedPoint += newSwitchPointStep;
                    }
                }
            }
        }