//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) { Thread.Sleep(0); ctr = lockedSegmentIxs.Count; } var segment = lockedSegmentIxs.Dequeue(); if (segment.Lock(forReading)) { try { yield return(segment); } finally { segment.Release(forReading); } } else { lockedSegmentIxs.Enqueue(segment); } } } }
/// <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.Add(ref _AllocatedSpace, newSegmentCount * segmentSize); //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.Add(ref _SwitchPoint, (Int32)currentSwitchPointStep); //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; } } } }