diff --git a/STPExamples/STPExamples.csproj b/STPExamples/STPExamples.csproj index aee9252..5445fce 100644 --- a/STPExamples/STPExamples.csproj +++ b/STPExamples/STPExamples.csproj @@ -1,10 +1,9 @@  - net46;netcoreapp2.0 + net40;netcoreapp3.1 STPExamples STPExamples - TRACE; diff --git a/STPTests/STPTests.csproj b/STPTests/STPTests.csproj index 34e379f..324a13d 100644 --- a/STPTests/STPTests.csproj +++ b/STPTests/STPTests.csproj @@ -1,55 +1,39 @@  - net46;netcoreapp3.1;net5.0 + net40;netcoreapp3.1;net6; STPTests STPTests - TRACE; - Debug;Release;Publish - - - - true - - - - true - ..\publish\Keys\STP.snk - - - - true - - - - true - ..\publish\Keys\STP.snk - - - - true - - - - true - ..\publish\Keys\STP.snk + Debug;Release; + True - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + + + + + + - - + + + diff --git a/STPTests/TestAsyncAwait.cs b/STPTests/TestAsyncAwait.cs new file mode 100644 index 0000000..ea43b14 --- /dev/null +++ b/STPTests/TestAsyncAwait.cs @@ -0,0 +1,27 @@ +using Amib.Threading; +using NUnit.Framework; +using System; + +namespace AsyncAwait +{ + [TestFixture] + [Category("TestAsyncAwait")] + public class TestAsyncAwait : TestAsyncAwaitBase + { + protected SmartThreadPool _stp; + + [SetUp] + public void Init() + { + _stp = new SmartThreadPool(); + } + + [TearDown] + public void Fini() + { + _stp.Shutdown(); + } + + protected override IWorkItemsGroup GetWIG() => _stp; + } +} diff --git a/STPTests/TestAsyncAwaitBase.cs b/STPTests/TestAsyncAwaitBase.cs new file mode 100644 index 0000000..40f102d --- /dev/null +++ b/STPTests/TestAsyncAwaitBase.cs @@ -0,0 +1,503 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Amib.Threading; +using NUnit.Framework; + +namespace AsyncAwait +{ + public abstract class TestAsyncAwaitBase + { + protected abstract IWorkItemsGroup GetWIG(); + + static async void AsyncVoid(bool[] success, int i) + { + var workItem1 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + await Task.Delay(100); + + var workItem2 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + // Ensure we are in the same context as in before the await + success[i] = ReferenceEquals(workItem1, workItem2); + + if (success[i]) + { + await Task.Delay(100); + + var workItem3 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + success[i] = ReferenceEquals(workItem1, workItem3); + } + } + + static async Task AsyncTask(bool[] success, int i) + { + var workItem1 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + await Task.Delay(100); + + var workItem2 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + // Ensure we are in the same context as in before the await + success[i] = ReferenceEquals(workItem1, workItem2); + + if (success[i]) + { + await Task.Delay(100); + + var workItem3 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + success[i] = ReferenceEquals(workItem1, workItem3); + } + } + + static async Task AsyncTask(bool[] success, int i, T result) + { + var workItem1 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + await Task.Delay(100); + + var workItem2 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + // Ensure we are in the same context as in before the await + success[i] = ReferenceEquals(workItem1, workItem2); + + if (success[i]) + { + await Task.Delay(100); + + var workItem3 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem; + + success[i] = ReferenceEquals(workItem1, workItem3); + } + + return result; + } + + [Test] + public async Task SingleAsyncVoid() + { + bool[] success = {false}; + + var wir = GetWIG().QueueWorkItem(AsyncVoid, success, 0); + + // Wait for the work item to complete + await wir.GetResultAsync(); + + Assert.IsTrue(success.All(s => s)); + } + + [Test] + public async Task MultipleAsyncVoid() + { + bool[] success = new bool[10]; + + for (int i = 0; i < success.Length; i++) + { + GetWIG().QueueWorkItem(AsyncVoid, success, i); + } + + // Wait for all work items to complete + await GetWIG().WaitForIdleAsync(); + + Assert.IsTrue(success.All(s => s)); + } + + [Test] + public async Task SingleAsyncTask() + { + bool[] success = {false}; + + var wir = GetWIG().QueueWorkItem(AsyncTask, success, 0); + + // Wait for the work item to complete + await wir.GetResultAsync(); + + Assert.IsTrue(success.All(s => s)); + } + + [Test] + public async Task MultipleAsyncTask() + { + bool[] success = new bool[10]; + + for (int i = 0; i < success.Length; i++) + { + GetWIG().QueueWorkItem(AsyncTask, success, i); + } + + // Wait for all work items to complete + await GetWIG().WaitForIdleAsync(); + + Assert.IsTrue(success.All(s => s)); + } + + [Test] + public async Task SingleAsyncTaskT() + { + bool[] success = {false}; + + var wir = GetWIG().QueueWorkItem(AsyncTask, success, 0, "abc"); + + // Wait for the work item to complete + var result = await wir.GetResultAsync(); + + Assert.IsTrue(success.All(s => s)); + Assert.AreEqual(result, "abc"); + + } + + [Test] + public async Task MultipleAsyncTaskT() + { + bool[] success = new bool[10]; + IWorkItemResult[] wirs = new IWorkItemResult[success.Length]; + + for (int i = 0; i < success.Length; i++) + { + wirs[i] = GetWIG().QueueWorkItem(AsyncTask, success, i, "abc" + i); + } + + // Wait for all work items to complete + await GetWIG().WaitForIdleAsync(); + + Assert.IsTrue(success.All(s => s)); + + for (int i = 0; i < success.Length; i++) + { + var task = wirs[i].GetResultAsync(); + Assert.IsTrue(task.IsCompleted); + Assert.AreEqual(task.Result, "abc" + i); + } + } + + [Test] + public async Task WaitForIdle_AsyncVoid() + { + bool waited = false; + + async void DoWork() + { + await Task.Delay(1000); + + waited = true; + } + + GetWIG().QueueWorkItem(DoWork); + + var isIdleTask = GetWIG().WaitForIdleAsync(); + var timeoutTask = Task.Delay(2000); + + var resultTask = await Task.WhenAny(isIdleTask, timeoutTask); + + Assert.AreSame(resultTask, isIdleTask); + Assert.IsTrue(waited); + } + + [Test] + public async Task GetResultAsync_On_AsyncVoid() + { + int data = 0; + + async void DoProduce() + { + await Task.Delay(2000); + data = 17; + } + + var wirProducer = GetWIG().QueueWorkItem(DoProduce); + + async Task DoConsume() + { + await wirProducer.GetResultAsync(); + + return data == 17; + } + + var wirConsumer = GetWIG().QueueWorkItem(DoConsume); + + await Task.WhenAll( + wirConsumer.GetResultAsync(), + wirProducer.GetResultAsync()); + + Assert.IsTrue(wirConsumer.Result); + } + + [Test] + public async Task GetResultAsync_On_AsyncTaskT() + { + async Task DoProduce() + { + await Task.Delay(1000); + + return 17; + } + + var wirProducer = GetWIG().QueueWorkItem(DoProduce); + + async Task DoConsume() + { + var result = await wirProducer.GetResultAsync(); + + return result == 17; + } + + var wirConsumer = GetWIG().QueueWorkItem(DoConsume); + + await Task.WhenAll( + wirConsumer.GetResultAsync(), + wirProducer.GetResultAsync()); + + Assert.IsTrue(wirConsumer.Result); + } + + [Test] + public async Task QueueAnonymousAsync_Task() + { + var wig = GetWIG(); + var tcs = new TaskCompletionSource(); + + async Task DoProduce() + { + bool correctWig = + ReferenceEquals(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + if (!correctWig) + { + tcs.SetResult(false); + return; + } + + await Task.Delay(100); + + correctWig = ReferenceEquals(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + if (!correctWig) + { + tcs.SetResult(false); + return; + } + + tcs.SetResult(true); + } + + wig.QueueWorkItem(async () => await DoProduce()); + + var result = await tcs.Task; + + Assert.IsTrue(result); + } + + [Test] + public async Task QueueAnonymousAsync_TaskT() + { + var wig = GetWIG(); + var tcs = new TaskCompletionSource(); + + async Task DoProduce() + { + bool correctWig = + ReferenceEquals(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + if (!correctWig) + { + tcs.SetResult(false); + return -1; + } + + await Task.Delay(100); + + correctWig = ReferenceEquals(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + if (!correctWig) + { + tcs.SetResult(false); + return -1; + } + + tcs.SetResult(true); + return 17; + } + + var wir = wig.QueueWorkItem(async () => await DoProduce()); + + var result = await tcs.Task; + + Assert.IsTrue(result); + Assert.AreEqual(17, wir.Result); + } + + [Test] + public async Task RunTask_Action() + { + var wig = GetWIG(); + int result = 0; + void DoSomething() + { + Assert.AreSame(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + result = 17; + } + + await wig.RunTask(DoSomething); + + Assert.AreEqual(17, result); + Assert.IsTrue(wig.IsIdle); + } + + [Test] + public async Task RunTask_FuncT() + { + var wig = GetWIG(); + + int ComputeSomething() + { + Assert.AreSame(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + return 17; + } + + int result = await wig.RunTask(ComputeSomething); + + Assert.AreEqual(17, result); + Assert.IsTrue(wig.IsIdle); + } + + [Test] + public async Task RunTask_FuncTask() + { + var wig = GetWIG(); + int result = 0; + Task DoSomething() + { + Assert.AreSame(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + result = 17; + return Task.CompletedTask; + } + + await wig.RunTask(DoSomething); + + Assert.AreEqual(17, result); + Assert.IsTrue(wig.IsIdle); + } + + [Test] + public async Task RunTask_FuncTaskT() + { + var wig = GetWIG(); + + Task ComputeSomething() + { + Assert.AreSame(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + return Task.FromResult(17); + } + + int result = await wig.RunTask(ComputeSomething); + + Assert.AreEqual(17, result); + Assert.IsTrue(wig.IsIdle); + } + + [Test] + public async Task RunTask_CancellationToken_Awaiting() + { + var wig = GetWIG(); + var isReady = new TaskCompletionSource(); + var waitingForCancel = new TaskCompletionSource(); + + bool runAfterWaiting = false; + + async Task ComputeSomething() + { + Assert.AreSame(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + isReady.SetResult(true); + await waitingForCancel.Task; + + runAfterWaiting = true; + + return 0; + } + + CancellationTokenSource cts = new CancellationTokenSource(); + var t = wig.RunTask(ComputeSomething, cts.Token); + + await isReady.Task; + + cts.Cancel(); + waitingForCancel.SetResult(true); + + Exception ex = null; + try + { + await t; + } + catch (Exception e) + { + ex = e; + } + + await wig.WaitForIdleAsync(); + + Assert.IsTrue(t.IsCompleted); + Assert.IsFalse(runAfterWaiting); + Assert.IsTrue(ex is TaskCanceledException); + Assert.IsTrue(wig.IsIdle); + } + + [Test] + public async Task RunTask_CancellationToken_Waiting() + { + var wig = GetWIG(); + AutoResetEvent isReady = new AutoResetEvent(false); + AutoResetEvent waitingForCancel = new AutoResetEvent(false); + + bool runAfterWaiting = false; + bool isWorkItemCanceled = false; + + Task ComputeSomething() + { + Assert.AreSame(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + isReady.Set(); + waitingForCancel.WaitOne(); + + runAfterWaiting = true; + + Assert.AreSame(SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemsGroup, wig); + + isWorkItemCanceled = SmartThreadPool.IsWorkItemCanceled; + + return Task.FromResult(0); + } + + CancellationTokenSource cts = new CancellationTokenSource(); + var t = wig.RunTask(ComputeSomething, cts.Token); + + isReady.WaitOne(); + + cts.Cancel(); + waitingForCancel.Set(); + + Exception ex = null; + try + { + await t; + } + catch (Exception e) + { + ex = e; + } + + await wig.WaitForIdleAsync(); + + + Assert.IsTrue(t.IsCompleted); + Assert.IsTrue(runAfterWaiting); + Assert.IsTrue(isWorkItemCanceled); + Assert.IsTrue(ex is TaskCanceledException); + Assert.IsTrue(wig.IsIdle); + } + } +} \ No newline at end of file diff --git a/STPTests/TestFalseFillStateWithParams.cs b/STPTests/TestFalseFillStateWithParams.cs index 58b2be9..b6aee80 100644 --- a/STPTests/TestFalseFillStateWithParams.cs +++ b/STPTests/TestFalseFillStateWithParams.cs @@ -1,246 +1,246 @@ -using System; -using Amib.Threading; -using NUnit.Framework; -using System.Net; - -namespace STPTests -{ - /// - /// Summary description for TestFalseFillStateWithArgs. - /// - [TestFixture] - [Category("TestFalseFillStateWithArgs")] - public class TestFalseFillStateWithArgs - { - private SmartThreadPool _stp; - private IWorkItemsGroup _wig; - - [SetUp] - public void Init() - { - _stp = new SmartThreadPool(); - _wig = _stp.CreateWorkItemsGroup(10); - } - - [TearDown] - public void Fini() - { - _stp.WaitForIdle(); - _stp.Shutdown(); - } - - [Test] - public void STPActionT0() - { - IWorkItemResult wir = _stp.QueueWorkItem(Action0); - Assert.IsNull(wir.State); - } - - [Test] - public void STPActionT1() - { - IWorkItemResult wir = _stp.QueueWorkItem(Action1, 17); - Assert.IsNull(wir.State); - } - - [Test] - public void STPActionT2() - { - IWorkItemResult wir = _stp.QueueWorkItem(Action2, 'a', "bla bla"); - Assert.IsNull(wir.State); - } - - [Test] - public void STPActionT3() - { - char[] chars = new char[] { 'a', 'b' }; - object obj = new object(); - - IWorkItemResult wir = _stp.QueueWorkItem(Action3, true, chars, obj); - Assert.IsNull(wir.State); - } - - [Test] - public void STPActionT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _stp.QueueWorkItem(Action4, long.MinValue, p, ip, guid); - Assert.IsNull(wir.State); - } - - [Test] - public void STPFuncT0() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func0)); - Assert.IsNull(wir.State); - } - - [Test] - public void STPFuncT1() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func1), 17); - Assert.IsNull(wir.State); - } - - [Test] - public void STPFuncT2() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func2), 'a', "bla bla"); - Assert.IsNull(wir.State); - } - - [Test] - public void STPFuncT3() - { - char[] chars = new char[] { 'a', 'b' }; - object obj = new object(); - - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func3), true, chars, obj); - Assert.IsNull(wir.State); - } - - [Test] - public void STPFuncT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGActionT0() - { - IWorkItemResult wir = _wig.QueueWorkItem(Action0); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGActionT1() - { - IWorkItemResult wir = _wig.QueueWorkItem(Action1, 17); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGActionT2() - { - IWorkItemResult wir = _wig.QueueWorkItem(Action2, 'a', "bla bla"); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGActionT3() - { - char[] chars = new char[] { 'a', 'b' }; - object obj = new object(); - - IWorkItemResult wir = _wig.QueueWorkItem(Action3, true, chars, obj); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGActionT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _wig.QueueWorkItem(Action4, long.MinValue, p, ip, guid); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGFuncT0() - { - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func0)); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGFuncT1() - { - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func1), 17); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGFuncT2() - { - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func2), 'a', "bla bla"); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGFuncT3() - { - char[] chars = new char[] { 'a', 'b' }; - object obj = new object(); - - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func3), true, chars, obj); - Assert.IsNull(wir.State); - } - - [Test] - public void WIGFuncT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); - Assert.IsNull(wir.State); - } - - - private void Action0() - { - } - - private void Action1(int p1) - { - } - - private void Action2(char p1, string p2) - { - } - - private void Action3(bool p1, char[] p2, object p3) - { - } - - private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4) - { - } - - private int Func0() - { - return 0; - } - - private bool Func1(int p1) - { - return true; - } - - private string Func2(char p1, string p2) - { - return "value"; - } - - private char Func3(bool p1, char[] p2, object p3) - { - return 'z'; - } - - private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4) - { - return IPAddress.None; - } - } -} +using System; +using Amib.Threading; +using NUnit.Framework; +using System.Net; + +namespace SmartThreadPoolTests +{ + /// + /// Summary description for TestFalseFillStateWithArgs. + /// + [TestFixture] + [Category("TestFalseFillStateWithArgs")] + public class TestFalseFillStateWithArgs + { + private SmartThreadPool _stp; + private IWorkItemsGroup _wig; + + [SetUp] + public void Init() + { + _stp = new SmartThreadPool(); + _wig = _stp.CreateWorkItemsGroup(10); + } + + [TearDown] + public void Fini() + { + _stp.WaitForIdle(); + _stp.Shutdown(); + } + + [Test] + public void STPActionT0() + { + IWorkItemResult wir = _stp.QueueWorkItem(Action0); + Assert.IsNull(wir.State); + } + + [Test] + public void STPActionT1() + { + IWorkItemResult wir = _stp.QueueWorkItem(Action1, 17); + Assert.IsNull(wir.State); + } + + [Test] + public void STPActionT2() + { + IWorkItemResult wir = _stp.QueueWorkItem(Action2, 'a', "bla bla"); + Assert.IsNull(wir.State); + } + + [Test] + public void STPActionT3() + { + char[] chars = new char[] { 'a', 'b' }; + object obj = new object(); + + IWorkItemResult wir = _stp.QueueWorkItem(Action3, true, chars, obj); + Assert.IsNull(wir.State); + } + + [Test] + public void STPActionT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _stp.QueueWorkItem(Action4, long.MinValue, p, ip, guid); + Assert.IsNull(wir.State); + } + + [Test] + public void STPFuncT0() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func0)); + Assert.IsNull(wir.State); + } + + [Test] + public void STPFuncT1() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func1), 17); + Assert.IsNull(wir.State); + } + + [Test] + public void STPFuncT2() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func2), 'a', "bla bla"); + Assert.IsNull(wir.State); + } + + [Test] + public void STPFuncT3() + { + char[] chars = new char[] { 'a', 'b' }; + object obj = new object(); + + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func3), true, chars, obj); + Assert.IsNull(wir.State); + } + + [Test] + public void STPFuncT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGActionT0() + { + IWorkItemResult wir = _wig.QueueWorkItem(Action0); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGActionT1() + { + IWorkItemResult wir = _wig.QueueWorkItem(Action1, 17); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGActionT2() + { + IWorkItemResult wir = _wig.QueueWorkItem(Action2, 'a', "bla bla"); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGActionT3() + { + char[] chars = new char[] { 'a', 'b' }; + object obj = new object(); + + IWorkItemResult wir = _wig.QueueWorkItem(Action3, true, chars, obj); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGActionT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _wig.QueueWorkItem(Action4, long.MinValue, p, ip, guid); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGFuncT0() + { + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func0)); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGFuncT1() + { + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func1), 17); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGFuncT2() + { + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func2), 'a', "bla bla"); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGFuncT3() + { + char[] chars = new char[] { 'a', 'b' }; + object obj = new object(); + + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func3), true, chars, obj); + Assert.IsNull(wir.State); + } + + [Test] + public void WIGFuncT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); + Assert.IsNull(wir.State); + } + + + private void Action0() + { + } + + private void Action1(int p1) + { + } + + private void Action2(char p1, string p2) + { + } + + private void Action3(bool p1, char[] p2, object p3) + { + } + + private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4) + { + } + + private int Func0() + { + return 0; + } + + private bool Func1(int p1) + { + return true; + } + + private string Func2(char p1, string p2) + { + return "value"; + } + + private char Func3(bool p1, char[] p2, object p3) + { + return 'z'; + } + + private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4) + { + return IPAddress.None; + } + } +} diff --git a/STPTests/TestFillStateWithParams.cs b/STPTests/TestFillStateWithParams.cs index 2a99bdc..b08a07d 100644 --- a/STPTests/TestFillStateWithParams.cs +++ b/STPTests/TestFillStateWithParams.cs @@ -1,209 +1,209 @@ -using System; -using Amib.Threading; -using NUnit.Framework; -using System.Net; -using Guid = System.Guid; -using IntPtr = System.IntPtr; - -namespace STPTests -{ - /// - /// Summary description for TestFillStateWithArgs. - /// - [TestFixture] - [Category("TestFillStateWithArgs")] - public class TestFillStateWithArgs - { - private SmartThreadPool _stp; - - [SetUp] - public void Init() - { - STPStartInfo stpStartInfo = new STPStartInfo(); - stpStartInfo.FillStateWithArgs = true; - _stp = new SmartThreadPool(stpStartInfo); - } - - [TearDown] - public void Fini() - { - _stp.WaitForIdle(); - _stp.Shutdown(); - } - - [Test] - public void ActionT0() - { - IWorkItemResult wir = _stp.QueueWorkItem(Action0); - Assert.IsNull(wir.State); - } - - [Test] - public void ActionT1() - { - IWorkItemResult wir = _stp.QueueWorkItem(Action1, 17); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 1); - Assert.AreEqual(args[0], 17); - } - - [Test] - public void ActionT2() - { - IWorkItemResult wir = _stp.QueueWorkItem(Action2, 'a', "bla bla"); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 2); - Assert.AreEqual(args[0], 'a'); - Assert.AreEqual(args[1], "bla bla"); - } - - [Test] - public void ActionT3() - { - char[] chars = new char[] {'a', 'b'}; - object obj = new object(); - - IWorkItemResult wir = _stp.QueueWorkItem(Action3, true, chars, obj); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 3); - Assert.AreEqual(args[0], true); - Assert.AreEqual(args[1], chars); - Assert.AreEqual(args[2], obj); - } - - [Test] - public void ActionT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _stp.QueueWorkItem(Action4, long.MinValue, p, ip, guid); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 4); - Assert.AreEqual(args[0], long.MinValue); - Assert.AreEqual(args[1], p); - Assert.AreEqual(args[2], ip); - Assert.AreEqual(args[3], guid); - } - - [Test] - public void FuncT0() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func0)); - Assert.AreEqual(wir.State, null); - } - - [Test] - public void FuncT1() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func1), 17); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 1); - Assert.AreEqual(args[0], 17); - } - - [Test] - public void FuncT2() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func2), 'a', "bla bla"); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 2); - Assert.AreEqual(args[0], 'a'); - Assert.AreEqual(args[1], "bla bla"); - } - - [Test] - public void FuncT3() - { - char[] chars = new char[] { 'a', 'b' }; - object obj = new object(); - - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func3), true, chars, obj); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 3); - Assert.AreEqual(args[0], true); - Assert.AreEqual(args[1], chars); - Assert.AreEqual(args[2], obj); - } - - [Test] - public void FuncT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); - - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 4); - Assert.AreEqual(args[0], long.MinValue); - Assert.AreEqual(args[1], p); - Assert.AreEqual(args[2], ip); - Assert.AreEqual(args[3], guid); - } - - - private void Action0() - { - } - - private void Action1(int p1) - { - } - - private void Action2(char p1, string p2) - { - } - - private void Action3(bool p1, char [] p2, object p3) - { - } - - private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4) - { - } - - private int Func0() - { - return 0; - } - - private bool Func1(int p1) - { - return true; - } - - private string Func2(char p1, string p2) - { - return "value"; - } - - private char Func3(bool p1, char[] p2, object p3) - { - return 'z'; - } - - private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4) - { - return IPAddress.None; - } - } -} +using System; +using Amib.Threading; +using NUnit.Framework; +using System.Net; +using Guid = System.Guid; +using IntPtr = System.IntPtr; + +namespace SmartThreadPoolTests +{ + /// + /// Summary description for TestFillStateWithArgs. + /// + [TestFixture] + [Category("TestFillStateWithArgs")] + public class TestFillStateWithArgs + { + private SmartThreadPool _stp; + + [SetUp] + public void Init() + { + STPStartInfo stpStartInfo = new STPStartInfo(); + stpStartInfo.FillStateWithArgs = true; + _stp = new SmartThreadPool(stpStartInfo); + } + + [TearDown] + public void Fini() + { + _stp.WaitForIdle(); + _stp.Shutdown(); + } + + [Test] + public void ActionT0() + { + IWorkItemResult wir = _stp.QueueWorkItem(Action0); + Assert.IsNull(wir.State); + } + + [Test] + public void ActionT1() + { + IWorkItemResult wir = _stp.QueueWorkItem(Action1, 17); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 1); + Assert.AreEqual(args[0], 17); + } + + [Test] + public void ActionT2() + { + IWorkItemResult wir = _stp.QueueWorkItem(Action2, 'a', "bla bla"); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 2); + Assert.AreEqual(args[0], 'a'); + Assert.AreEqual(args[1], "bla bla"); + } + + [Test] + public void ActionT3() + { + char[] chars = new char[] {'a', 'b'}; + object obj = new object(); + + IWorkItemResult wir = _stp.QueueWorkItem(Action3, true, chars, obj); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 3); + Assert.AreEqual(args[0], true); + Assert.AreEqual(args[1], chars); + Assert.AreEqual(args[2], obj); + } + + [Test] + public void ActionT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _stp.QueueWorkItem(Action4, long.MinValue, p, ip, guid); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 4); + Assert.AreEqual(args[0], long.MinValue); + Assert.AreEqual(args[1], p); + Assert.AreEqual(args[2], ip); + Assert.AreEqual(args[3], guid); + } + + [Test] + public void FuncT0() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func0)); + Assert.AreEqual(wir.State, null); + } + + [Test] + public void FuncT1() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func1), 17); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 1); + Assert.AreEqual(args[0], 17); + } + + [Test] + public void FuncT2() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func2), 'a', "bla bla"); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 2); + Assert.AreEqual(args[0], 'a'); + Assert.AreEqual(args[1], "bla bla"); + } + + [Test] + public void FuncT3() + { + char[] chars = new char[] { 'a', 'b' }; + object obj = new object(); + + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func3), true, chars, obj); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 3); + Assert.AreEqual(args[0], true); + Assert.AreEqual(args[1], chars); + Assert.AreEqual(args[2], obj); + } + + [Test] + public void FuncT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); + + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 4); + Assert.AreEqual(args[0], long.MinValue); + Assert.AreEqual(args[1], p); + Assert.AreEqual(args[2], ip); + Assert.AreEqual(args[3], guid); + } + + + private void Action0() + { + } + + private void Action1(int p1) + { + } + + private void Action2(char p1, string p2) + { + } + + private void Action3(bool p1, char [] p2, object p3) + { + } + + private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4) + { + } + + private int Func0() + { + return 0; + } + + private bool Func1(int p1) + { + return true; + } + + private string Func2(char p1, string p2) + { + return "value"; + } + + private char Func3(bool p1, char[] p2, object p3) + { + return 'z'; + } + + private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4) + { + return IPAddress.None; + } + } +} diff --git a/STPTests/TestFunc.cs b/STPTests/TestFunc.cs deleted file mode 100644 index cdc1345..0000000 --- a/STPTests/TestFunc.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -using Amib.Threading; -using NUnit.Framework; - -namespace STPTests -{ - /// - /// Summary description for TestCancel. - /// - [TestFixture] - [Category("TestFuncT")] - public class TestFuncT - { - [Test] - public void FuncT() - { - SmartThreadPool stp = new SmartThreadPool(); - IWorkItemResult wir = - stp.QueueWorkItem(new Func(f), 1); - - int y = wir.GetResult(); - - Assert.AreEqual(y, 2); - - try - { - wir.GetResult(); - } - finally - { - stp.Shutdown(); - } - } - - private int f(int x) - { - return x + 1; - } - } -} diff --git a/STPTests/TestFuncT.cs b/STPTests/TestFuncT.cs index ac00b4b..4ec8eb6 100644 --- a/STPTests/TestFuncT.cs +++ b/STPTests/TestFuncT.cs @@ -1,109 +1,125 @@ -using System; -using Amib.Threading; -using NUnit.Framework; - -namespace SmartThreadPoolTests -{ - /// - /// Summary description for TestCancel. - /// - [TestFixture] - [Category("TestFuncT")] - public class TestFuncT - { - private SmartThreadPool _stp; - - [SetUp] - public void Init() - { - _stp = new SmartThreadPool(); - } - - [TearDown] - public void Fini() - { - _stp.Shutdown(); - } - - [Test] - public void FuncT0() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(MaxInt)); - - int result = wir.GetResult(); - - Assert.AreEqual(result, int.MaxValue); - } - - [Test] - public void FuncT1() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Not), true); - - bool result = wir.Result; - - Assert.AreEqual(result, false); - } - - [Test] - public void FuncT2() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(string.Concat), "ABC", "xyz"); - - string result = wir.Result; - - Assert.AreEqual(result, "ABCxyz"); - } - - [Test] - public void FuncT3() - { - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Substring), "ABCDEF", 1, 2); - - string result = wir.Result; - - Assert.AreEqual(result, "BC"); - } - - [Test] - public void FuncT4() - { - int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - - IWorkItemResult wir = _stp.QueueWorkItem(new Func(Subarray), numbers, 1, 2, 3); - - int[] result = wir.Result; - - Assert.AreEqual(result, new int[] { 2, 3, 2, 3, 2, 3, }); - } - - private int MaxInt() - { - return int.MaxValue; - } - - private bool Not(bool flag) - { - return !flag; - } - - private string Substring(string s, int startIndex, int length) - { - return s.Substring(startIndex, length); - } - - private int[] Subarray(int[] numbers, int startIndex, int length, int repeat) - { - int[] result = new int[length * repeat]; - for (int i = 0; i < repeat; i++) - { - for (int j = 0; j < length; j++) - { - result[i * length + j] = numbers[startIndex + j]; - } - } - - return result; - } - } -} +using System; +using Amib.Threading; +using NUnit.Framework; + +namespace SmartThreadPoolTests +{ + /// + /// Summary description for TestCancel. + /// + [TestFixture] + [Category("TestFuncT")] + public class TestFuncT + { + private SmartThreadPool _stp; + + [SetUp] + public void Init() + { + _stp = new SmartThreadPool(); + } + + [TearDown] + public void Fini() + { + _stp.Shutdown(); + } + + [Test] + public void FuncInt1() + { + IWorkItemResult wir = + _stp.QueueWorkItem(new Func(Increment), 1); + + int y = wir.GetResult(); + + Assert.AreEqual(y, 2); + } + + [Test] + public void FuncInt2() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(MaxInt)); + + int result = wir.GetResult(); + + Assert.AreEqual(result, int.MaxValue); + } + + [Test] + public void FuncBool() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Not), true); + + bool result = wir.Result; + + Assert.AreEqual(result, false); + } + + [Test] + public void FuncString1() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(string.Concat), "ABC", "xyz"); + + string result = wir.Result; + + Assert.AreEqual(result, "ABCxyz"); + } + + [Test] + public void FuncString2() + { + IWorkItemResult wir = _stp.QueueWorkItem(new Func(Substring), "ABCDEF", 1, 2); + + string result = wir.Result; + + Assert.AreEqual(result, "BC"); + } + + [Test] + public void FuncIntArray() + { + int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + IWorkItemResult wir = _stp.QueueWorkItem(new Func(SubArray), numbers, 1, 2, 3); + + int[] result = wir.Result; + + Assert.AreEqual(result, new int[] { 2, 3, 2, 3, 2, 3, }); + } + + private int Increment(int x) + { + return x + 1; + } + + private int MaxInt() + { + return int.MaxValue; + } + + private bool Not(bool flag) + { + return !flag; + } + + private string Substring(string s, int startIndex, int length) + { + return s.Substring(startIndex, length); + } + + private int[] SubArray(int[] numbers, int startIndex, int length, int repeat) + { + int[] result = new int[length * repeat]; + for (int i = 0; i < repeat; i++) + { + for (int j = 0; j < length; j++) + { + result[i * length + j] = numbers[startIndex + j]; + } + } + + return result; + } + } +} diff --git a/STPTests/TestMaxQueueLength.cs b/STPTests/TestMaxQueueLength.cs index 7b119e6..6d7040c 100644 --- a/STPTests/TestMaxQueueLength.cs +++ b/STPTests/TestMaxQueueLength.cs @@ -3,7 +3,7 @@ using Amib.Threading; using NUnit.Framework; -namespace STPTests +namespace SmartThreadPoolTests { [TestFixture] [Category("TestMaxQueueLength")] diff --git a/STPTests/TestThreadApartmentState.cs b/STPTests/TestThreadApartmentState.cs index 9e43a45..2528d76 100644 --- a/STPTests/TestThreadApartmentState.cs +++ b/STPTests/TestThreadApartmentState.cs @@ -1,7 +1,9 @@ +using System; +using System.Diagnostics; using System.Threading; using NUnit.Framework; -using Amib.Threading; - +using Amib.Threading; + namespace SmartThreadPoolTests { /// @@ -14,6 +16,7 @@ public class TestThreadApartmentState [Test] public void TestSTA() { + if (Environment.OSVersion.Platform == PlatformID.Win32NT) CheckApartmentState(ApartmentState.STA); } @@ -44,4 +47,4 @@ private static ApartmentState GetCurrentThreadApartmentState() return Thread.CurrentThread.GetApartmentState(); } } -} \ No newline at end of file +} diff --git a/STPTests/TestWIGAsyncAwait.cs b/STPTests/TestWIGAsyncAwait.cs new file mode 100644 index 0000000..6fff48d --- /dev/null +++ b/STPTests/TestWIGAsyncAwait.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Amib.Threading; +using NUnit.Framework; + +namespace AsyncAwait +{ + [TestFixture] + [Category("TestAsyncAwait")] + public class TestWIGAsyncAwait : TestAsyncAwaitBase + { + protected SmartThreadPool _stp; + private IWorkItemsGroup _wig; + + [OneTimeSetUp] + public void Init() + { + Console.WriteLine("Creating SmartThreadPool"); + _stp = new SmartThreadPool(); + _wig = _stp.CreateWorkItemsGroup(10); + } + + [OneTimeTearDown] + public void Fini() + { + Console.WriteLine("Shutting down SmartThreadPool"); + _stp.Shutdown(); + } + + protected override IWorkItemsGroup GetWIG() => _wig; + + [Test] + public async Task AwaitSerialSingle() + { + var wig = _stp.CreateWorkItemsGroup(1); + + async Task DoProduce() + { + await Task.Delay(1000); + + return 17; + } + + var wirProducer = wig.QueueWorkItem(DoProduce); + + async Task DoConsume() + { + var result = await wirProducer.GetResultAsync(); + + return result == 17; + } + + var wirConsumer = wig.QueueWorkItem(DoConsume); + + bool resultConsumer = await wirConsumer.GetResultAsync(); + + Assert.IsTrue(resultConsumer); + Assert.IsTrue(wirConsumer.Result); + } + + [Test] + public async Task AwaitSerialMultiple() + { + ConcurrentDictionary threads = new ConcurrentDictionary(); + + SmartThreadPool smartThreadPool = new SmartThreadPool(); + var wig = smartThreadPool.CreateWorkItemsGroup(1); + + var wirAdders = new IWorkItemResult[10]; + var wirCheckers = new IWorkItemResult[wirAdders.Length]; + + async Task DoAdd(int x, int y) + { + if (wig.InUseThreads > 1) + throw new Exception("Using too many threads"); + + threads.TryAdd(Thread.CurrentThread.ManagedThreadId, true); + + await Task.Delay(100); + + if (wig.InUseThreads > 1) + throw new Exception("Using too many threads"); + + threads.TryAdd(Thread.CurrentThread.ManagedThreadId, true); + + return x + y; + } + + for (int i = 0; i < wirAdders.Length; i++) + { + wirAdders[i] = wig.QueueWorkItem(DoAdd, i, i + 1); + } + + async Task DoCheck(int index, Task task) + { + if (wig.InUseThreads > 1) + throw new Exception("Using too many threads"); + + threads.TryAdd(Thread.CurrentThread.ManagedThreadId, true); + + var result = await task; + + if (wig.InUseThreads > 1) + throw new Exception("Using too many threads"); + + threads.TryAdd(Thread.CurrentThread.ManagedThreadId, true); + + return result == (index + index + 1); + } + + for (int i = 0; i < wirCheckers.Length; i++) + { + wirCheckers[i] = wig.QueueWorkItem(DoCheck, i, wirAdders[i].GetResultAsync()); + } + + await wig.WaitForIdleAsync(5_000); + + for (int i = 0; i < wirCheckers.Length; i++) + { + Assert.IsTrue(wirCheckers[i].IsCompleted); + Assert.IsTrue(wirCheckers[i].Result); + } + + smartThreadPool.Shutdown(); + } + + [Test] + public async Task InterleavingTasks_Yield() + { + SmartThreadPool stp = new SmartThreadPool(); + var wig = stp.CreateWorkItemsGroup(1, new WIGStartInfo() { StartSuspended = true,}); + + int count = 1000; + var list = new System.Collections.Generic.List(count); + + async Task AddEvens() + { + for (int i = 0; i < count; i += 2) + { + list.Add(i); + await Task.Yield(); + } + } + + async Task AddOdds() + { + for (int i = 1; i < count; i+=2) + { + list.Add(i); + await Task.Yield(); + } + } + + Assert.IsTrue(stp.IsIdle); + Assert.IsTrue(wig.IsIdle); + + wig.QueueWorkItem(AddEvens); + wig.QueueWorkItem(AddOdds); + + Assert.IsTrue(stp.IsIdle); + Assert.IsFalse(wig.IsIdle); + + wig.Start(); + + Assert.IsFalse(stp.IsIdle); + Assert.IsFalse(wig.IsIdle); + + await wig.WaitForIdleAsync(); + + Assert.That(list, Has.Count.EqualTo(count)); + Assert.That(list, Is.EquivalentTo(Enumerable.Range(0, count))); + } + } +} \ No newline at end of file diff --git a/STPTests/TestWIGFillStateWithParams.cs b/STPTests/TestWIGFillStateWithParams.cs index d53d375..18f1d0b 100644 --- a/STPTests/TestWIGFillStateWithParams.cs +++ b/STPTests/TestWIGFillStateWithParams.cs @@ -1,212 +1,212 @@ -using System; -using Amib.Threading; -using NUnit.Framework; -using System.Net; -using Guid = System.Guid; -using IntPtr = System.IntPtr; - -namespace STPTests -{ - /// - /// Summary description for TestWIGFillStateWithArgs. - /// - [TestFixture] - [Category("TestWIGFillStateWithArgs")] - public class TestWIGFillStateWithArgs - { - private SmartThreadPool _stp; - private IWorkItemsGroup _wig; - - [SetUp] - public void Init() - { - _stp = new SmartThreadPool(); - - WIGStartInfo wigStartInfo = new WIGStartInfo(); - wigStartInfo.FillStateWithArgs = true; - _wig = _stp.CreateWorkItemsGroup(10, wigStartInfo); - } - - [TearDown] - public void Fini() - { - _stp.WaitForIdle(); - _stp.Shutdown(); - } - - [Test] - public void ActionT0() - { - IWorkItemResult wir = _wig.QueueWorkItem(Action0); - Assert.IsNull(wir.State); - } - - [Test] - public void ActionT1() - { - IWorkItemResult wir = _wig.QueueWorkItem(Action1, 17); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 1); - Assert.AreEqual(args[0], 17); - } - - [Test] - public void ActionT2() - { - IWorkItemResult wir = _wig.QueueWorkItem(Action2, 'a', "bla bla"); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 2); - Assert.AreEqual(args[0], 'a'); - Assert.AreEqual(args[1], "bla bla"); - } - - [Test] - public void ActionT3() - { - char[] chars = new char[] { 'a', 'b' }; - object obj = new object(); - - IWorkItemResult wir = _wig.QueueWorkItem(Action3, true, chars, obj); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 3); - Assert.AreEqual(args[0], true); - Assert.AreEqual(args[1], chars); - Assert.AreEqual(args[2], obj); - } - - [Test] - public void ActionT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _wig.QueueWorkItem(Action4, long.MinValue, p, ip, guid); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 4); - Assert.AreEqual(args[0], long.MinValue); - Assert.AreEqual(args[1], p); - Assert.AreEqual(args[2], ip); - Assert.AreEqual(args[3], guid); - } - - [Test] - public void FuncT0() - { - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func0)); - Assert.AreEqual(wir.State, null); - } - - [Test] - public void FuncT1() - { - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func1), 17); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 1); - Assert.AreEqual(args[0], 17); - } - - [Test] - public void FuncT2() - { - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func2), 'a', "bla bla"); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 2); - Assert.AreEqual(args[0], 'a'); - Assert.AreEqual(args[1], "bla bla"); - } - - [Test] - public void FuncT3() - { - char[] chars = new char[] { 'a', 'b' }; - object obj = new object(); - - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func3), true, chars, obj); - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 3); - Assert.AreEqual(args[0], true); - Assert.AreEqual(args[1], chars); - Assert.AreEqual(args[2], obj); - } - - [Test] - public void FuncT4() - { - IntPtr p = new IntPtr(int.MaxValue); - Guid guid = Guid.NewGuid(); - - IPAddress ip = IPAddress.Parse("1.2.3.4"); - IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); - - object[] args = wir.State as object[]; - - Assert.IsNotNull(args); - Assert.AreEqual(args.Length, 4); - Assert.AreEqual(args[0], long.MinValue); - Assert.AreEqual(args[1], p); - Assert.AreEqual(args[2], ip); - Assert.AreEqual(args[3], guid); - } - - - private void Action0() - { - } - - private void Action1(int p1) - { - } - - private void Action2(char p1, string p2) - { - } - - private void Action3(bool p1, char[] p2, object p3) - { - } - - private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4) - { - } - - private int Func0() - { - return 0; - } - - private bool Func1(int p1) - { - return true; - } - - private string Func2(char p1, string p2) - { - return "value"; - } - - private char Func3(bool p1, char[] p2, object p3) - { - return 'z'; - } - - private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4) - { - return IPAddress.None; - } - } -} +using System; +using Amib.Threading; +using NUnit.Framework; +using System.Net; +using Guid = System.Guid; +using IntPtr = System.IntPtr; + +namespace SmartThreadPoolTests +{ + /// + /// Summary description for TestWIGFillStateWithArgs. + /// + [TestFixture] + [Category("TestWIGFillStateWithArgs")] + public class TestWIGFillStateWithArgs + { + private SmartThreadPool _stp; + private IWorkItemsGroup _wig; + + [SetUp] + public void Init() + { + _stp = new SmartThreadPool(); + + WIGStartInfo wigStartInfo = new WIGStartInfo(); + wigStartInfo.FillStateWithArgs = true; + _wig = _stp.CreateWorkItemsGroup(10, wigStartInfo); + } + + [TearDown] + public void Fini() + { + _stp.WaitForIdle(); + _stp.Shutdown(); + } + + [Test] + public void ActionT0() + { + IWorkItemResult wir = _wig.QueueWorkItem(Action0); + Assert.IsNull(wir.State); + } + + [Test] + public void ActionT1() + { + IWorkItemResult wir = _wig.QueueWorkItem(Action1, 17); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 1); + Assert.AreEqual(args[0], 17); + } + + [Test] + public void ActionT2() + { + IWorkItemResult wir = _wig.QueueWorkItem(Action2, 'a', "bla bla"); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 2); + Assert.AreEqual(args[0], 'a'); + Assert.AreEqual(args[1], "bla bla"); + } + + [Test] + public void ActionT3() + { + char[] chars = new char[] { 'a', 'b' }; + object obj = new object(); + + IWorkItemResult wir = _wig.QueueWorkItem(Action3, true, chars, obj); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 3); + Assert.AreEqual(args[0], true); + Assert.AreEqual(args[1], chars); + Assert.AreEqual(args[2], obj); + } + + [Test] + public void ActionT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _wig.QueueWorkItem(Action4, long.MinValue, p, ip, guid); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 4); + Assert.AreEqual(args[0], long.MinValue); + Assert.AreEqual(args[1], p); + Assert.AreEqual(args[2], ip); + Assert.AreEqual(args[3], guid); + } + + [Test] + public void FuncT0() + { + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func0)); + Assert.AreEqual(wir.State, null); + } + + [Test] + public void FuncT1() + { + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func1), 17); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 1); + Assert.AreEqual(args[0], 17); + } + + [Test] + public void FuncT2() + { + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func2), 'a', "bla bla"); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 2); + Assert.AreEqual(args[0], 'a'); + Assert.AreEqual(args[1], "bla bla"); + } + + [Test] + public void FuncT3() + { + char[] chars = new char[] { 'a', 'b' }; + object obj = new object(); + + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func3), true, chars, obj); + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 3); + Assert.AreEqual(args[0], true); + Assert.AreEqual(args[1], chars); + Assert.AreEqual(args[2], obj); + } + + [Test] + public void FuncT4() + { + IntPtr p = new IntPtr(int.MaxValue); + Guid guid = Guid.NewGuid(); + + IPAddress ip = IPAddress.Parse("1.2.3.4"); + IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid); + + object[] args = wir.State as object[]; + + Assert.IsNotNull(args); + Assert.AreEqual(args.Length, 4); + Assert.AreEqual(args[0], long.MinValue); + Assert.AreEqual(args[1], p); + Assert.AreEqual(args[2], ip); + Assert.AreEqual(args[3], guid); + } + + + private void Action0() + { + } + + private void Action1(int p1) + { + } + + private void Action2(char p1, string p2) + { + } + + private void Action3(bool p1, char[] p2, object p3) + { + } + + private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4) + { + } + + private int Func0() + { + return 0; + } + + private bool Func1(int p1) + { + return true; + } + + private string Func2(char p1, string p2) + { + return "value"; + } + + private char Func3(bool p1, char[] p2, object p3) + { + return 'z'; + } + + private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4) + { + return IPAddress.None; + } + } +} diff --git a/SmartThreadPool/Interfaces.cs b/SmartThreadPool/Interfaces.cs index ed9622e..b21e682 100644 --- a/SmartThreadPool/Interfaces.cs +++ b/SmartThreadPool/Interfaces.cs @@ -1,585 +1,665 @@ -using System; -using System.Threading; - -namespace Amib.Threading -{ - #region Delegates - - /// - /// A delegate that represents the method to run as the work item - /// - /// A state object for the method to run - public delegate object WorkItemCallback(object state); - - /// - /// A delegate to call after the WorkItemCallback completed - /// - /// The work item result object - public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); - - /// - /// A delegate to call after the WorkItemCallback completed - /// - /// The work item result object - public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); - - /// - /// A delegate to call when a WorkItemsGroup becomes idle - /// - /// A reference to the WorkItemsGroup that became idle - public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); - - /// - /// A delegate to call after a thread is created, but before - /// it's first use. - /// - public delegate void ThreadInitializationHandler(); - - /// - /// A delegate to call when a thread is about to exit, after - /// it is no longer belong to the pool. - /// - public delegate void ThreadTerminationHandler(); - - #endregion - - #region WorkItem Priority - - /// - /// Defines the availeable priorities of a work item. - /// The higher the priority a work item has, the sooner - /// it will be executed. - /// - public enum WorkItemPriority - { - Lowest, - BelowNormal, - Normal, - AboveNormal, - Highest, - } - - #endregion - - #region IWorkItemsGroup interface - - /// - /// IWorkItemsGroup interface - /// Created by SmartThreadPool.CreateWorkItemsGroup() - /// - public interface IWorkItemsGroup - { - /// - /// Get/Set the name of the WorkItemsGroup - /// - string Name { get; set; } - - /// - /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool - /// - int Concurrency { get; set; } - - /// - /// Get the number of work items waiting in the queue. - /// - int WaitingCallbacks { get; } - - /// - /// Get the number of currently executing work items - /// - int InUseThreads { get; } - - /// - /// Get an array with all the state objects of the currently running items. - /// The array represents a snap shot and impact performance. - /// - object[] GetStates(); - - /// - /// Get the WorkItemsGroup start information - /// - WIGStartInfo WIGStartInfo { get; } - - /// - /// Starts to execute work items - /// - void Start(); - - /// - /// Cancel all the work items. - /// Same as Cancel(false) - /// - void Cancel(); - - /// - /// Cancel all work items using thread abortion - /// - /// True to stop work items by raising ThreadAbortException - void Cancel(bool abortExecution); - - /// - /// Wait for all work item to complete. - /// - void WaitForIdle(); - - /// - /// Wait for all work item to complete, until timeout expired - /// - /// How long to wait for the work items to complete - /// Returns true if work items completed within the timeout, otherwise false. - bool WaitForIdle(TimeSpan timeout); - - /// - /// Wait for all work item to complete, until timeout expired - /// - /// How long to wait for the work items to complete in milliseconds - /// Returns true if work items completed within the timeout, otherwise false. - bool WaitForIdle(int millisecondsTimeout); - - /// - /// IsIdle is true when there are no work items running or queued. - /// - bool IsIdle { get; } - - /// - /// This event is fired when all work items are completed. - /// (When IsIdle changes to true) - /// This event only work on WorkItemsGroup. On SmartThreadPool - /// it throws the NotImplementedException. - /// - event WorkItemsGroupIdleHandler OnIdle; - - #region QueueWorkItem - - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback); - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); - - #endregion - - #region QueueWorkItem(Action<...>) - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult object, but its GetResult() will always return null - IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - #endregion - - #region QueueWorkItem(Func<...>) - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - /// - /// Queue a work item. - /// - /// Returns a IWorkItemResult<TResult> object. - /// its GetResult() returns a TResult object - IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); - - #endregion - } - - #endregion - - #region CallToPostExecute enumerator - - [Flags] - public enum CallToPostExecute - { - /// - /// Never call to the PostExecute call back - /// - Never = 0x00, - - /// - /// Call to the PostExecute only when the work item is cancelled - /// - WhenWorkItemCanceled = 0x01, - - /// - /// Call to the PostExecute only when the work item is not cancelled - /// - WhenWorkItemNotCanceled = 0x02, - - /// - /// Always call to the PostExecute - /// - Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, - } - - #endregion - - #region IWorkItemResult interface - - /// - /// The common interface of IWorkItemResult and IWorkItemResult<T> - /// - public interface IWaitableResult - { - /// - /// This method intent is for internal use. - /// - /// - IWorkItemResult GetWorkItemResult(); - - /// - /// This method intent is for internal use. - /// - /// - IWorkItemResult GetWorkItemResultT(); - } - - /// - /// IWorkItemResult interface. - /// Created when a WorkItemCallback work item is queued. - /// - public interface IWorkItemResult : IWorkItemResult - { - } - - /// - /// IWorkItemResult<TResult> interface. - /// Created when a Func<TResult> work item is queued. - /// - public interface IWorkItemResult : IWaitableResult - { - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. - /// - /// The result of the work item - TResult GetResult(); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - int millisecondsTimeout, - bool exitContext); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - TimeSpan timeout, - bool exitContext); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits. - /// - /// Filled with the exception if one was thrown - /// The result of the work item - TResult GetResult(out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// - /// - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - int millisecondsTimeout, - bool exitContext, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout. - /// - /// - /// Filled with the exception if one was thrown - /// - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - TResult GetResult( - TimeSpan timeout, - bool exitContext, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// Timeout in milliseconds, or -1 for infinite - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the blocking if needed - /// Filled with the exception if one was thrown - /// The result of the work item - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. - /// - /// The result of the work item - /// - /// Filled with the exception if one was thrown - /// - /// - /// On timeout throws WorkItemTimeoutException - /// On cancel throws WorkItemCancelException - TResult GetResult( - TimeSpan timeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e); - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - bool IsCompleted { get; } - - /// - /// Gets an indication whether the asynchronous operation has been canceled. - /// - bool IsCanceled { get; } - - /// - /// Gets the user-defined object that contains context data - /// for the work item method. - /// - object State { get; } - - /// - /// Same as Cancel(false). - /// - bool Cancel(); - - /// - /// Cancel the work item execution. - /// If the work item is in the queue then it won't execute - /// If the work item is completed, it will remain completed - /// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled - /// property to check if the work item has been cancelled. If the abortExecution is set to true then - /// the Smart Thread Pool will send an AbortException to the running thread to stop the execution - /// of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException. - /// If the work item is already cancelled it will remain cancelled - /// - /// When true send an AbortException to the executing thread. - /// Returns true if the work item was not completed, otherwise false. - bool Cancel(bool abortExecution); - - /// - /// Get the work item's priority - /// - WorkItemPriority WorkItemPriority { get; } - - /// - /// Return the result, same as GetResult() - /// - TResult Result { get; } - - /// - /// Returns the exception if occured otherwise returns null. - /// - object Exception { get; } - } - - #endregion -} +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Amib.Threading +{ + #region Delegates + + /// + /// A delegate that represents the method to run as the work item + /// + /// A state object for the method to run + public delegate object WorkItemCallback(object state); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call when a WorkItemsGroup becomes idle + /// + /// A reference to the WorkItemsGroup that became idle + public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); + + /// + /// A delegate to call after a thread is created, but before + /// it's first use. + /// + public delegate void ThreadInitializationHandler(); + + /// + /// A delegate to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + public delegate void ThreadTerminationHandler(); + + #endregion + + #region WorkItem Priority + + /// + /// Defines the availeable priorities of a work item. + /// The higher the priority a work item has, the sooner + /// it will be executed. + /// + public enum WorkItemPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } + + #endregion + + #region IWorkItemsGroup interface + + /// + /// IWorkItemsGroup interface + /// Created by SmartThreadPool.CreateWorkItemsGroup() + /// + public interface IWorkItemsGroup + { + /// + /// Get/Set the name of the WorkItemsGroup + /// + string Name { get; set; } + + /// + /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool + /// + int Concurrency { get; set; } + + /// + /// Get the number of work items waiting in the queue. + /// + int WaitingCallbacks { get; } + + /// + /// Get the number of currently executing work items + /// + int InUseThreads { get; } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + object[] GetStates(); + + /// + /// Get the WorkItemsGroup start information + /// + WIGStartInfo WIGStartInfo { get; } + + /// + /// Starts to execute work items + /// + void Start(); + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + void Cancel(); + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + void Cancel(bool abortExecution); + + /// + /// Wait for all work item to complete. + /// + void WaitForIdle(); + +#if _ASYNC_SUPPORTED + /// + /// Wait asynchronously for all work item to complete. + /// + Task WaitForIdleAsync(CancellationToken? cancellationToken = null); + + /// + /// Wait asynchronously for all work item to complete. + /// + Task WaitForIdleAsync(TimeSpan timeout); + + /// + /// Wait asynchronously for all work item to complete. + /// + Task WaitForIdleAsync(int millisecondsTimeout); +#endif + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(TimeSpan timeout); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete in milliseconds + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(int millisecondsTimeout); + + /// + /// IsIdle is true when there are no work items running or queued. + /// + bool IsIdle { get; } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + event WorkItemsGroupIdleHandler OnIdle; + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); + + #endregion + + #region QueueWorkItem(Action<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + #endregion + + #region QueueWorkItem(Func<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + #endregion +#if _ASYNC_SUPPORTED + #region RunTask(Func) ==> async Task DoWork(..) + + Task RunTask(Action action, CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + Task RunTask(Func function, CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + Task RunTask(Func function, CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + Task RunTask(Func> function, CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + #endregion +#endif + + #region QueueWorkItem(Func) ==> async Task DoWork(..) + + IWorkItemResult QueueWorkItem(Func func, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func func, T arg, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + #endregion + + #region QueueWorkItem(Func, ...>) ==> async Task DoWork(..) + + IWorkItemResult QueueWorkItem(Func> func, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func> func, T arg, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func> func, T1 arg1, T2 arg2, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func> func, T1 arg1, + T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + IWorkItemResult QueueWorkItem(Func> func, + T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority); + + #endregion + } + + #endregion + + #region CallToPostExecute enumerator + + [Flags] + public enum CallToPostExecute + { + /// + /// Never call to the PostExecute call back + /// + Never = 0x00, + + /// + /// Call to the PostExecute only when the work item is cancelled + /// + WhenWorkItemCanceled = 0x01, + + /// + /// Call to the PostExecute only when the work item is not cancelled + /// + WhenWorkItemNotCanceled = 0x02, + + /// + /// Always call to the PostExecute + /// + Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, + } + + #endregion + + #region IWorkItemResult interface + + /// + /// The common interface of IWorkItemResult and IWorkItemResult<T> + /// + public interface IWaitableResult + { + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResult(); + + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResultT(); + } + + /// + /// IWorkItemResult interface. + /// Created when a WorkItemCallback work item is queued. + /// + public interface IWorkItemResult : IWorkItemResult + { + } + + /// + /// IWorkItemResult<TResult> interface. + /// Created when a Func<TResult> work item is queued. + /// + public interface IWorkItemResult : IWaitableResult + { + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// The result of the work item + TResult GetResult(); + +#if _ASYNC_SUPPORTED + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// The result of the work item + Task GetResultAsync(); +#endif + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// Filled with the exception if one was thrown + /// The result of the work item + TResult GetResult(out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// Filled with the exception if one was thrown + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// + /// Filled with the exception if one was thrown + /// + /// + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Gets an indication whether the asynchronous operation has completed. + /// + bool IsCompleted { get; } + + /// + /// Gets an indication whether the asynchronous operation has been canceled. + /// + bool IsCanceled { get; } + + /// + /// Gets the user-defined object that contains context data + /// for the work item method. + /// + object State { get; } + + /// + /// Same as Cancel(false). + /// + bool Cancel(); + + /// + /// Cancel the work item execution. + /// If the work item is in the queue then it won't execute + /// If the work item is completed, it will remain completed + /// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled + /// property to check if the work item has been cancelled. If the abortExecution is set to true then + /// the Smart Thread Pool will send an AbortException to the running thread to stop the execution + /// of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException. + /// If the work item is already cancelled it will remain cancelled + /// + /// When true send an AbortException to the executing thread. + /// Returns true if the work item was not completed, otherwise false. + bool Cancel(bool abortExecution); + + /// + /// Get the work item's priority + /// + WorkItemPriority WorkItemPriority { get; } + + /// + /// Return the result, same as GetResult() + /// + TResult Result { get; } + + /// + /// Returns the exception if occured otherwise returns null. + /// + object Exception { get; } + } + + #endregion +} diff --git a/SmartThreadPool/InternalInterfaces.cs b/SmartThreadPool/InternalInterfaces.cs index b1efa34..a0cd9ae 100644 --- a/SmartThreadPool/InternalInterfaces.cs +++ b/SmartThreadPool/InternalInterfaces.cs @@ -1,15 +1,34 @@  +using System; + namespace Amib.Threading.Internal { + [Flags] + internal enum WorkItemExecutionStatus + { + Started = 0b_11, + Executing = 0b_01, + Awaiting = 0b_00, + Completed = 0b_10, + } + /// - /// An internal delegate to call when the WorkItem starts or completes + /// An internal delegate to call when a WorkItem starts or completes /// internal delegate void WorkItemStateCallback(WorkItem workItem); + + /// + /// An internal delegate to call when a WorkItem starts or stops executing. + /// + /// The WorkItemsGroup needs to know how many work items are currently executing, hence using a thread. + /// An awaiting async method doesn't use a thread so another work item may execute. + /// + internal delegate void WorkItemExecutionStatusCallback(WorkItem workItem, WorkItemExecutionStatus status); + internal interface IInternalWorkItemResult { - event WorkItemStateCallback OnWorkItemStarted; - event WorkItemStateCallback OnWorkItemCompleted; + event WorkItemExecutionStatusCallback OnWorkItemExecutionStatusChanged; } internal interface IInternalWaitableResult diff --git a/SmartThreadPool/STPSynchronizationContext.cs b/SmartThreadPool/STPSynchronizationContext.cs new file mode 100644 index 0000000..6030857 --- /dev/null +++ b/SmartThreadPool/STPSynchronizationContext.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +#if _ASYNC_SUPPORTED + +namespace Amib.Threading.Internal +{ + class STPSynchronizationContext : SynchronizationContext + { + private WorkItem WorkItem { get; } + + public STPSynchronizationContext(WorkItem workItem) + { + WorkItem = workItem; + } + + public override SynchronizationContext CreateCopy() + { + return new STPSynchronizationContext(WorkItem); + } + + public override void Post(SendOrPostCallback d, object state) + { + WorkItem.HandleRequeue(d, state); + } + + public override void OperationStarted() + { + WorkItem.SetTaskState(false); + } + + public override void OperationCompleted() + { + WorkItem.SetTaskState(true); + } + } +} + +#endif \ No newline at end of file diff --git a/SmartThreadPool/SmartThreadPool.cs b/SmartThreadPool/SmartThreadPool.cs index dc067a5..f25ee1d 100644 --- a/SmartThreadPool/SmartThreadPool.cs +++ b/SmartThreadPool/SmartThreadPool.cs @@ -120,7 +120,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; - +using System.Threading.Tasks; using Amib.Threading.Internal; using Stopwatch = System.Diagnostics.Stopwatch; @@ -132,7 +132,7 @@ namespace Amib.Threading /// public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable { - #region Public Default Constants +#region Public Default Constants /// /// Default minimum number of threads the thread pool contains. (0) @@ -229,9 +229,9 @@ public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable /// public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; - #endregion +#endregion - #region Member Variables +#region Member Variables /// /// Dictionary of all the threads in the thread pool. @@ -273,6 +273,13 @@ public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); +#if _ASYNC_SUPPORTED + /// + /// A task completion source to indicate that the STP is idle, used in WaitForIdleAsync + /// + private TaskCompletionSource _isIdleTCS; +#endif + /// /// An event to signal all the threads to quit immediately. /// @@ -338,9 +345,9 @@ public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable /// private event ThreadTerminationHandler _onThreadTermination; - #endregion +#endregion - #region Per thread properties +#region Per thread properties /// /// A reference to the current work item a thread from the thread pool @@ -358,9 +365,9 @@ internal static ThreadEntry CurrentThreadEntry } } - #endregion +#endregion - #region Construction and Finalization +#region Construction and Finalization /// /// Constructor @@ -455,7 +462,7 @@ private void Initialize() _isSuspended = _stpStartInfo.StartSuspended; #if !(NETFRAMEWORK) - if (null != _stpStartInfo.PerformanceCounterInstanceName) + if (null != _stpStartInfo.PerformanceCounterInstanceName) { throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); } @@ -534,9 +541,9 @@ private static void ValidateCallback(Delegate callback) } } - #endregion +#endregion - #region Thread Processing +#region Thread Processing /// /// Waits on the queue for a work item, shutdown, or timeout. @@ -559,22 +566,30 @@ private WorkItem Dequeue() internal override void Enqueue(WorkItem workItem) { // Make sure the workItem is not null - Debug.Assert(null != workItem); + Debug.Assert(null != workItem); - IncrementWorkItemsCount(); +#if _ASYNC_SUPPORTED + if (!workItem.IsAsync) +#endif + { + IncrementWorkItemsCount(); + } workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; _workItemsQueue.EnqueueWorkItem(workItem); workItem.WorkItemIsQueued(); // If all the threads are busy then try to create a new one - if (_currentWorkItemsCount > _workerThreads.Count) - { - StartThreads(1); + if (_currentWorkItemsCount > _workerThreads.Count) + { + StartThreads(1); } } - private void IncrementWorkItemsCount() + internal override void Requeue(WorkItem workItem) + => Enqueue(workItem); + + private void IncrementWorkItemsCount() { _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); @@ -596,7 +611,10 @@ private void DecrementWorkItemsCount() { IsIdle = true; _isIdleWaitHandle.Set(); - } +#if _ASYNC_SUPPORTED + _isIdleTCS?.TrySetResult(true); +#endif + } Interlocked.Increment(ref _workItemsProcessed); @@ -606,10 +624,9 @@ private void DecrementWorkItemsCount() _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); } + } - } - - internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) { _workItemsGroups[workItemsGroup] = workItemsGroup; } @@ -670,8 +687,8 @@ private void StartThreads(int threadsCount) Thread workerThread = _stpStartInfo.MaxStackSize.HasValue - ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) - : new Thread(ProcessQueuedItems); + ? new Thread(ProcessQueuedWorkItems, _stpStartInfo.MaxStackSize.Value) + : new Thread(ProcessQueuedWorkItems); // Configure the new thread and start it workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; @@ -697,7 +714,7 @@ private void StartThreads(int threadsCount) /// /// A worker thread method that processes work items from the work items queue. /// - private void ProcessQueuedItems() + private void ProcessQueuedWorkItems() { // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks // of the dictionary. @@ -795,7 +812,7 @@ private void ProcessQueuedItems() // will return true, so the post execute can run. if (!workItem.StartingWorkItem()) { - continue; + continue; } // Execute the callback. Make sure to accurately @@ -807,10 +824,15 @@ private void ProcessQueuedItems() // Mark that the _inUseWorkerThreads incremented, so in the finally{} // statement we will decrement it correctly. bInUseWorkerThreadsWasIncremented = true; - - workItem.FireWorkItemStarted(); - - ExecuteWorkItem(workItem); +#if _ASYNC_SUPPORTED + workItem.NotifyWorkItemExecutionStatusChanged( + workItem.IsAsync + ? WorkItemExecutionStatus.Executing + : WorkItemExecutionStatus.Started); +#else + workItem.NotifyWorkItemExecutionStatusChanged(WorkItemExecutionStatus.Started); +#endif + ExecuteWorkItem(workItem); } catch(Exception ex) { @@ -819,9 +841,14 @@ private void ProcessQueuedItems() } finally { - workItem.DisposeOfState(); + var isCompleted = workItem.IsCompleted; + + if (isCompleted) + { + workItem.DisposeOfState(); + } - // Set the CurrentWorkItem to null, since we + // Set the CurrentWorkItem to null, since we // no longer run user's code. CurrentThreadEntry.CurrentWorkItem = null; @@ -835,20 +862,31 @@ private void ProcessQueuedItems() _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); } - // Notify that the work item has been completed. - // WorkItemsGroup may enqueue their next work item. - workItem.FireWorkItemCompleted(); + // Notify that the work item has released the execution thread and its + // WorkItemsGroup may enqueue the next work item to the STP. - // Decrement the number of work items here so the idle - // ManualResetEvent won't fluctuate. - DecrementWorkItemsCount(); - } - } +#if _ASYNC_SUPPORTED + workItem.NotifyWorkItemExecutionStatusChanged( + isCompleted + ? WorkItemExecutionStatus.Completed + : WorkItemExecutionStatus.Awaiting); +#else + workItem.NotifyWorkItemExecutionStatusChanged(WorkItemExecutionStatus.Completed); +#endif + + if (isCompleted) + { + // Decrement the number of work items here so the idle + // ManualResetEvent won't fluctuate. + DecrementWorkItemsCount(); + } + } + } } catch(ThreadAbortException tae) { tae.GetHashCode(); - // Handle the abort exception gracfully. + // Handle the abort exception gracefully. Thread.ResetAbort(); } catch(Exception e) @@ -877,10 +915,9 @@ private void ExecuteWorkItem(WorkItem workItem) } } +#endregion - #endregion - - #region Public Methods +#region Public Methods private void ValidateWaitForIdle() { @@ -1273,7 +1310,7 @@ public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigSta return workItemsGroup; } - #region Fire Thread's Events +#region Fire Thread's Events private void FireOnThreadInitialization() { @@ -1315,7 +1352,7 @@ private void FireOnThreadTermination() } } - #endregion +#endregion /// /// This event is fired when a thread is created. @@ -1373,9 +1410,9 @@ private void ValidateQueueIsWithinLimits() } } - #endregion +#endregion - #region Properties +#region Properties /// /// Get/Set the lower limit of threads in the pool. @@ -1515,9 +1552,9 @@ public ISTPPerformanceCountersReader PerformanceCountersReader get { return (ISTPPerformanceCountersReader)_localPCs; } } - #endregion +#endregion - #region IDisposable Members +#region IDisposable Members public void Dispose() { @@ -1552,9 +1589,9 @@ private void ValidateNotDisposed() throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); } } - #endregion +#endregion - #region WorkItemsGroupBase Overrides +#region WorkItemsGroupBase Overrides /// /// Get/Set the maximum number of work items that execute cocurrency on the thread pool @@ -1667,13 +1704,60 @@ public override bool WaitForIdle(int millisecondsTimeout) return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); } - /// - /// This event is fired when all work items are completed. - /// (When IsIdle changes to true) - /// This event only work on WorkItemsGroup. On SmartThreadPool - /// it throws the NotImplementedException. - /// - public override event WorkItemsGroupIdleHandler OnIdle +#if _ASYNC_SUPPORTED + public override Task WaitForIdleAsync(CancellationToken? cancellationToken = null) + { + // If the STP is already idle then return a completed task + if (IsIdle) + { + return Task.CompletedTask; + } + + if (cancellationToken?.IsCancellationRequested ?? false) + { + // Throw task cancel exception + return Task.FromCanceled(cancellationToken.Value); + } + + // Prepare a local tcs + TaskCompletionSource isIdleTCS = null; + + lock (_workerThreads.SyncRoot) + { + // If the _isIdleTCS was not initialized or was already set then create a new one + if (_isIdleTCS == null || _isIdleTCS.Task.IsCompleted) + { + _isIdleTCS = new TaskCompletionSource(); + } + + // Store the local tcs + isIdleTCS = _isIdleTCS; + } + + // If in the meantime the STP become idle then set the tcs + if (IsIdle) + { + isIdleTCS.TrySetResult(true); + } + + if (!cancellationToken.HasValue) + { + return isIdleTCS.Task; + } + + TaskCompletionSource cancelled = new TaskCompletionSource(); + cancellationToken.Value.Register(() => cancelled.TrySetCanceled()); + + return Task.WhenAny(isIdleTCS.Task, cancelled.Task); + } +#endif + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + public override event WorkItemsGroupIdleHandler OnIdle { add { @@ -1687,7 +1771,7 @@ public override event WorkItemsGroupIdleHandler OnIdle } } - internal override void PreQueueWorkItem() + internal override void PreQueueWorkItem() { ValidateNotDisposed(); @@ -1695,9 +1779,9 @@ internal override void PreQueueWorkItem() ValidateQueueIsWithinLimits(); } - #endregion +#endregion - #region Join, Choice, Pipe, etc. +#region Join, Choice, Pipe, etc. /// /// Executes all actions in parallel. @@ -1798,7 +1882,8 @@ public void Pipe(T pipeState, params Action[] actions) { Pipe(pipeState, (IEnumerable>)actions); } - #endregion + +#endregion } - #endregion +#endregion } diff --git a/SmartThreadPool/SmartThreadPool.csproj b/SmartThreadPool/SmartThreadPool.csproj index 436b16f..54e68bb 100644 --- a/SmartThreadPool/SmartThreadPool.csproj +++ b/SmartThreadPool/SmartThreadPool.csproj @@ -1,62 +1,52 @@  - - net40;net45;net46;netstandard2.0;netcoreapp3.0;netcoreapp3.1;net5.0 - SmartThreadPool - SmartThreadPool - TRACE; - true - 2.3.0 - - Ami Bar - Smart Thread Pool, implemented in .NET - SmartThreadPool Thread Pool .NET - MS-PL - Ami Bar - https://github.com/amibar/SmartThreadPool - https://github.com/amibar/SmartThreadPool - Debug;Release;Publish - Added .net core 3.1 and 5.0 support - SmartThreadPool.dll - 2.3.0.0 - 2.3.0.0 - - - - - - - - - - - - - - - - - <_Parameter1>STPTests - - - - - - <_Parameter1>STPTests - - - - - - <_Parameter1>STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad - - - - - true - true - ..\publish\Keys\STP.snk - ..\publish\dist\bin - + + net40;netstandard2.0 + SmartThreadPool + SmartThreadPool + TRACE; + true + 2.3.0 + + Ami Bar + Smart Thread Pool, implemented in .NET + SmartThreadPool Thread Pool .NET + MS-PL + Ami Bar + https://github.com/amibar/SmartThreadPool + https://github.com/amibar/SmartThreadPool + Debug;Release;Publish + Added .net core 3.1 and 5.0 support + SmartThreadPool.dll + 2.3.0.0 + 2.3.0.0 + + + + + + + + $(DefineConstants);_ASYNC_SUPPORTED + + + + + <_Parameter1>STPTests + + + + + + <_Parameter1>STPTests + + + + + true + true + ..\publish\Keys\STP.snk + ..\publish\dist\bin + diff --git a/SmartThreadPool/WorkItem.WorkItemResult.cs b/SmartThreadPool/WorkItem.WorkItemResult.cs index f965c92..b47bbb0 100644 --- a/SmartThreadPool/WorkItem.WorkItemResult.cs +++ b/SmartThreadPool/WorkItem.WorkItemResult.cs @@ -1,190 +1,168 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; - -namespace Amib.Threading.Internal -{ - public partial class WorkItem - { - #region WorkItemResult class - - private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult - { - /// - /// A back reference to the work item - /// - private readonly WorkItem _workItem; - - public WorkItemResult(WorkItem workItem) - { - _workItem = workItem; - } - - internal WorkItem GetWorkItem() - { - return _workItem; - } - - #region IWorkItemResult Members - - public bool IsCompleted - { - get - { - return _workItem.IsCompleted; - } - } - - public bool IsCanceled - { - get - { - return _workItem.IsCanceled; - } - } - - public object GetResult() - { - return _workItem.GetResult(Timeout.Infinite, true, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null); - } - - public object GetResult(TimeSpan timeout, bool exitContext) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); - } - - public object GetResult(out Exception e) - { - return _workItem.GetResult(Timeout.Infinite, true, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); - } - - public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); - } - - public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); - } - - public bool Cancel() - { - return Cancel(false); - } - - public bool Cancel(bool abortExecution) - { - return _workItem.Cancel(abortExecution); - } - - public object State - { - get - { - return _workItem._state; - } - } - - public WorkItemPriority WorkItemPriority - { - get - { - return _workItem._workItemInfo.WorkItemPriority; - } - } - - /// - /// Return the result, same as GetResult() - /// - public object Result - { - get { return GetResult(); } - } - - /// - /// Returns the exception if occured otherwise returns null. - /// This value is valid only after the work item completed, - /// before that it is always null. - /// - public object Exception - { - get { return _workItem._exception; } - } - - #endregion - - #region IInternalWorkItemResult Members - - public event WorkItemStateCallback OnWorkItemStarted - { - add - { - _workItem.OnWorkItemStarted += value; - } - remove - { - _workItem.OnWorkItemStarted -= value; - } - } - - - public event WorkItemStateCallback OnWorkItemCompleted - { - add - { - _workItem.OnWorkItemCompleted += value; - } - remove - { - _workItem.OnWorkItemCompleted -= value; - } - } - - #endregion - - #region IInternalWorkItemResult Members - - public IWorkItemResult GetWorkItemResult() - { - return this; - } - - public IWorkItemResult GetWorkItemResultT() - { - return new WorkItemResultTWrapper(this); - } - - #endregion - } - - #endregion - - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Amib.Threading.Internal +{ + public partial class WorkItem + { + #region WorkItemResult class + + private class WorkItemResult : IWorkItemResult, IInternalWaitableResult + { + /// + /// A back reference to the work item + /// + private readonly WorkItem _workItem; + + public WorkItemResult(WorkItem workItem) + { + _workItem = workItem; + } + + internal WorkItem GetWorkItem() + { + return _workItem; + } + + #region IWorkItemResult Members + + public bool IsCompleted + { + get + { + return _workItem.IsCompleted; + } + } + + public bool IsCanceled + { + get + { + return _workItem.IsCanceled; + } + } + + public object GetResult() + { + return _workItem.GetResult(Timeout.Infinite, true, null); + } + +#if _ASYNC_SUPPORTED + public Task GetResultAsync() + { + return _workItem.GetResultAsync(); + } +#endif + public object GetResult(int millisecondsTimeout, bool exitContext) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null); + } + + public object GetResult(TimeSpan timeout, bool exitContext) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + public object GetResult(out Exception e) + { + return _workItem.GetResult(Timeout.Infinite, true, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); + } + + public bool Cancel() + { + return Cancel(false); + } + + public bool Cancel(bool abortExecution) + { + return _workItem.Cancel(abortExecution); + } + + public object State + { + get + { + return _workItem._state; + } + } + + public WorkItemPriority WorkItemPriority + { + get + { + return _workItem._workItemInfo.WorkItemPriority; + } + } + + /// + /// Return the result, same as GetResult() + /// + public object Result + { + get { return GetResult(); } + } + + /// + /// Returns the exception if occured otherwise returns null. + /// This value is valid only after the work item completed, + /// before that it is always null. + /// + public object Exception + { + get { return _workItem._exception; } + } + +#endregion + +#region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return this; + } + + public IWorkItemResult GetWorkItemResultT() + { + return new WorkItemResultTWrapper(this); + } + +#endregion + } + +#endregion + + } +} diff --git a/SmartThreadPool/WorkItem.cs b/SmartThreadPool/WorkItem.cs index 5cff866..5efe421 100644 --- a/SmartThreadPool/WorkItem.cs +++ b/SmartThreadPool/WorkItem.cs @@ -1,986 +1,1212 @@ -using System; -using System.Threading; -using System.Diagnostics; - -using Amib.Threading; - -namespace Amib.Threading.Internal -{ - /// - /// Holds a callback delegate and the state for that delegate. - /// - public partial class WorkItem : IHasWorkItemPriority - { - #region WorkItemState enum - - /// - /// Indicates the state of the work item in the thread pool - /// - private enum WorkItemState - { - InQueue = 0, // Nexts: InProgress, Canceled - InProgress = 1, // Nexts: Completed, Canceled - Completed = 2, // Stays Completed - Canceled = 3, // Stays Canceled - } - - private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState) - { - bool valid = false; - - switch (currentState) - { - case WorkItemState.InQueue: - valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState); - break; - case WorkItemState.InProgress: - valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState); - break; - case WorkItemState.Completed: - case WorkItemState.Canceled: - // Cannot be changed - break; - default: - // Unknown state - Debug.Assert(false); - break; - } - - return valid; - } - - #endregion - - #region Fields - - /// - /// Callback delegate for the callback. - /// - private readonly WorkItemCallback _callback; - - /// - /// State with which to call the callback delegate. - /// - private object _state; - -#if (NETFRAMEWORK) - /// - /// Stores the caller's context - /// - private readonly CallerThreadContext _callerContext; -#endif - /// - /// Holds the result of the mehtod - /// - private object _result; - - /// - /// Hold the exception if the method threw it - /// - private Exception _exception; - - /// - /// Hold the state of the work item - /// - private WorkItemState _workItemState; - - /// - /// A ManualResetEvent to indicate that the result is ready - /// - private ManualResetEvent _workItemCompleted; - - /// - /// A reference count to the _workItemCompleted. - /// When it reaches to zero _workItemCompleted is Closed - /// - private int _workItemCompletedRefCount; - - /// - /// Represents the result state of the work item - /// - private readonly WorkItemResult _workItemResult; - - /// - /// Work item info - /// - private readonly WorkItemInfo _workItemInfo; - - /// - /// Called when the WorkItem starts - /// - private event WorkItemStateCallback _workItemStartedEvent; - - /// - /// Called when the WorkItem completes - /// - private event WorkItemStateCallback _workItemCompletedEvent; - - /// - /// A reference to an object that indicates whatever the - /// WorkItemsGroup has been canceled - /// - private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; - - /// - /// A reference to an object that indicates whatever the - /// SmartThreadPool has been canceled - /// - private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; - - /// - /// The work item group this work item belong to. - /// - private readonly IWorkItemsGroup _workItemsGroup; - - /// - /// The thread that executes this workitem. - /// This field is available for the period when the work item is executed, before and after it is null. - /// - private Thread _executingThread; - - /// - /// The absulote time when the work item will be timeout - /// - private long _expirationTime; - - #region Performance Counter fields - - - - - /// - /// Stores how long the work item waited on the stp queue - /// - private Stopwatch _waitingOnQueueStopwatch; - - /// - /// Stores how much time it took the work item to execute after it went out of the queue - /// - private Stopwatch _processingStopwatch; - - #endregion - - #endregion - - #region Properties - - public TimeSpan WaitingTime - { - get - { - return _waitingOnQueueStopwatch.Elapsed; - } - } - - public TimeSpan ProcessTime - { - get - { - return _processingStopwatch.Elapsed; - } - } - - internal WorkItemInfo WorkItemInfo - { - get - { - return _workItemInfo; - } - } - - #endregion - - #region Construction - - /// - /// Initialize the callback holding object. - /// - /// The workItemGroup of the workitem - /// The WorkItemInfo of te workitem - /// Callback delegate for the callback. - /// State with which to call the callback delegate. - /// - /// We assume that the WorkItem object is created within the thread - /// that meant to run the callback - public WorkItem( - IWorkItemsGroup workItemsGroup, - WorkItemInfo workItemInfo, - WorkItemCallback callback, - object state) - { - _workItemsGroup = workItemsGroup; - _workItemInfo = workItemInfo; - -#if (NETFRAMEWORK) - if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) - { - _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); - } -#endif - - _callback = callback; - _state = state; - _workItemResult = new WorkItemResult(this); - Initialize(); - } - - internal void Initialize() - { - // The _workItemState is changed directly instead of using the SetWorkItemState - // method since we don't want to go throught IsValidStateTransition. - _workItemState = WorkItemState.InQueue; - - _workItemCompleted = null; - _workItemCompletedRefCount = 0; - _waitingOnQueueStopwatch = new Stopwatch(); - _processingStopwatch = new Stopwatch(); - _expirationTime = - _workItemInfo.Timeout > 0 ? - DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond : - long.MaxValue; - } - - internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) - { - return (workItemsGroup == _workItemsGroup); - } - - - #endregion - - #region Methods - - internal CanceledWorkItemsGroup CanceledWorkItemsGroup - { - get { return _canceledWorkItemsGroup; } - set { _canceledWorkItemsGroup = value; } - } - - internal CanceledWorkItemsGroup CanceledSmartThreadPool - { - get { return _canceledSmartThreadPool; } - set { _canceledSmartThreadPool = value; } - } - - /// - /// Change the state of the work item to in progress if it wasn't canceled. - /// - /// - /// Return true on success or false in case the work item was canceled. - /// If the work item needs to run a post execute then the method will return true. - /// - public bool StartingWorkItem() - { - _waitingOnQueueStopwatch.Stop(); - _processingStopwatch.Start(); - - lock (this) - { - if (IsCanceled) - { - bool result = false; - if ((_workItemInfo.PostExecuteWorkItemCallback != null) && - ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled)) - { - result = true; - } - - return result; - } - - Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); - - // No need for a lock yet, only after the state has changed to InProgress - _executingThread = Thread.CurrentThread; - - SetWorkItemState(WorkItemState.InProgress); - } - - return true; - } - - /// - /// Execute the work item and the post execute - /// - public void Execute() - { - CallToPostExecute currentCallToPostExecute = 0; - - // Execute the work item if we are in the correct state - switch (GetWorkItemState()) - { - case WorkItemState.InProgress: - currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled; - ExecuteWorkItem(); - break; - case WorkItemState.Canceled: - currentCallToPostExecute |= CallToPostExecute.WhenWorkItemCanceled; - break; - default: - Debug.Assert(false); - throw new NotSupportedException(); - } - - // Run the post execute as needed - if ((currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0) - { - PostExecute(); - } - - _processingStopwatch.Stop(); - } - - internal void FireWorkItemCompleted() - { - try - { - if (null != _workItemCompletedEvent) - { - _workItemCompletedEvent(this); - } - } - catch // Suppress exceptions - { } - } - - internal void FireWorkItemStarted() - { - try - { - if (null != _workItemStartedEvent) - { - _workItemStartedEvent(this); - } - } - catch // Suppress exceptions - { } - } - - /// - /// Execute the work item - /// - private void ExecuteWorkItem() - { - -#if (NETFRAMEWORK) - CallerThreadContext ctc = null; - if (null != _callerContext) - { - ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); - CallerThreadContext.Apply(_callerContext); - } -#endif - - Exception exception = null; - object result = null; - - try - { - try - { - result = _callback(_state); - } - catch (Exception e) - { - // Save the exception so we can rethrow it later - exception = e; - } - - // Remove the value of the execution thread, so it will be impossible to cancel the work item, - // since it is already completed. - // Cancelling a work item that already completed may cause the abortion of the next work item!!! - Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); - - if (null == executionThread) - { - // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException - Thread.Sleep(60 * 1000); - - // If after 1 minute this thread was not aborted then let it continue working. - } - } - // We must treat the ThreadAbortException or else it will be stored in the exception variable - catch (ThreadAbortException tae) - { - tae.GetHashCode(); - // Check if the work item was cancelled - // If we got a ThreadAbortException and the STP is not shutting down, it means the - // work items was cancelled. - if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown) - { - Thread.ResetAbort(); - } - } - -#if (NETFRAMEWORK) - if (null != _callerContext) - { - CallerThreadContext.Apply(ctc); - } -#endif - - if (!SmartThreadPool.IsWorkItemCanceled) - { - SetResult(result, exception); - } - } - - /// - /// Runs the post execute callback - /// - private void PostExecute() - { - if (null != _workItemInfo.PostExecuteWorkItemCallback) - { - try - { - _workItemInfo.PostExecuteWorkItemCallback(_workItemResult); - } - catch (Exception e) - { - Debug.Assert(null != e); - } - } - } - - /// - /// Set the result of the work item to return - /// - /// The result of the work item - /// The exception that was throw while the workitem executed, null - /// if there was no exception. - internal void SetResult(object result, Exception exception) - { - _result = result; - _exception = exception; - SignalComplete(false); - } - - /// - /// Returns the work item result - /// - /// The work item result - internal IWorkItemResult GetWorkItemResult() - { - return _workItemResult; - } - - /// - /// Wait for all work items to complete - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// true when every work item in waitableResults has completed; otherwise false. - /// - internal static bool WaitAll( - IWaitableResult[] waitableResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - if (0 == waitableResults.Length) - { - return true; - } - - bool success; - WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; - GetWaitHandles(waitableResults, waitHandles); - - if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) - { - success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); - } - else - { - success = true; - int millisecondsLeft = millisecondsTimeout; - Stopwatch stopwatch = Stopwatch.StartNew(); - - WaitHandle[] whs; - if (null != cancelWaitHandle) - { - whs = new WaitHandle[] { null, cancelWaitHandle }; - } - else - { - whs = new WaitHandle[] { null }; - } - - bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); - // Iterate over the wait handles and wait for each one to complete. - // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle - // won't affect it. - // Each iteration we update the time left for the timeout. - for (int i = 0; i < waitableResults.Length; ++i) - { - // WaitAny don't work with negative numbers - if (!waitInfinitely && (millisecondsLeft < 0)) - { - success = false; - break; - } - - whs[0] = waitHandles[i]; - int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); - if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) - { - success = false; - break; - } - - if (!waitInfinitely) - { - // Update the time left to wait - millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; - } - } - } - // Release the wait handles - ReleaseWaitHandles(waitableResults); - - return success; - } - - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// - /// Array of work item result objects - /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. - /// - /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. - /// - /// A cancel wait handle to interrupt the wait if needed - /// - /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. - /// - internal static int WaitAny( - IWaitableResult[] waitableResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - WaitHandle[] waitHandles; - - if (null != cancelWaitHandle) - { - waitHandles = new WaitHandle[waitableResults.Length + 1]; - GetWaitHandles(waitableResults, waitHandles); - waitHandles[waitableResults.Length] = cancelWaitHandle; - } - else - { - waitHandles = new WaitHandle[waitableResults.Length]; - GetWaitHandles(waitableResults, waitHandles); - } - - int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); - - // Treat cancel as timeout - if (null != cancelWaitHandle) - { - if (result == waitableResults.Length) - { - result = STPEventWaitHandle.WaitTimeout; - } - } - - ReleaseWaitHandles(waitableResults); - - return result; - } - - /// - /// Fill an array of wait handles with the work items wait handles. - /// - /// An array of work item results - /// An array of wait handles to fill - private static void GetWaitHandles( - IWaitableResult[] waitableResults, - WaitHandle[] waitHandles) - { - for (int i = 0; i < waitableResults.Length; ++i) - { - WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult; - Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects"); - - waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); - } - } - - /// - /// Release the work items' wait handles - /// - /// An array of work item results - private static void ReleaseWaitHandles(IWaitableResult[] waitableResults) - { - for (int i = 0; i < waitableResults.Length; ++i) - { - WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult(); - - wir.GetWorkItem().ReleaseWaitHandle(); - } - } - - #endregion - - #region Private Members - - private WorkItemState GetWorkItemState() - { - lock (this) - { - if (WorkItemState.Completed == _workItemState) - { - return _workItemState; - } - - long nowTicks = DateTime.UtcNow.Ticks; - - if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime) - { - _workItemState = WorkItemState.Canceled; - } - - if (WorkItemState.InProgress == _workItemState) - { - return _workItemState; - } - - if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled) - { - return WorkItemState.Canceled; - } - - return _workItemState; - } - } - - - /// - /// Sets the work item's state - /// - /// The state to set the work item to - private void SetWorkItemState(WorkItemState workItemState) - { - lock (this) - { - if (IsValidStatesTransition(_workItemState, workItemState)) - { - _workItemState = workItemState; - } - } - } - - /// - /// Signals that work item has been completed or canceled - /// - /// Indicates that the work item has been canceled - private void SignalComplete(bool canceled) - { - SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed); - lock (this) - { - // If someone is waiting then signal. - if (null != _workItemCompleted) - { - _workItemCompleted.Set(); - } - } - } - - internal void WorkItemIsQueued() - { - _waitingOnQueueStopwatch.Start(); - } - - #endregion - - #region Members exposed by WorkItemResult - - /// - /// Cancel the work item if it didn't start running yet. - /// - /// Returns true on success or false if the work item is in progress or already completed - private bool Cancel(bool abortExecution) - { - bool success = false; - bool signalComplete = false; - - lock (this) - { - switch (GetWorkItemState()) - { - case WorkItemState.Canceled: - //Debug.WriteLine("Work item already canceled"); - if (abortExecution) - { - Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); - if (null != executionThread) - { - executionThread.Abort(); // "Cancel" - // No need to signalComplete, because we already cancelled this work item - // so it already signaled its completion. - //signalComplete = true; - } - } - success = true; - break; - case WorkItemState.Completed: - //Debug.WriteLine("Work item cannot be canceled"); - break; - case WorkItemState.InProgress: - if (abortExecution) - { - Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); - if (null != executionThread) - { - executionThread.Abort(); // "Cancel" - success = true; - signalComplete = true; - } - } - else - { - success = true; - signalComplete = true; - } - break; - case WorkItemState.InQueue: - // Signal to the wait for completion that the work - // item has been completed (canceled). There is no - // reason to wait for it to get out of the queue - signalComplete = true; - //Debug.WriteLine("Work item canceled"); - success = true; - break; - } - - if (signalComplete) - { - SignalComplete(true); - } - } - return success; - } - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel. - /// In case of error the method throws and exception - /// - /// The result of the work item - private object GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { - Exception e; - object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); - if (null != e) - { - throw new WorkItemResultException("The work item caused an excpetion, see the inner exception for details", e); - } - return result; - } - - /// - /// Get the result of the work item. - /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel. - /// In case of error the e argument is filled with the exception - /// - /// The result of the work item - private object GetResult( - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle, - out Exception e) - { - e = null; - - // Check for cancel - if (WorkItemState.Canceled == GetWorkItemState()) - { - throw new WorkItemCancelException("Work item canceled"); - } - - // Check for completion - if (IsCompleted) - { - e = _exception; - return _result; - } - - // If no cancelWaitHandle is provided - if (null == cancelWaitHandle) - { - WaitHandle wh = GetWaitHandle(); - - bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); - - ReleaseWaitHandle(); - - if (timeout) - { - throw new WorkItemTimeoutException("Work item timeout"); - } - } - else - { - WaitHandle wh = GetWaitHandle(); - int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); - ReleaseWaitHandle(); - - switch (result) - { - case 0: - // The work item signaled - // Note that the signal could be also as a result of canceling the - // work item (not the get result) - break; - case 1: - case STPEventWaitHandle.WaitTimeout: - throw new WorkItemTimeoutException("Work item timeout"); - default: - Debug.Assert(false); - break; - - } - } - - // Check for cancel - if (WorkItemState.Canceled == GetWorkItemState()) - { - throw new WorkItemCancelException("Work item canceled"); - } - - Debug.Assert(IsCompleted); - - e = _exception; - - // Return the result - return _result; - } - - /// - /// A wait handle to wait for completion, cancel, or timeout - /// - private WaitHandle GetWaitHandle() - { - lock (this) - { - if (null == _workItemCompleted) - { - _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted); - } - ++_workItemCompletedRefCount; - } - return _workItemCompleted; - } - - private void ReleaseWaitHandle() - { - lock (this) - { - if (null != _workItemCompleted) - { - --_workItemCompletedRefCount; - if (0 == _workItemCompletedRefCount) - { - _workItemCompleted.Close(); - _workItemCompleted = null; - } - } - } - } - - /// - /// Returns true when the work item has completed or canceled - /// - private bool IsCompleted - { - get - { - lock (this) - { - WorkItemState workItemState = GetWorkItemState(); - return ((workItemState == WorkItemState.Completed) || - (workItemState == WorkItemState.Canceled)); - } - } - } - - /// - /// Returns true when the work item has canceled - /// - public bool IsCanceled - { - get - { - lock (this) - { - return (GetWorkItemState() == WorkItemState.Canceled); - } - } - } - - #endregion - - #region IHasWorkItemPriority Members - - /// - /// Returns the priority of the work item - /// - public WorkItemPriority WorkItemPriority - { - get - { - return _workItemInfo.WorkItemPriority; - } - } - - #endregion - - internal event WorkItemStateCallback OnWorkItemStarted - { - add - { - _workItemStartedEvent += value; - } - remove - { - _workItemStartedEvent -= value; - } - } - - internal event WorkItemStateCallback OnWorkItemCompleted - { - add - { - _workItemCompletedEvent += value; - } - remove - { - _workItemCompletedEvent -= value; - } - } - - public void DisposeOfState() - { - if (_workItemInfo.DisposeOfStateObjects) - { - IDisposable disp = _state as IDisposable; - if (null != disp) - { - disp.Dispose(); - _state = null; - } - } - } - } -} +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Amib.Threading.Internal +{ + /// + /// Holds a callback delegate and the state for that delegate. + /// + public partial class WorkItem : IHasWorkItemPriority + { + #region Static + + /// + /// Cached functions for extracting result from a Task<> + /// + private static readonly ConcurrentDictionary> _cachedTaskResultExtractors = new ConcurrentDictionary>(); + + #endregion + + #region WorkItemState enum + + /// + /// Indicates the state of the work item in the thread pool + /// + private enum WorkItemState + { + InQueue = 0, // Nexts: InProgress, Canceled + InProgress = 1, // Nexts: Completed, Canceled, Awaiting + Completed = 2, // Stays Completed + Canceled = 3, // Stays Canceled + + Awaiting = 4, // Nexts: InProgress + } + + private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState) + { + bool valid = false; + + switch (currentState) + { + case WorkItemState.InQueue: + valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState); + break; + case WorkItemState.InProgress: + valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState) || (WorkItemState.Awaiting == nextState); + break; + case WorkItemState.Completed: + case WorkItemState.Canceled: + // Cannot be changed + break; + case WorkItemState.Awaiting: + valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState); + break; + default: + // Unknown state + throw new ArgumentOutOfRangeException(nameof(currentState), currentState, null); + } + + return valid; + } + + #endregion + + #region Fields + + /// + /// Callback delegate for the callback. + /// + /// Changes when an await is called from an async method + private WorkItemCallback _callback; + +#if _ASYNC_SUPPORTED + /// + /// When the original callback is an async method, this callback advances the + /// async method to its next state + /// + private WorkItemCallback _nextCallback; +#endif + /// + /// State with which to call the callback delegate. + /// + private object _state; + +#if (NETFRAMEWORK) + /// + /// Stores the caller's context + /// + private readonly CallerThreadContext _callerContext; +#endif + /// + /// Holds the result of the mehtod + /// + private object _result; + + /// + /// Hold the exception if the method threw it + /// + private Exception _exception; + + /// + /// Hold the previous state of the work item + /// + private WorkItemState _prevWorkItemState; + + /// + /// Hold the state of the work item + /// + private WorkItemState _workItemState; + + /// + /// A ManualResetEvent to indicate that the result is ready + /// + private ManualResetEvent _workItemCompleted; + + /// + /// A reference count to the _workItemCompleted. + /// When it reaches to zero _workItemCompleted is Closed + /// + private int _workItemCompletedRefCount; + + /// + /// Represents the result state of the work item + /// + private readonly WorkItemResult _workItemResult; + + /// + /// Work item info + /// + private readonly WorkItemInfo _workItemInfo; + + /// + /// A reference to an object that indicates whatever the + /// WorkItemsGroup has been canceled + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; + + /// + /// A reference to an object that indicates whatever the + /// SmartThreadPool has been canceled + /// + private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; + + /// + /// The work item group this work item belong to. + /// + private readonly IWorkItemsGroup _workItemsGroup; + + /// + /// The thread that executes this workitem. + /// This field is available for the period when the work item is executed, before and after it is null. + /// + private Thread _executingThread; + + /// + /// The absolute time when the work item will be timeout + /// + private long _expirationTime; + +#if _ASYNC_SUPPORTED + /// + /// Store the SynchronizationContext of this work item so in case of async method it will continue + /// in the same IWorkItemsGroup + /// + private STPSynchronizationContext _synchronizationContext; + + /// + /// Stores the Task object so when the await returns the task can continue + /// + private Task _task; + + /// + /// Stores the state of an async void method + /// + /// + /// async void methods (in contradiction to async Task DoWork() and async Task<T> DoWork()) don't + /// return a Task object so in order to know if they are async we use the OperationStarted() and OperationCompleted() callbacks + /// of the SynchronizationContext. + /// This value is only relevant if _task is null. + /// For some reason OperationStarted() and OperationCompleted() are only called by async void methods and not by async Task methods! + /// + private bool? _taskCompleted; + + /// + /// A TaskCompletionSource to support GetResultAsync() + /// + private TaskCompletionSource _tcsResult; + + /// + /// Indicates that this is an async work item. + /// Initialized once the work item is awaiting. + /// + internal bool IsAsync { get; private set; } + + /// + /// Indicates if the STPSynchronizationContext can queue the work item back. + /// In some cases the await completes very fast and calls to the SynchronizationContext.Post + /// before the SmartThreadPool thread completed handling the work item, which may cause + /// several threads to work on the same work item at once. + /// + private bool _canRequeue = false; + + /// + /// Let the SynchronizationContext set the _taskCompleted. + /// true on OperationStarted() and false on OperationCompleted() + /// + internal void SetTaskState(bool completed) => _taskCompleted = completed; + + /// + /// Requeue the work item so it can continue its awaitable method. + /// This method is called from SynchronizationContext + /// + internal void HandleRequeue(SendOrPostCallback d, object state) + { + // Set the _nextCallback to what we got from the SynchronizationContext. + // The new callback will continue where the await was called + // We don't re-queue it yet, since the current work item may still be executing + // on a SmartPoolThread thread + _nextCallback = _ => + { + d(state); + return _task; + }; + + RequeueIfReady(); + } + + private void RequeueIfReady() + { + // Check if we need and can requeue the work item.. + + // An async work item should be re-queued once its await is satisfied and + // it is no longer running on a SmartPoolThread thread. + // Since STPSynchronizationContext.Post is called from an arbitrary thread, + // we need to make sure we can re-queue the work item. + if (_canRequeue) + { + // Check if there is a next callback to call to + var nextCallback = _nextCallback; + if (nextCallback != null) + { + // To overcome the race condition, use Interlocked.CompareExchange to + // get the next callback exclusively. + var callback = Interlocked.CompareExchange(ref _nextCallback, null, nextCallback); + if (callback != null) + { + // We got the call back so re-queue the work item + _callback = callback; + + // The Requeue queue the work item directly to the STP + ((WorkItemsGroupBase)_workItemsGroup).Requeue(this); + } + } + } + } +#endif + + #region Performance Counter fields + + /// + /// Stores how long the work item waited on the stp queue + /// + private Stopwatch _waitingOnQueueStopwatch; + + /// + /// Stores how much time it took the work item to execute after it went out of the queue + /// + private Stopwatch _processingStopwatch; + +#endregion + +#endregion + +#region Properties + + public TimeSpan WaitingTime + { + get + { + return _waitingOnQueueStopwatch.Elapsed; + } + } + + public TimeSpan ProcessTime + { + get + { + return _processingStopwatch.Elapsed; + } + } + + internal WorkItemInfo WorkItemInfo + { + get + { + return _workItemInfo; + } + } + + internal IWorkItemsGroup WorkItemsGroup + { + get + { + return _workItemsGroup; + } + } + + /// + /// This callback is called each time a work item changes its execution status. + /// It is used by the WorkItemsGroup to control its work items + /// + internal WorkItemExecutionStatusCallback OnWorkItemExecutionStatusChanged { get; set; } + + #endregion + + #region Construction + + /// + /// Initialize the callback holding object. + /// + /// The workItemGroup of the workitem + /// The WorkItemInfo of te workitem + /// Callback delegate for the callback. + /// State with which to call the callback delegate. + /// + /// We assume that the WorkItem object is created within the thread + /// that meant to run the callback + public WorkItem( + IWorkItemsGroup workItemsGroup, + WorkItemInfo workItemInfo, + WorkItemCallback callback, + object state) + { + _workItemsGroup = workItemsGroup; + _workItemInfo = workItemInfo; + +#if (NETFRAMEWORK) + if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) + { + _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); + } +#endif + + _callback = callback; + _state = state; + _workItemResult = new WorkItemResult(this); + Initialize(); + } + + internal void Initialize() + { + // The _workItemState is changed directly instead of using the SetWorkItemState + // method since we don't want to go through IsValidStateTransition. + _workItemState = WorkItemState.InQueue; + _prevWorkItemState = WorkItemState.InQueue; + + _workItemCompleted = null; + _workItemCompletedRefCount = 0; + _waitingOnQueueStopwatch = new Stopwatch(); + _processingStopwatch = new Stopwatch(); + _expirationTime = + _workItemInfo.Timeout > 0 ? + DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond : + long.MaxValue; + } + + internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) + { + return (workItemsGroup == _workItemsGroup); + } + + +#endregion + +#region Methods + + internal CanceledWorkItemsGroup CanceledWorkItemsGroup + { + get { return _canceledWorkItemsGroup; } + set { _canceledWorkItemsGroup = value; } + } + + internal CanceledWorkItemsGroup CanceledSmartThreadPool + { + get { return _canceledSmartThreadPool; } + set { _canceledSmartThreadPool = value; } + } + + /// + /// Change the state of the work item to in progress if it wasn't canceled. + /// + /// + /// Return true on success or false in case the work item was canceled. + /// If the work item needs to run a post execute then the method will return true. + /// + public bool StartingWorkItem() + { + _waitingOnQueueStopwatch.Stop(); + _processingStopwatch.Start(); + + lock (this) + { + if (IsCanceled) + { + bool result = false; + if ((_workItemInfo.PostExecuteWorkItemCallback != null) && + ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled)) + { + result = true; + } + + return result; + } + + var workItemState = GetWorkItemState(); + + Debug.Assert(workItemState == WorkItemState.InQueue || workItemState == WorkItemState.Awaiting); + + // No need for a lock yet, only after the state has changed to InProgress + _executingThread = Thread.CurrentThread; + if (workItemState == WorkItemState.InQueue || workItemState == WorkItemState.Awaiting) + { + SetWorkItemState(WorkItemState.InProgress); + } + } + + return true; + } + + /// + /// Execute the work item and the post execute + /// + public void Execute() + { + CallToPostExecute currentCallToPostExecute = 0; + + // Execute the work item if we are in the correct state + switch (GetWorkItemState()) + { + case WorkItemState.InProgress: + currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled; + ExecuteWorkItem(); + break; + case WorkItemState.Canceled: + currentCallToPostExecute |= CallToPostExecute.WhenWorkItemCanceled; + break; + case WorkItemState.Awaiting: + // The WorkItemState.Awaiting case is not relevant since we changed the state + // to InProgress just before we get here. + break; + default: + Debug.Assert(false); + throw new NotSupportedException(); + } + + // Run the post execute as needed + if (IsCompleted && (currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0) + { + PostExecute(); + } + + _processingStopwatch.Stop(); + } + + internal void NotifyWorkItemExecutionStatusChanged(WorkItemExecutionStatus status) + { + OnWorkItemExecutionStatusChanged?.Invoke(this, status); + +#if _ASYNC_SUPPORTED + _canRequeue = !status.HasFlag(WorkItemExecutionStatus.Executing); + RequeueIfReady(); +#endif + } + + /// + /// Execute the work item + /// + private WorkItemState ExecuteWorkItem() + { + +#if (NETFRAMEWORK) + CallerThreadContext ctc = null; + if (null != _callerContext) + { + ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); + CallerThreadContext.Apply(_callerContext); + } +#endif +#if _ASYNC_SUPPORTED + if (_synchronizationContext == null) + { + _synchronizationContext = new STPSynchronizationContext(this); + } + + SynchronizationContext.SetSynchronizationContext(_synchronizationContext); +#endif + + Exception exception = null; + object result = null; + + try + { + try + { +#if _ASYNC_SUPPORTED + _nextCallback = null; +#endif + result = _callback(_state); + + if (result is Task t && t.IsFaulted) + { + exception = t.Exception; + } + } + catch (Exception e) + { + // Save the exception so we can rethrow it later + exception = e; + } + + // Remove the value of the execution thread, so it will be impossible to cancel the work item, + // since it is already completed. + // Cancelling a work item that already completed may cause the abortion of the next work item!!! + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + + if (null == executionThread) + { + // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException + Thread.Sleep(60 * 1000); + + // If after 1 minute this thread was not aborted then let it continue working. + } + } + // We must treat the ThreadAbortException or else it will be stored in the exception variable + catch (ThreadAbortException tae) + { + tae.GetHashCode(); + // Check if the work item was cancelled + // If we got a ThreadAbortException and the STP is not shutting down, it means the + // work items was cancelled. + if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown) + { + Thread.ResetAbort(); + } + } + +#if (NETFRAMEWORK) + if (null != _callerContext) + { + CallerThreadContext.Apply(ctc); + } +#endif + + if (!SmartThreadPool.IsWorkItemCanceled) + { +#if _ASYNC_SUPPORTED + // If the result is a task, it means we are in async method and should "wait" for it to complete + if (result is Task task) + { + if (!IsAsync) + { + IsAsync = true; + } + + if (_task == null) + { + // Keep the _task so we can use it when the work item need to be re-queued + _task = task; + } + + // If the task is completed then signal it + if (_task.IsCompleted) + { + var taskResult = !_task.IsFaulted ? ExtractTaskResult(_task) : null; + SetResult(taskResult, _task.Exception?.Flatten()); + } + // Otherwise we are in await + else + { + SetWorkItemState(WorkItemState.Awaiting); + } + } + // Check if this is an async void method and it wasn't completed yet + else if (_taskCompleted == false) + { + if (!IsAsync) + { + IsAsync = true; + } + SetWorkItemState(WorkItemState.Awaiting); + } + else +#endif + { + SetResult(result, exception); + } + } + + return _workItemState; + } + +#if _ASYNC_SUPPORTED + private object ExtractTaskResult(Task task) + { + // If task is just a Task, it has no result so just return null. + if (typeof(Task) == task.GetType()) + { + return null; + } + + var extractor = _cachedTaskResultExtractors.GetOrAdd( + task.GetType(), + type => + { + var getMethod = type.GetProperty("Result").GetGetMethod(); + + return t => getMethod.Invoke(t , null); + + }); + + return extractor(task); + } +#endif + + /// + /// Runs the post execute callback + /// + private void PostExecute() + { + if (null != _workItemInfo.PostExecuteWorkItemCallback) + { + try + { + _workItemInfo.PostExecuteWorkItemCallback(_workItemResult); + } + catch (Exception e) + { + Debug.Assert(null != e); + } + } + } + + /// + /// Set the result of the work item to return + /// + /// The result of the work item + /// The exception that was throw while the workitem executed, null + /// if there was no exception. + internal void SetResult(object result, Exception exception) + { + _result = result; + _exception = exception; + SignalComplete(false); + } + + /// + /// Returns the work item result + /// + /// The work item result + internal IWorkItemResult GetWorkItemResult() + { + return _workItemResult; + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in waitableResults has completed; otherwise false. + /// + internal static bool WaitAll( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + if (0 == waitableResults.Length) + { + return true; + } + + bool success; + WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); + + if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) + { + success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + } + else + { + success = true; + int millisecondsLeft = millisecondsTimeout; + Stopwatch stopwatch = Stopwatch.StartNew(); + + WaitHandle[] whs; + if (null != cancelWaitHandle) + { + whs = new WaitHandle[] { null, cancelWaitHandle }; + } + else + { + whs = new WaitHandle[] { null }; + } + + bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); + // Iterate over the wait handles and wait for each one to complete. + // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle + // won't affect it. + // Each iteration we update the time left for the timeout. + for (int i = 0; i < waitableResults.Length; ++i) + { + // WaitAny don't work with negative numbers + if (!waitInfinitely && (millisecondsLeft < 0)) + { + success = false; + break; + } + + whs[0] = waitHandles[i]; + int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); + if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) + { + success = false; + break; + } + + if (!waitInfinitely) + { + // Update the time left to wait + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; + } + } + } + // Release the wait handles + ReleaseWaitHandles(waitableResults); + + return success; + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + internal static int WaitAny( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + WaitHandle[] waitHandles; + + if (null != cancelWaitHandle) + { + waitHandles = new WaitHandle[waitableResults.Length + 1]; + GetWaitHandles(waitableResults, waitHandles); + waitHandles[waitableResults.Length] = cancelWaitHandle; + } + else + { + waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); + } + + int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + + // Treat cancel as timeout + if (null != cancelWaitHandle) + { + if (result == waitableResults.Length) + { + result = STPEventWaitHandle.WaitTimeout; + } + } + + ReleaseWaitHandles(waitableResults); + + return result; + } + + /// + /// Fill an array of wait handles with the work items wait handles. + /// + /// An array of work item results + /// An array of wait handles to fill + private static void GetWaitHandles( + IWaitableResult[] waitableResults, + WaitHandle[] waitHandles) + { + for (int i = 0; i < waitableResults.Length; ++i) + { + WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult; + Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects"); + + waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); + } + } + + /// + /// Release the work items' wait handles + /// + /// An array of work item results + private static void ReleaseWaitHandles(IWaitableResult[] waitableResults) + { + for (int i = 0; i < waitableResults.Length; ++i) + { + WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult(); + + wir.GetWorkItem().ReleaseWaitHandle(); + } + } + +#endregion + +#region Private Members + + private WorkItemState GetWorkItemState() + { + lock (this) + { + if (WorkItemState.Completed == _workItemState) + { + return _workItemState; + } + + long nowTicks = DateTime.UtcNow.Ticks; + + if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime) + { + _prevWorkItemState = _workItemState; + _workItemState = WorkItemState.Canceled; + } + + if (WorkItemState.InProgress == _workItemState) + { + return _workItemState; + } + + if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled) + { + return WorkItemState.Canceled; + } + + return _workItemState; + } + } + + /// + /// Sets the work item's state + /// + /// The state to set the work item to + private void SetWorkItemState(WorkItemState workItemState) + { + lock (this) + { + if (IsValidStatesTransition(_workItemState, workItemState)) + { + _prevWorkItemState = _workItemState; + _workItemState = workItemState; + } + } + } + + /// + /// Signals that work item has been completed or canceled + /// + /// Indicates that the work item has been canceled + private void SignalComplete(bool canceled) + { + SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed); + lock (this) + { + // If someone is waiting then signal. + _workItemCompleted?.Set(); + } + +#if _ASYNC_SUPPORTED + if (_tcsResult != null) + { + if (canceled) + { + _tcsResult.TrySetCanceled(); + } + else if (_exception != null) + { + _tcsResult.TrySetException(_exception); + } + else + { + _tcsResult.TrySetResult(_result); + } + } +#endif + } + + internal void WorkItemIsQueued() + { + _waitingOnQueueStopwatch.Start(); + } + +#endregion + +#region Members exposed by WorkItemResult + + /// + /// Cancel the work item if it didn't start running yet. + /// + /// Returns true on success or false if the work item is in progress or already completed + private bool Cancel(bool abortExecution) + { + bool success = false; + bool signalComplete = false; + + lock (this) + { + switch (GetWorkItemState()) + { + case WorkItemState.Canceled: + //Debug.WriteLine("Work item already canceled"); + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + // No need to signalComplete, because we already cancelled this work item + // so it already signaled its completion. + //signalComplete = true; + } + } + success = true; + break; + case WorkItemState.Completed: + //Debug.WriteLine("Work item cannot be canceled"); + break; + case WorkItemState.InProgress: + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + success = true; + signalComplete = true; + } + } + else + { + success = true; + signalComplete = true; + } + break; + case WorkItemState.InQueue: + // Signal to the wait for completion that the work + // item has been completed (canceled). There is no + // reason to wait for it to get out of the queue + signalComplete = true; + //Debug.WriteLine("Work item canceled"); + success = true; + break; + case WorkItemState.Awaiting: + // Signal to the wait for completion that the work + // item has been completed (canceled). There is no + // reason to wait for it to get out of the queue + signalComplete = true; + //Debug.WriteLine("Work item canceled"); + success = true; + break; + } + + if (signalComplete) + { + SignalComplete(true); + } + } + return success; + } + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel. + /// In case of error the method throws and exception + /// + /// The result of the work item + private object GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + Exception e; + object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + if (null != e) + { + throw new WorkItemResultException("The work item caused an excpetion, see the inner exception for details", e); + } + return result; + } + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel. + /// In case of error the e argument is filled with the exception + /// + /// The result of the work item + private object GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e) + { + e = null; + + // Check for cancel + if (WorkItemState.Canceled == GetWorkItemState()) + { + throw new WorkItemCancelException("Work item canceled"); + } + + // Check for completion + if (IsCompleted) + { + e = _exception; + return _result; + } + + // If no cancelWaitHandle is provided + if (null == cancelWaitHandle) + { + WaitHandle wh = GetWaitHandle(); + + bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); + + ReleaseWaitHandle(); + + if (timeout) + { + throw new WorkItemTimeoutException("Work item timeout"); + } + } + else + { + WaitHandle wh = GetWaitHandle(); + int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); + ReleaseWaitHandle(); + + switch (result) + { + case 0: + // The work item signaled + // Note that the signal could be also as a result of canceling the + // work item (not the get result) + break; + case 1: + case STPEventWaitHandle.WaitTimeout: + throw new WorkItemTimeoutException("Work item timeout"); + default: + Debug.Assert(false); + break; + + } + } + + // Check for cancel + if (WorkItemState.Canceled == GetWorkItemState()) + { + throw new WorkItemCancelException("Work item canceled"); + } + + Debug.Assert(IsCompleted); + + e = _exception; + + // Return the result + return _result; + } + +#if _ASYNC_SUPPORTED + private Task GetResultAsync() + { + if (_tcsResult == null) + { + Interlocked.CompareExchange(ref _tcsResult, new TaskCompletionSource(), null); + } + + if (IsCompleted && !_tcsResult.Task.IsCompleted) + { + if (IsCanceled) + { + _tcsResult.TrySetCanceled(); + } + else if (_exception != null) + { + _tcsResult.TrySetException(_exception); + } + else + { + _tcsResult.TrySetResult(_result); + } + } + + return _tcsResult.Task; + } +#endif + + /// + /// A wait handle to wait for completion, cancel, or timeout + /// + private WaitHandle GetWaitHandle() + { + lock (this) + { + if (null == _workItemCompleted) + { + _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted); + } + ++_workItemCompletedRefCount; + } + return _workItemCompleted; + } + + private void ReleaseWaitHandle() + { + lock (this) + { + if (null != _workItemCompleted) + { + --_workItemCompletedRefCount; + if (0 == _workItemCompletedRefCount) + { + _workItemCompleted.Close(); + _workItemCompleted = null; + } + } + } + } + + /// + /// Returns true when the work item has completed or canceled + /// + public bool IsCompleted + { + get + { + lock (this) + { + WorkItemState workItemState = GetWorkItemState(); + return ((workItemState == WorkItemState.Completed) || + (workItemState == WorkItemState.Canceled)); + } + } + } + + /// + /// Returns true when the work item has canceled + /// + public bool IsCanceled + { + get + { + lock (this) + { + return (GetWorkItemState() == WorkItemState.Canceled); + } + } + } + +#endregion + +#region IHasWorkItemPriority Members + + /// + /// Returns the priority of the work item + /// + public WorkItemPriority WorkItemPriority + { + get + { + return _workItemInfo.WorkItemPriority; + } + } + +#endregion + + public void DisposeOfState() + { + if (_workItemInfo.DisposeOfStateObjects) + { + if (_state is IDisposable disp) + { + disp.Dispose(); + _state = null; + } + } + } + } +} diff --git a/SmartThreadPool/WorkItemResultTWrapper.cs b/SmartThreadPool/WorkItemResultTWrapper.cs index fbd0c8b..29a1f17 100644 --- a/SmartThreadPool/WorkItemResultTWrapper.cs +++ b/SmartThreadPool/WorkItemResultTWrapper.cs @@ -1,128 +1,135 @@ -using System; -using System.Threading; - -namespace Amib.Threading.Internal -{ - #region WorkItemResultTWrapper class - - internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult - { - private readonly IWorkItemResult _workItemResult; - - public WorkItemResultTWrapper(IWorkItemResult workItemResult) - { - _workItemResult = workItemResult; - } - - #region IWorkItemResult Members - - public TResult GetResult() - { - return (TResult)_workItemResult.GetResult(); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); - } - - public TResult GetResult(out Exception e) - { - return (TResult)_workItemResult.GetResult(out e); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); - } - - public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); - } - - public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) - { - return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); - } - - public bool IsCompleted - { - get { return _workItemResult.IsCompleted; } - } - - public bool IsCanceled - { - get { return _workItemResult.IsCanceled; } - } - - public object State - { - get { return _workItemResult.State; } - } - - public bool Cancel() - { - return _workItemResult.Cancel(); - } - - public bool Cancel(bool abortExecution) - { - return _workItemResult.Cancel(abortExecution); - } - - public WorkItemPriority WorkItemPriority - { - get { return _workItemResult.WorkItemPriority; } - } - - public TResult Result - { - get { return (TResult)_workItemResult.Result; } - } - - public object Exception - { - get { return _workItemResult.Exception; } - } - - #region IInternalWorkItemResult Members - - public IWorkItemResult GetWorkItemResult() - { - return _workItemResult.GetWorkItemResult(); - } - - public IWorkItemResult GetWorkItemResultT() - { - return (IWorkItemResult)this; - } - - #endregion - - #endregion - } - - #endregion - -} +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Amib.Threading.Internal +{ + #region WorkItemResultTWrapper class + + internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult + { + private readonly IWorkItemResult _workItemResult; + + public WorkItemResultTWrapper(IWorkItemResult workItemResult) + { + _workItemResult = workItemResult; + } + + #region IWorkItemResult Members + + public TResult GetResult() + { + return (TResult)_workItemResult.GetResult(); + } + +#if _ASYNC_SUPPORTED + public async Task GetResultAsync() + { + return (TResult)await _workItemResult.GetResultAsync(); + } +#endif + + public TResult GetResult(int millisecondsTimeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(out Exception e) + { + return (TResult)_workItemResult.GetResult(out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); + } + + public bool IsCompleted + { + get { return _workItemResult.IsCompleted; } + } + + public bool IsCanceled + { + get { return _workItemResult.IsCanceled; } + } + + public object State + { + get { return _workItemResult.State; } + } + + public bool Cancel() + { + return _workItemResult.Cancel(); + } + + public bool Cancel(bool abortExecution) + { + return _workItemResult.Cancel(abortExecution); + } + + public WorkItemPriority WorkItemPriority + { + get { return _workItemResult.WorkItemPriority; } + } + + public TResult Result + { + get { return (TResult)_workItemResult.Result; } + } + + public object Exception + { + get { return _workItemResult.Exception; } + } + + #endregion + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return _workItemResult.GetWorkItemResult(); + } + + public IWorkItemResult GetWorkItemResultT() + { + return (IWorkItemResult)this; + } + + #endregion + } + + #endregion +} diff --git a/SmartThreadPool/WorkItemsGroup.cs b/SmartThreadPool/WorkItemsGroup.cs index 14740fd..641331f 100644 --- a/SmartThreadPool/WorkItemsGroup.cs +++ b/SmartThreadPool/WorkItemsGroup.cs @@ -1,367 +1,438 @@ -using System; -using System.Threading; -using System.Runtime.CompilerServices; -using System.Diagnostics; - -namespace Amib.Threading.Internal -{ - - #region WorkItemsGroup class - - /// - /// Summary description for WorkItemsGroup. - /// - public class WorkItemsGroup : WorkItemsGroupBase - { - #region Private members - - private readonly object _lock = new object(); - - /// - /// A reference to the SmartThreadPool instance that created this - /// WorkItemsGroup. - /// - private readonly SmartThreadPool _stp; - - /// - /// The OnIdle event - /// - private event WorkItemsGroupIdleHandler _onIdle; - - /// - /// A flag to indicate if the Work Items Group is now suspended. - /// - private bool _isSuspended; - - /// - /// Defines how many work items of this WorkItemsGroup can run at once. - /// - private int _concurrency; - - /// - /// Priority queue to hold work items before they are passed - /// to the SmartThreadPool. - /// - private readonly PriorityQueue _workItemsQueue; - - /// - /// Indicate how many work items are waiting in the SmartThreadPool - /// queue. - /// This value is used to apply the concurrency. - /// - private int _workItemsInStpQueue; - - /// - /// Indicate how many work items are currently running in the SmartThreadPool. - /// This value is used with the Cancel, to calculate if we can send new - /// work items to the STP. - /// - private int _workItemsExecutingInStp = 0; - - /// - /// WorkItemsGroup start information - /// - private readonly WIGStartInfo _workItemsGroupStartInfo; - - /// - /// Signaled when all of the WorkItemsGroup's work item completed. - /// - //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); - private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); - - /// - /// A common object for all the work items that this work items group - /// generate so we can mark them to cancel in O(1) - /// - private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - - #endregion - - #region Construction - - public WorkItemsGroup( - SmartThreadPool stp, - int concurrency, - WIGStartInfo wigStartInfo) - { - if (concurrency <= 0) - { - throw new ArgumentOutOfRangeException( - "concurrency", - concurrency, - "concurrency must be greater than zero"); - } - _stp = stp; - _concurrency = concurrency; - _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly(); - _workItemsQueue = new PriorityQueue(); - Name = "WorkItemsGroup"; - - // The _workItemsInStpQueue gets the number of currently executing work items, - // because once a work item is executing, it cannot be cancelled. - _workItemsInStpQueue = _workItemsExecutingInStp; - - _isSuspended = _workItemsGroupStartInfo.StartSuspended; - } - - #endregion - - #region WorkItemsGroupBase Overrides - - public override int Concurrency - { - get { return _concurrency; } - set - { - Debug.Assert(value > 0); - - int diff = value - _concurrency; - _concurrency = value; - if (diff > 0) - { - EnqueueToSTPNextNWorkItem(diff); - } - } - } - - public override int InUseThreads - { - get - { - return _workItemsExecutingInStp; - } - } - - public override int WaitingCallbacks - { - get { return _workItemsQueue.Count; } - } - - public override object[] GetStates() - { - lock (_lock) - { - object[] states = new object[_workItemsQueue.Count]; - int i = 0; - foreach (WorkItem workItem in _workItemsQueue) - { - states[i] = workItem.GetWorkItemResult().State; - ++i; - } - return states; - } - } - - /// - /// WorkItemsGroup start information - /// - public override WIGStartInfo WIGStartInfo - { - get { return _workItemsGroupStartInfo; } - } - - /// - /// Start the Work Items Group if it was started suspended - /// - public override void Start() - { - // If the Work Items Group already started then quit - if (!_isSuspended) - { - return; - } - _isSuspended = false; - - EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); - } - - public override void Cancel(bool abortExecution) - { - lock (_lock) - { - _canceledWorkItemsGroup.IsCanceled = true; - _workItemsQueue.Clear(); - _workItemsInStpQueue = 0; - _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); - } - - if (abortExecution) - { - _stp.CancelAbortWorkItemsGroup(this); - } - } - - /// - /// Wait for the thread pool to be idle - /// - public override bool WaitForIdle(int millisecondsTimeout) - { - SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this); - return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); - } - - public override event WorkItemsGroupIdleHandler OnIdle - { - add { _onIdle += value; } - remove { _onIdle -= value; } - } - - #endregion - - #region Private methods - - private void RegisterToWorkItemCompletion(IWorkItemResult wir) - { - IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir; - iwir.OnWorkItemStarted += OnWorkItemStartedCallback; - iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback; - } - - public void OnSTPIsStarting() - { - if (_isSuspended) - { - return; - } - - EnqueueToSTPNextNWorkItem(_concurrency); - } - - public void EnqueueToSTPNextNWorkItem(int count) - { - for (int i = 0; i < count; ++i) - { - EnqueueToSTPNextWorkItem(null, false); - } - } - - private object FireOnIdle(object state) - { - FireOnIdleImpl(_onIdle); - return null; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle) - { - if(null == onIdle) - { - return; - } - - Delegate[] delegates = onIdle.GetInvocationList(); - foreach(WorkItemsGroupIdleHandler eh in delegates) - { - try - { - eh(this); - } - catch { } // Suppress exceptions - } - } - - private void OnWorkItemStartedCallback(WorkItem workItem) - { - lock(_lock) - { - ++_workItemsExecutingInStp; - } - } - - private void OnWorkItemCompletedCallback(WorkItem workItem) - { - EnqueueToSTPNextWorkItem(null, true); - } - - internal override void Enqueue(WorkItem workItem) - { - EnqueueToSTPNextWorkItem(workItem); - } - - private void EnqueueToSTPNextWorkItem(WorkItem workItem) - { - EnqueueToSTPNextWorkItem(workItem, false); - } - - private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue) - { - lock(_lock) - { - // Got here from OnWorkItemCompletedCallback() - if (decrementWorkItemsInStpQueue) - { - --_workItemsInStpQueue; - - if(_workItemsInStpQueue < 0) - { - _workItemsInStpQueue = 0; - } - - --_workItemsExecutingInStp; - - if(_workItemsExecutingInStp < 0) - { - _workItemsExecutingInStp = 0; - } - } - - // If the work item is not null then enqueue it - if (null != workItem) - { - workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup; - - RegisterToWorkItemCompletion(workItem.GetWorkItemResult()); - _workItemsQueue.Enqueue(workItem); - //_stp.IncrementWorkItemsCount(); - - if ((1 == _workItemsQueue.Count) && - (0 == _workItemsInStpQueue)) - { - _stp.RegisterWorkItemsGroup(this); - IsIdle = false; - _isIdleWaitHandle.Reset(); - } - } - - // If the work items queue of the group is empty than quit - if (0 == _workItemsQueue.Count) - { - if (0 == _workItemsInStpQueue) - { - _stp.UnregisterWorkItemsGroup(this); - IsIdle = true; - _isIdleWaitHandle.Set(); - if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0) - { - _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle)); - } - } - return; - } - - if (!_isSuspended) - { - if (_workItemsInStpQueue < _concurrency) - { - WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; - try - { - _stp.Enqueue(nextWorkItem); - } - catch (ObjectDisposedException e) - { - e.GetHashCode(); - // The STP has been shutdown - } - - ++_workItemsInStpQueue; - } - } - } - } - - #endregion - } - - #endregion -} +using System; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Amib.Threading.Internal +{ + #region WorkItemsGroup class + + /// + /// Summary description for WorkItemsGroup. + /// + public class WorkItemsGroup : WorkItemsGroupBase + { + #region Private members + + private readonly object _lock = new object(); + + /// + /// A reference to the SmartThreadPool instance that created this + /// WorkItemsGroup. + /// + private readonly SmartThreadPool _stp; + + /// + /// The OnIdle event + /// + private event WorkItemsGroupIdleHandler _onIdle; + + /// + /// A flag to indicate if the Work Items Group is now suspended. + /// + private bool _isSuspended; + + /// + /// Defines how many work items of this WorkItemsGroup can run at once. + /// + private int _concurrency; + + /// + /// Priority queue to hold work items before they are passed + /// to the SmartThreadPool. + /// + private readonly PriorityQueue _workItemsQueue; + + /// + /// Indicate how many work items are waiting in the SmartThreadPool + /// queue. + /// This value is used to apply the concurrency. + /// + private int _workItemsInStpQueue; + + /// + /// Indicate how many work items are currently running in the SmartThreadPool. + /// This value is used with the Cancel, to calculate if we can send new + /// work items to the STP. + /// + private int _workItemsExecutingInStp = 0; + + /// + /// The number of work items this WorkItemsGroup is responsible on. + /// A work item is attached to a WorkItemsGroup since it is enqueued until it is completed (or cancelled) + /// We need this count in order to know if the WorkItemsGroup is idle. A WorkItemsGroup with all its + /// work items awaiting is not idle + /// + private int _attachedWorkItemsCount = 0; + + /// + /// WorkItemsGroup start information + /// + private readonly WIGStartInfo _workItemsGroupStartInfo; + + /// + /// Signaled when all of the WorkItemsGroup's work item completed. + /// + //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + +#if _ASYNC_SUPPORTED + /// + /// A task completion source to indicate that the WorkItemsGroup is idle, used in WaitForIdleAsync + /// + private TaskCompletionSource _isIdleTCS; +#endif + /// + /// A common object for all the work items that this work items group + /// generate so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + #endregion + + #region Construction + + public WorkItemsGroup( + SmartThreadPool stp, + int concurrency, + WIGStartInfo wigStartInfo) + { + if (concurrency <= 0) + { + throw new ArgumentOutOfRangeException( + "concurrency", + concurrency, + "concurrency must be greater than zero"); + } + _stp = stp; + _concurrency = concurrency; + _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly(); + _workItemsQueue = new PriorityQueue(); + Name = "WorkItemsGroup"; + + // The _workItemsInStpQueue gets the number of currently executing work items, + // because once a work item is executing, it cannot be cancelled. + _workItemsInStpQueue = _workItemsExecutingInStp; + + _isSuspended = _workItemsGroupStartInfo.StartSuspended; + } + + #endregion + + #region WorkItemsGroupBase Overrides + + public override int Concurrency + { + get { return _concurrency; } + set + { + Debug.Assert(value > 0); + + int diff = value - _concurrency; + _concurrency = value; + if (diff > 0) + { + EnqueueToSTPNextNWorkItem(diff); + } + } + } + + public override int InUseThreads + { + get + { + return _workItemsExecutingInStp; + } + } + + public override int WaitingCallbacks + { + get { return _workItemsQueue.Count; } + } + + public override object[] GetStates() + { + lock (_lock) + { + object[] states = new object[_workItemsQueue.Count]; + int i = 0; + foreach (WorkItem workItem in _workItemsQueue) + { + states[i] = workItem.GetWorkItemResult().State; + ++i; + } + return states; + } + } + + /// + /// WorkItemsGroup start information + /// + public override WIGStartInfo WIGStartInfo + { + get { return _workItemsGroupStartInfo; } + } + + /// + /// Start the Work Items Group if it was started suspended + /// + public override void Start() + { + // If the Work Items Group already started then quit + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); + } + + public override void Cancel(bool abortExecution) + { + lock (_lock) + { + _attachedWorkItemsCount -= _workItemsQueue.Count; + _canceledWorkItemsGroup.IsCanceled = true; + _workItemsQueue.Clear(); + _workItemsInStpQueue = 0; + _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + } + + if (abortExecution) + { + _stp.CancelAbortWorkItemsGroup(this); + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this); + return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); + } + +#if _ASYNC_SUPPORTED + public override Task WaitForIdleAsync(CancellationToken? cancellationToken = null) + { + SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this); + + // If the STP is already idle then return a completed task + if (IsIdle) + { + return Task.CompletedTask; + } + + if (cancellationToken?.IsCancellationRequested ?? false) + { + // Throw task cancel exception + return Task.FromCanceled(cancellationToken.Value); + } + + // Prepare a local tcs + TaskCompletionSource isIdleTCS = null; + + lock (_lock) + { + // If the _isIdleTCS was not initialized or was already set then create a new one + if (_isIdleTCS == null || _isIdleTCS.Task.IsCompleted) + { + _isIdleTCS = new TaskCompletionSource(); + } + + // Store the local tcs + isIdleTCS = _isIdleTCS; + } + + // If in the meantime the STP become idle then set the tcs + if (IsIdle) + { + isIdleTCS.TrySetResult(true); + } + + if (!cancellationToken.HasValue) + { + return isIdleTCS.Task; + } + + TaskCompletionSource cancelled = new TaskCompletionSource(); + cancellationToken.Value.Register(() => cancelled.TrySetCanceled()); + + return Task.WhenAny(isIdleTCS.Task, cancelled.Task); + } +#endif + public override event WorkItemsGroupIdleHandler OnIdle + { + add { _onIdle += value; } + remove { _onIdle -= value; } + } + + #endregion + + #region Private methods + + private void OnWorkItemExecutionStatusChanged(WorkItem workItem, WorkItemExecutionStatus status) + { + if (status == WorkItemExecutionStatus.Started) + { + lock (_lock) + { + ++_workItemsExecutingInStp; + } + } + else if (status == WorkItemExecutionStatus.Completed) + { + workItem.OnWorkItemExecutionStatusChanged = null; + EnqueueToSTPNextWorkItem(null, true, true); + } + } + + public void OnSTPIsStarting() + { + if (_isSuspended) + { + return; + } + + EnqueueToSTPNextNWorkItem(_concurrency); + } + + public void EnqueueToSTPNextNWorkItem(int count) + { + for (int i = 0; i < count; ++i) + { + EnqueueToSTPNextWorkItem(null, false, false); + } + } + + private object FireOnIdle(object state) + { + FireOnIdleImpl(_onIdle); + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle) + { + if(null == onIdle) + { + return; + } + + Delegate[] delegates = onIdle.GetInvocationList(); + foreach(WorkItemsGroupIdleHandler eh in delegates) + { + try + { + eh(this); + } + catch { } // Suppress exceptions + } + } + + internal override void Enqueue(WorkItem workItem) + { + EnqueueToSTPNextWorkItem(workItem, false, false); + } + + internal override void Requeue(WorkItem workItem) + { + _stp.Enqueue(workItem); + } + + private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue, bool workItemCompleted) + { + lock(_lock) + { + // Got here from OnWorkItemExecutionStatusChanged(status) when not status.HasFlag(WorkItemExecutionStatus.Executing) + if (decrementWorkItemsInStpQueue) + { + --_workItemsInStpQueue; + + if (_workItemsInStpQueue < 0) + { + _workItemsInStpQueue = 0; + } + + --_workItemsExecutingInStp; + + if(_workItemsExecutingInStp < 0) + { + _workItemsExecutingInStp = 0; + } + } + + if (workItemCompleted) + { + --_attachedWorkItemsCount; + } + + // If the work item is not null then enqueue it + if (null != workItem) + { + workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup; +#if _ASYNC_SUPPORTED + // Avoid duplicate event registration after awaiting + if (!workItem.IsAsync) +#endif + { + workItem.OnWorkItemExecutionStatusChanged = OnWorkItemExecutionStatusChanged; + ++_attachedWorkItemsCount; + } + + _workItemsQueue.Enqueue(workItem); + + if ((1 == _workItemsQueue.Count) && + (0 == _workItemsInStpQueue)) + { + _stp.RegisterWorkItemsGroup(this); + IsIdle = false; + _isIdleWaitHandle.Reset(); + } + } + + // If the WorkItemsGroup has no more attached work items then notify idle + if (0 == _attachedWorkItemsCount) + { + _stp.UnregisterWorkItemsGroup(this); + IsIdle = true; + _isIdleWaitHandle.Set(); +#if _ASYNC_SUPPORTED + _isIdleTCS?.TrySetResult(true); +#endif + + if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0) + { + _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle)); + } + } + + if (!_isSuspended && _workItemsQueue.Count > 0) + { + if (_workItemsInStpQueue < _concurrency) + { + WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; + try + { + _stp.Enqueue(nextWorkItem); + } + catch (ObjectDisposedException e) + { + e.GetHashCode(); + // The STP has been shutdown + } + + ++_workItemsInStpQueue; + } + } + } + } + +#endregion + } + +#endregion +} diff --git a/SmartThreadPool/WorkItemsGroupBase.cs b/SmartThreadPool/WorkItemsGroupBase.cs index c6c0fdb..2dc6392 100644 --- a/SmartThreadPool/WorkItemsGroupBase.cs +++ b/SmartThreadPool/WorkItemsGroupBase.cs @@ -1,449 +1,694 @@ -using System; -using System.Threading; - -namespace Amib.Threading.Internal -{ - public abstract class WorkItemsGroupBase : IWorkItemsGroup - { - #region Private Fields - - /// - /// Contains the name of this instance of SmartThreadPool. - /// Can be changed by the user. - /// - private string _name = "WorkItemsGroupBase"; - - public WorkItemsGroupBase() - { - IsIdle = true; - } - - #endregion - - #region IWorkItemsGroup Members - - #region Public Methods - - /// - /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance - /// - public string Name - { - get { return _name; } - set { _name = value; } - } - - #endregion - - #region Abstract Methods - - public abstract int Concurrency { get; set; } - public abstract int WaitingCallbacks { get; } - public abstract int InUseThreads { get; } - - public abstract object[] GetStates(); - public abstract WIGStartInfo WIGStartInfo { get; } - public abstract void Start(); - public abstract void Cancel(bool abortExecution); - public abstract bool WaitForIdle(int millisecondsTimeout); - public abstract event WorkItemsGroupIdleHandler OnIdle; - - internal abstract void Enqueue(WorkItem workItem); - internal virtual void PreQueueWorkItem() { } - - #endregion - - #region Common Base Methods - - /// - /// Cancel all the work items. - /// Same as Cancel(false) - /// - public virtual void Cancel() - { - Cancel(false); - } - - /// - /// Wait for the SmartThreadPool/WorkItemsGroup to be idle - /// - public void WaitForIdle() - { - WaitForIdle(Timeout.Infinite); - } - - /// - /// Wait for the SmartThreadPool/WorkItemsGroup to be idle - /// - public bool WaitForIdle(TimeSpan timeout) - { - return WaitForIdle((int)timeout.TotalMilliseconds); - } - - /// - /// IsIdle is true when there are no work items running or queued. - /// - public bool IsIdle { get; protected set; } - - #endregion - - #region QueueWorkItem - - /// - /// Queue a work item - /// - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// The priority of the work item - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item info - /// A callback to execute - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) - { - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// Work item information - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - /// - /// Queue a work item - /// - /// A callback to execute - /// - /// The context object of the work item. Used for passing arguments to the work item. - /// - /// - /// A delegate to call after the callback completion - /// - /// Indicates on which cases to call to the post execute callback - /// The work item priority - /// Returns a work item result - public IWorkItemResult QueueWorkItem( - WorkItemCallback callback, - object state, - PostExecuteWorkItemCallback postExecuteWorkItemCallback, - CallToPostExecute callToPostExecute, - WorkItemPriority workItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); - Enqueue(workItem); - return workItem.GetWorkItemResult(); - } - - #endregion - - #region QueueWorkItem(Action<...>) - - public IWorkItemResult QueueWorkItem(Action action, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - delegate - { - action.Invoke (); - return null; - }, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem(Action action, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg1, arg2); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg1, arg2, arg3); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - public IWorkItemResult QueueWorkItem ( - Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem (); - WorkItem workItem = WorkItemFactory.CreateWorkItem ( - this, - WIGStartInfo, - state => - { - action.Invoke (arg1, arg2, arg3, arg4); - return null; - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority); - Enqueue (workItem); - return workItem.GetWorkItemResult (); - } - - #endregion - - #region QueueWorkItem(Func<...>) - - public IWorkItemResult QueueWorkItem(Func func, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(); - }, priority); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem(Func func, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, - priority); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg1, arg2); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, - priority); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem( - Func func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg1, arg2, arg3); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, - priority); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - public IWorkItemResult QueueWorkItem( - Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) - { - PreQueueWorkItem(); - WorkItem workItem = WorkItemFactory.CreateWorkItem( - this, - WIGStartInfo, - state => - { - return func.Invoke(arg1, arg2, arg3, arg4); - }, - WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, - priority); - Enqueue(workItem); - return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); - } - - #endregion - - #endregion - } +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Amib.Threading.Internal +{ + public abstract class WorkItemsGroupBase : IWorkItemsGroup + { + #region Private Fields + + /// + /// Contains the name of this instance of SmartThreadPool. + /// Can be changed by the user. + /// + private string _name = "WorkItemsGroupBase"; + + protected WorkItemsGroupBase() + { + IsIdle = true; + } + + #endregion + + #region IWorkItemsGroup Members + + #region Public Methods + + /// + /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + #endregion + + #region Abstract Methods + + public abstract int Concurrency { get; set; } + public abstract int WaitingCallbacks { get; } + public abstract int InUseThreads { get; } + + public abstract object[] GetStates(); + public abstract WIGStartInfo WIGStartInfo { get; } + public abstract void Start(); + public abstract void Cancel(bool abortExecution); + public abstract bool WaitForIdle(int millisecondsTimeout); + public abstract event WorkItemsGroupIdleHandler OnIdle; + + internal abstract void Enqueue(WorkItem workItem); + internal abstract void Requeue(WorkItem workItem); + internal virtual void PreQueueWorkItem() { } + +#if _ASYNC_SUPPORTED + public abstract Task WaitForIdleAsync(CancellationToken? cancellationToken = null); + + public Task WaitForIdleAsync(TimeSpan timeout) => + WaitForIdleAsync(new CancellationTokenSource(timeout).Token); + + public Task WaitForIdleAsync(int millisecondsTimeout) => + WaitForIdleAsync(new CancellationTokenSource(millisecondsTimeout).Token); +#endif + + #endregion + + #region Common Base Methods + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + public virtual void Cancel() + { + Cancel(false); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public void WaitForIdle() + { + WaitForIdle(Timeout.Infinite); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public bool WaitForIdle(TimeSpan timeout) + { + return WaitForIdle((int)timeout.TotalMilliseconds); + } + + /// + /// IsIdle is true when there are no work items running or queued. + /// + public bool IsIdle { get; protected set; } + + #endregion + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + #endregion + + #region QueueWorkItem(Action<...>) + + public IWorkItemResult QueueWorkItem(Action action, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + delegate + { + action.Invoke(); + return null; + }, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem(Action action, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + action.Invoke(arg); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + action.Invoke(arg1, arg2); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + action.Invoke(arg1, arg2, arg3); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + action.Invoke(arg1, arg2, arg3, arg4); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + #endregion + +#if _ASYNC_SUPPORTED + #region RunTask(Func) ==> async Task DoWork(..) + + public Task RunTask( + Action action, + CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + if (cancellationToken?.IsCancellationRequested ?? false) + return Task.FromCanceled(cancellationToken.Value); + + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + _ => + { + action(); + return null; + }, + priority); + Enqueue(workItem); + + var wir = workItem.GetWorkItemResult(); + cancellationToken?.Register(() => wir.Cancel()); + + return wir.GetResultAsync(); + } + + public Task RunTask( + Func func, + CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + if (cancellationToken?.IsCancellationRequested ?? false) + return Task.FromCanceled(cancellationToken.Value); + + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + _ => func(), + priority); + Enqueue(workItem); + + var wir = workItem.GetWorkItemResult().GetWorkItemResultT(); + cancellationToken?.Register(() => wir.Cancel()); + + return wir.GetResultAsync(); + } + + public Task RunTask( + Func func, + CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + if (cancellationToken?.IsCancellationRequested ?? false) + return Task.FromCanceled(cancellationToken.Value); + + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + _ => func(), + priority); + Enqueue(workItem); + + var wir = workItem.GetWorkItemResult(); + cancellationToken?.Register(() => wir.Cancel()); + + return wir.GetResultAsync(); + } + + public Task RunTask( + Func> func, + CancellationToken? cancellationToken = null, + WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + if (cancellationToken?.IsCancellationRequested ?? false) + return Task.FromCanceled(cancellationToken.Value); + + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + _ => func(), + priority); + Enqueue(workItem); + + var wir = workItem.GetWorkItemResult().GetWorkItemResultT(); + cancellationToken?.Register(() => wir.Cancel()); + + return wir.GetResultAsync(); + } + + #endregion +#endif + #region QueueWorkItem(Func) ==> async Task DoWork(..) + + public IWorkItemResult QueueWorkItem(Func func, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(), + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem(Func func, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg), + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg1, arg2), + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg1, arg2, arg3), + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg1, arg2, arg3, arg4), + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, + priority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + #endregion + + #region QueueWorkItem(Func, ...>) ==> async Task DoWork(..) + + public IWorkItemResult QueueWorkItem(Func> func, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(), + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func> func, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg), + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func> func, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg1, arg2), + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func> func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg1, arg2, arg3), + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => func(arg1, arg2, arg3, arg4), + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + #endregion + + #region QueueWorkItem(Func<...>) + + public IWorkItemResult QueueWorkItem(Func func, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(); + }, priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T arg, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func(arg1, arg2); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority = SmartThreadPool.DefaultWorkItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3, arg4); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, + priority); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + #endregion + + #endregion + } } \ No newline at end of file diff --git a/WorkItemsGroupDemo/Properties/Resources.Designer.cs b/WorkItemsGroupDemo/Properties/Resources.Designer.cs index 097fb52..1dd4d53 100644 --- a/WorkItemsGroupDemo/Properties/Resources.Designer.cs +++ b/WorkItemsGroupDemo/Properties/Resources.Designer.cs @@ -1,63 +1,63 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WorkItemsGroupDemo.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WorkItemsGroupDemo.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WorkItemsGroupDemo.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WorkItemsGroupDemo.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/WorkItemsGroupDemo/Properties/Settings.Designer.cs b/WorkItemsGroupDemo/Properties/Settings.Designer.cs index 30e3f3d..f90a83f 100644 --- a/WorkItemsGroupDemo/Properties/Settings.Designer.cs +++ b/WorkItemsGroupDemo/Properties/Settings.Designer.cs @@ -1,26 +1,26 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace WorkItemsGroupDemo.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace WorkItemsGroupDemo.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +}