public void TestSimpleRunAndStop() { int threadStart = 0; using (DelegateThreadSetManager testInst = new DelegateThreadSetManager(Environment.ProcessorCount, "name", (id, state, token) => { Interlocked.Increment(ref threadStart); })) { Assert.IsTrue(testInst.State == ThreadSetManagerState.Created, "State != Created"); Assert.AreEqual(Environment.ProcessorCount, testInst.ThreadCount); Assert.IsFalse(testInst.IsWork); testInst.Start(); SpinWait.SpinUntil(() => Volatile.Read(ref threadStart) >= testInst.ThreadCount, 10000); TestContext.WriteLine("All thread started"); bool byCondition = SpinWait.SpinUntil(() => testInst.ActiveThreadCount == 0, 5000); TestContext.WriteLine(byCondition ? "ActiveThreadCount == 0" : "ActiveThreadCount != 0 (timeout)"); TimingAssert.AreEqual(15000, Environment.ProcessorCount, () => Volatile.Read(ref threadStart)); TimingAssert.IsTrue(5000, () => testInst.State == ThreadSetManagerState.AllThreadsExited, "State != AllThreadsExited"); testInst.Stop(); Assert.IsTrue(testInst.State == ThreadSetManagerState.Stopped, "State != Stopped"); } }
public void TestRunAndStopWithLongWork() { int threadExit = 0; using (DelegateThreadSetManager testInst = new DelegateThreadSetManager(Environment.ProcessorCount, "name", (id, state, token) => { Thread.Sleep(2500); Interlocked.Increment(ref threadExit); })) { Assert.IsTrue(testInst.State == ThreadSetManagerState.Created); Assert.AreEqual(Environment.ProcessorCount, testInst.ThreadCount); Assert.IsFalse(testInst.IsWork); testInst.Start(); TimingAssert.IsTrue(15000, () => testInst.ActiveThreadCount == Environment.ProcessorCount); testInst.Stop(); Assert.IsTrue(testInst.State == ThreadSetManagerState.Stopped); Assert.AreEqual(0, testInst.ActiveThreadCount); Assert.AreEqual(Environment.ProcessorCount, threadExit); } }
public void TestCancellationOnStop() { int threadEnter = 0; int threadExit = 0; ManualResetEventSlim waiter = new ManualResetEventSlim(false); using (DelegateThreadSetManager testInst = new DelegateThreadSetManager(Environment.ProcessorCount, "name", (id, state, token) => { try { Interlocked.Increment(ref threadEnter); waiter.Wait(token); } finally { Interlocked.Increment(ref threadExit); } })) { testInst.Start(); TimingAssert.IsTrue(15000, () => testInst.State == ThreadSetManagerState.Running); TimingAssert.IsTrue(15000, () => testInst.ActiveThreadCount == Environment.ProcessorCount); TimingAssert.IsTrue(15000, () => Volatile.Read(ref threadEnter) == Environment.ProcessorCount); testInst.Stop(); Assert.IsTrue(testInst.State == ThreadSetManagerState.Stopped); Assert.AreEqual(0, testInst.ActiveThreadCount); Assert.AreEqual(Environment.ProcessorCount, threadExit, "threadExit != configurated thread count"); } }
/// <summary> /// LevelingQueue constructor /// </summary> /// <param name="highLevelQueue">High level queue (queue with higher priority)</param> /// <param name="lowLevelQueue">Low level queue (queue with lower priority)</param> /// <param name="addingMode">Adding mode of the queue</param> /// <param name="isBackgroundTransferingEnabled">Is background transfering items from LowLevelQueue to HighLevelQueue enabled</param> public LevelingQueue(IQueue <T> highLevelQueue, IQueue <T> lowLevelQueue, LevelingQueueAddingMode addingMode, bool isBackgroundTransferingEnabled) { if (highLevelQueue == null) { throw new ArgumentNullException(nameof(highLevelQueue)); } if (lowLevelQueue == null) { throw new ArgumentNullException(nameof(lowLevelQueue)); } _highLevelQueue = highLevelQueue; _lowLevelQueue = lowLevelQueue; _addingMode = addingMode; _isBackgroundTransferingEnabled = isBackgroundTransferingEnabled; _addMonitor = new MonitorObject("LevelingQueue.AddMonitor"); _takeMonitor = new MonitorObject("LevelingQueue.TakeMonitor"); _peekMonitor = new MonitorObject("LevelingQueue.PeekMonitor"); _itemCount = highLevelQueue.Count + lowLevelQueue.Count; _isDisposed = false; if (isBackgroundTransferingEnabled) { _backgoundTransfererExclusive = new MutuallyExclusivePrimitive(); if (addingMode == LevelingQueueAddingMode.PreferLiveData) { _backgoundTransfererExclusive.AllowBackgroundGate(); // Allow background transfering from the start } _backgroundTransferer = new DelegateThreadSetManager(1, this.GetType().GetCSName() + "_" + this.GetHashCode().ToString() + " Background Transferer", BackgroundTransferProc); _backgroundTransferer.IsBackground = true; _backgroundTransferer.Start(); } }
/// <summary> /// DiskQueue constructor /// </summary> /// <param name="path">Path to the folder on the disk to store queue segments</param> /// <param name="segmentFactory">Factory to create DiskQueueSegments</param> /// <param name="maxSegmentCount">Maximum number of simultaniously active segments</param> /// <param name="backgroundCompaction">Is background compaction allowed (if not then compaction happens synchronously within the Take operation)</param> /// <param name="compactionPeriod">Compaction period in milliseconds</param> internal DiskQueue(string path, DiskQueueSegmentFactory <T> segmentFactory, int maxSegmentCount, bool backgroundCompaction, int compactionPeriod) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(nameof(path)); } if (segmentFactory == null) { throw new ArgumentNullException(nameof(segmentFactory)); } if (compactionPeriod <= 0) { throw new ArgumentOutOfRangeException(nameof(compactionPeriod), "Compaction period should be positive"); } if (maxSegmentCount == 0 || maxSegmentCount == 1) { throw new ArgumentOutOfRangeException(nameof(maxSegmentCount), "At least two segments should be available"); } if (maxSegmentCount > int.MaxValue / 4) { throw new ArgumentOutOfRangeException(nameof(maxSegmentCount), "Segment count is too large"); } if (maxSegmentCount <= 0 || maxSegmentCount > int.MaxValue / 4) { maxSegmentCount = int.MaxValue / 4; } _addMonitor = new MonitorObject("DiskQueue.AddMonitor"); _takeMonitor = new MonitorObject("DiskQueue.TakeMonitor"); _peekMonitor = new MonitorObject("DiskQueue.PeekMonitor"); _segmentFactory = segmentFactory; _segmentOperationsLock = new object(); _maxSegmentCount = maxSegmentCount; _segmentsPath = path; _itemCount = 0; _boundedCapacity = -1; if (_segmentFactory.SegmentCapacity > 0) { _boundedCapacity = (long)_segmentFactory.SegmentCapacity * maxSegmentCount; } _nonFairItemThreshold = Environment.ProcessorCount; _nonFairSegmentThreshold = _segmentFactory.SegmentCapacity > 0 ? Math.Max(1, (8 * Environment.ProcessorCount) / _segmentFactory.SegmentCapacity) : 16; var discoveredSegments = segmentFactory.DiscoverSegmentsWrapped(path); _segments = new CircularList <DiskQueueSegmentWrapper <T> >(discoveredSegments.OrderBy(o => o.Number)); if (_segments.Count > 0) { for (int i = 0; i < _segments.Count; i++) { if (i + 1 < _segments.Count) { _segments[i].NextSegment = _segments[i + 1]; // Build linked-list if (_segments[i].Number == _segments[i + 1].Number) { throw new InvalidOperationException("DiscoverSegments returned duplicated segment numbers"); } } _itemCount += _segments[i].Count; } _headSegment = _segments[0]; _tailSegment = _segments[_segments.Count - 1]; _lastSegmentNumber = _tailSegment.Number; if (_tailSegment.IsFull) { // Allocate new segment when tail is Full (prevent write modifications of segments from previous run) var newTailSegment = segmentFactory.CreateSegmentWrapped(path, ++_lastSegmentNumber); _tailSegment.NextSegment = newTailSegment; _tailSegment = newTailSegment; _segments.Add(newTailSegment); } } else { // Allocate new segment _headSegment = _tailSegment = segmentFactory.CreateSegmentWrapped(path, ++_lastSegmentNumber); _segments.Add(_tailSegment); } _compactionPeriod = compactionPeriod; if (backgroundCompaction) { _backgroundCompactionThread = new DelegateThreadSetManager(1, this.GetType().GetCSName() + "_" + this.GetHashCode().ToString() + " Background compaction", BackgroundCompactionProc); _backgroundCompactionThread.IsBackground = true; _backgroundCompactionThread.Start(); } _isDisposed = false; }