public void TestBlockRequiredCount() { PartialThreadBlocker inst = new PartialThreadBlocker(4); Barrier startBar = new Barrier(8 + 1); int exitedCount = 0; for (int i = 0; i < 8; i++) { Task.Run(() => { startBar.SignalAndWait(); inst.Wait(); Interlocked.Increment(ref exitedCount); }); } startBar.SignalAndWait(); Assert.IsTrue(SpinWait.SpinUntil(() => Volatile.Read(ref exitedCount) >= 4 && inst.RealWaiterCount >= 4, 4000)); TimingAssert.AreEqual(5000, 8 - 4, () => Volatile.Read(ref exitedCount), "exitedCount != 8"); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount, "ExpectedWaiterCount != 4"); TimingAssert.AreEqual(5000, 4, () => inst.RealWaiterCount, "RealWaiterCount != 4"); inst.SetExpectedWaiterCount(0); TimingAssert.AreEqual(5000, 8, () => Volatile.Read(ref exitedCount), "exitedCount != 8"); TimingAssert.AreEqual(5000, 0, () => inst.ExpectedWaiterCount, "ExpectedWaiterCount != 0"); TimingAssert.AreEqual(5000, 0, () => inst.RealWaiterCount, "RealWaiterCount != 0"); }
public void TestCancellationWork() { PartialThreadBlocker inst = new PartialThreadBlocker(4); CancellationTokenSource tokSrc = new CancellationTokenSource(); int exitedCount = 0; Task.Run(() => { try { inst.Wait(tokSrc.Token); } catch (OperationCanceledException) { } Interlocked.Increment(ref exitedCount); }); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 1, () => inst.RealWaiterCount); tokSrc.Cancel(); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 0, () => inst.RealWaiterCount); TimingAssert.AreEqual(5000, 1, () => Volatile.Read(ref exitedCount)); }
public void TestNoBlockWithZeroWaiterCount() { PartialThreadBlocker inst = new PartialThreadBlocker(); Assert.IsTrue(inst.Wait(1)); Assert.IsTrue(inst.Wait(1)); }
public void TestSingleDecreaseWork() { PartialThreadBlocker inst = new PartialThreadBlocker(4); Barrier startBar = new Barrier(1 + 1); int exitedCount = 0; Task.Run(() => { startBar.SignalAndWait(); inst.Wait(); Interlocked.Increment(ref exitedCount); }); startBar.SignalAndWait(); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 1, () => inst.RealWaiterCount, "RealWaiterCount != 1"); for (int i = 1; i <= 4; i++) { inst.SubstractExpectedWaiterCount(1); TimingAssert.AreEqual(5000, 4 - i, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, i == 4 ? 0 : 1, () => inst.RealWaiterCount); } TimingAssert.AreEqual(5000, 1, () => Volatile.Read(ref exitedCount), "exitedCount != 1"); }
/// <summary> /// <see cref="DynamicThreadPool"/> constructor /// </summary> /// <param name="minThreadCount">Minimum number of threads that should always be in pool</param> /// <param name="maxThreadCount">Maximum number of threads in pool</param> /// <param name="queueBoundedCapacity">The bounded size of the work items queue (if less or equal to 0 then no limitation)</param> /// <param name="name">The name for this instance of ThreadPool and for its threads</param> /// <param name="isBackground">Whether or not threads are a background threads</param> /// <param name="options">Additional thread pool creation parameters</param> private DynamicThreadPool(DynamicThreadPoolOptions options, int minThreadCount, int maxThreadCount, int queueBoundedCapacity, string name, bool isBackground) : base(queueBoundedCapacity, options.QueueStealAwakePeriod, isBackground, name, options.UseOwnTaskScheduler, options.UseOwnSyncContext, options.FlowExecutionContext) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (minThreadCount < 0) { throw new ArgumentOutOfRangeException(nameof(minThreadCount)); } if (maxThreadCount <= 0 || maxThreadCount >= 4096) { throw new ArgumentOutOfRangeException(nameof(maxThreadCount), "maxThreadCount should be in range [0, 4096)"); } if (maxThreadCount < minThreadCount) { throw new ArgumentOutOfRangeException(nameof(maxThreadCount), "maxThreadCount should be greater than minThreadCount"); } if (options.MaxQueueCapacityExtension < 0) { throw new ArgumentOutOfRangeException(nameof(options.MaxQueueCapacityExtension)); } if (options.ManagementProcessPeriod <= 0) { throw new ArgumentOutOfRangeException(nameof(options.ManagementProcessPeriod)); } _minThreadCount = minThreadCount; _maxThreadCount = maxThreadCount; _fastSpawnThreadCountLimit = Math.Max(_minThreadCount, Math.Min(_maxThreadCount, FastSpawnThreadCountLimit)); _reasonableThreadCount = Math.Max(_minThreadCount, Math.Min(_maxThreadCount, ReasonableThreadCount)); _managementProcessPeriod = options.ManagementProcessPeriod; _maxQueueCapacityExtension = options.MaxQueueCapacityExtension; _noWorkItemTrimPeriod = options.NoWorkItemTrimPeriod >= 0 ? options.NoWorkItemTrimPeriod : -1; _throughoutTracker = new ExecutionThroughoutTrackerUpDownCorrection(_maxThreadCount, _reasonableThreadCount); _wasSomeProcessByThreadsFlag = false; _dieSlotActiveFullThreadCountCombination = 0; _extThreadBlocker = new PartialThreadBlocker(0); Qoollo.Turbo.Threading.ServiceStuff.ManagementThreadController.Instance.RegisterCallback(ManagementThreadProc); FillPoolUpTo(minThreadCount); }
public void TestIncreaseWaiters() { PartialThreadBlocker inst = new PartialThreadBlocker(4); Barrier startBar = new Barrier(8 + 1); int exitedCount = 0; int somethingWork = 0; CancellationTokenSource tokenSrc = new CancellationTokenSource(); for (int i = 0; i < 8; i++) { Task.Run(() => { startBar.SignalAndWait(); while (!tokenSrc.IsCancellationRequested) { inst.Wait(); Interlocked.Increment(ref somethingWork); Thread.Sleep(10); } Interlocked.Increment(ref exitedCount); }); } startBar.SignalAndWait(); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 4, () => inst.RealWaiterCount, "Real waiter count != 4 (can be caused by slow processing)"); TimingAssert.IsTrue(5000, () => Volatile.Read(ref somethingWork) > 0); inst.SetExpectedWaiterCount(8); Assert.AreEqual(8, inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 8, () => inst.RealWaiterCount); Interlocked.Exchange(ref somethingWork, 0); TimingAssert.IsTrue(5000, () => Volatile.Read(ref somethingWork) == 0); tokenSrc.Cancel(); inst.SetExpectedWaiterCount(0); TimingAssert.AreEqual(5000, 8, () => Volatile.Read(ref exitedCount)); TimingAssert.AreEqual(5000, 0, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(500, 0, () => inst.RealWaiterCount); }
public void TestTimeoutWork() { PartialThreadBlocker inst = new PartialThreadBlocker(4); int exitedCount = 0; bool exitByTimeout = false; Task.Run(() => { Volatile.Write(ref exitByTimeout, !inst.Wait(1500)); Interlocked.Increment(ref exitedCount); }); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 1, () => inst.RealWaiterCount); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 0, () => inst.RealWaiterCount); TimingAssert.AreEqual(5000, 1, () => Volatile.Read(ref exitedCount)); TimingAssert.IsTrue(5000, () => Volatile.Read(ref exitByTimeout)); }
public void TestCycledBlockRequiredCount() { PartialThreadBlocker inst = new PartialThreadBlocker(4); Barrier startBar = new Barrier(8 + 1); int exitedCount = 0; int somethingWork = 0; CancellationTokenSource tokenSrc = new CancellationTokenSource(); for (int i = 0; i < 8; i++) { Task.Run(() => { startBar.SignalAndWait(); while (!tokenSrc.IsCancellationRequested) { inst.Wait(); Interlocked.Increment(ref somethingWork); Thread.Sleep(10); } Interlocked.Increment(ref exitedCount); }); } startBar.SignalAndWait(); TimingAssert.AreEqual(5000, 4, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 4, () => inst.RealWaiterCount); TimingAssert.IsTrue(5000, () => Volatile.Read(ref somethingWork) > 0); tokenSrc.Cancel(); inst.SetExpectedWaiterCount(0); TimingAssert.AreEqual(5000, 8, () => Volatile.Read(ref exitedCount)); TimingAssert.AreEqual(5000, 0, () => inst.ExpectedWaiterCount); TimingAssert.AreEqual(5000, 0, () => inst.RealWaiterCount); }
public void TestFullCycle() { PartialThreadBlocker inst = new PartialThreadBlocker(0); Barrier startBar = new Barrier(8 + 1); int exitedCount = 0; CancellationTokenSource tokenSrc = new CancellationTokenSource(); Thread[] threads = new Thread[8]; Func <int> sleepThCount = () => { int result = 0; foreach (var th in threads) { if ((th.ThreadState & ThreadState.WaitSleepJoin) != 0) { result++; } } return(result); }; for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(() => { startBar.SignalAndWait(); while (!tokenSrc.IsCancellationRequested) { inst.Wait(); SpinWaitHelper.SpinWait(1200); Thread.Yield(); } Interlocked.Increment(ref exitedCount); }); threads[i].Start(); } startBar.SignalAndWait(); for (int i = 0; i < threads.Length; i++) { TimingAssert.AreEqual(5000, i, sleepThCount, "UP. Sleeping thread count != i"); TimingAssert.AreEqual(5000, i, () => inst.RealWaiterCount, "UP. RealWaiterCount != i"); TimingAssert.AreEqual(5000, i, () => inst.ExpectedWaiterCount, "UP. ExpectedWaiterCount != i"); inst.AddExpectedWaiterCount(1); Thread.Sleep(10); } TimingAssert.AreEqual(5000, threads.Length, sleepThCount); TimingAssert.AreEqual(5000, threads.Length, () => inst.RealWaiterCount); TimingAssert.AreEqual(5000, threads.Length, () => inst.ExpectedWaiterCount); for (int i = threads.Length; i > 0; i--) { TimingAssert.AreEqual(5000, i, sleepThCount); TimingAssert.AreEqual(5000, i, () => inst.RealWaiterCount); TimingAssert.AreEqual(5000, i, () => inst.ExpectedWaiterCount); inst.SubstractExpectedWaiterCount(1); Thread.Sleep(10); } TimingAssert.AreEqual(5000, 0, sleepThCount, "Sleeping thread count != 0"); TimingAssert.AreEqual(5000, 0, () => inst.RealWaiterCount, "RealWaiterCount != 0"); TimingAssert.AreEqual(5000, 0, () => inst.ExpectedWaiterCount, "ExpectedWaiterCount != 0"); tokenSrc.Cancel(); TimingAssert.AreEqual(5000, threads.Length, () => Volatile.Read(ref exitedCount)); }