diff --git a/STPTests/TestCancel.cs b/STPTests/TestCancel.cs index 51420f9..e2b32ed 100644 --- a/STPTests/TestCancel.cs +++ b/STPTests/TestCancel.cs @@ -77,6 +77,47 @@ namespace SmartThreadPoolTests } } + /// + /// 1. Create STP + /// 2. Queue work item that takes some time + /// 3. Wait for it to start + /// 4. Cancel the work item (soft) + /// 5. Work item's GetResult should throw WorkItemCancelException + /// + [Test] + [ExpectedException(typeof(WorkItemCancelException))] + public void CancelCancelledWorkItemAbort() + { + ManualResetEvent waitToStart = new ManualResetEvent(false); + + SmartThreadPool stp = new SmartThreadPool(); + IWorkItemResult wir = stp.QueueWorkItem( + state => { waitToStart.Set(); while (true) { Thread.Sleep(1000); } return null; } + ); + + waitToStart.WaitOne(); + + wir.Cancel(false); + + Assert.IsTrue(wir.IsCanceled); + + bool completed = stp.WaitForIdle(1000); + + Assert.IsFalse(completed); + + wir.Cancel(true); + + try + { + wir.GetResult(); + } + finally + { + stp.Shutdown(); + } + } + + /// /// 1. Create STP /// 2. Queue work item that: @@ -191,6 +232,7 @@ namespace SmartThreadPoolTests stp.WaitForIdle(); + // Throws WorkItemCancelException wir.GetResult(); stp.Shutdown(); @@ -428,459 +470,5 @@ namespace SmartThreadPoolTests stp.Shutdown(); } - - ////////////////////////////////////////////////////////////////////////////////////////////////// -/* - private int _counter; - - /// - /// Example of how to queue a work item and then cancel it while it is in the queue. - /// - [Test] - [ExpectedException(typeof(WorkItemCancelException))] - public void WorkItemCanceling() - { - // Create a SmartThreadPool with only one thread. - // It just to show how to use the work item canceling feature - SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 1); - - // Queue a work item that will occupy the thread in the pool - IWorkItemResult wir1 = - smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null); - - // Queue another work item that will wait for the first to complete - IWorkItemResult wir2 = - smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null); - - // Wait a while for the thread pool to start executing the first work item - Thread.Sleep(100); - - try - { - // The first work item cannot be canceled since it is currently executing - if (!wir1.Cancel()) - { - // Cancel the second work item while it still in the queue - if (wir2.Cancel()) - { - // Retreiving result of a canceled work item throws WorkItemCancelException exception - wir2.GetResult(); - } - } - } - finally - { - smartThreadPool.Shutdown(); - } - } - - /// - /// - [Test] - public void WorkItemCancelingAndInUseWorkerThreads() - { - SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 10); - - IWorkItemResult [] wirs = new IWorkItemResult[100]; - for(int i = 0; i < 100; ++i) - { - wirs[i] = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null); - } - - // Wait a while for the thread pool to start executing the first work item - Thread.Sleep(100); - - for(int i = 0; i < 100; ++i) - { - wirs[i].Cancel(); - } - - smartThreadPool.WaitForIdle(2000); - - int inUseThreads = smartThreadPool.InUseThreads; - - smartThreadPool.Shutdown(); - - Assert.AreEqual(0, inUseThreads); - } - - private object DoSomeWork(object state) - { - Thread.Sleep(1000); - return 1; - } - - /// - /// Check within the work item if it was cancelled - /// - [Test] - public void SampleIfWorkItemCancelled() - { - _counter = 0; - STPStartInfo stpStartInfo = new STPStartInfo(); - stpStartInfo.StartSuspended = true; - - // Create a SmartThreadPool with only one thread. - SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo); - - IWorkItemResult[] wirs = new IWorkItemResult[100]; - for (int i = 0; i < 100; ++i) - { - wirs[i] = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancelledWorkItem), null); - } - - for (int i = 0; i < 50; ++i) - { - wirs[i].Cancel(); - } - - smartThreadPool.Start(); - - smartThreadPool.WaitForIdle(); - - smartThreadPool.Shutdown(); - - Assert.AreEqual(50, _counter); - } - - - /// - /// 1. Create STP - /// 2. Queue work item into the STP - /// 4. Cancel the work item - /// 5. Work item doesn't check for cancel - /// 6. Work item quits - /// 7. Make sure the work item result is ok, and not an exception - /// - [Test] - public void TestWorkItemCancelledWorkItemOK() - { - // Create a SmartThreadPool with only one thread. - SmartThreadPool smartThreadPool = new SmartThreadPool(); - - AutoResetEvent start = new AutoResetEvent(false); - - // Queue the work item - IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoWaitForCancel), start); - - // Wait for it to start executing - bool success = start.WaitOne(3000, false); - - // Make sure it was started - Assert.IsTrue(success); - - // Cancel the work item - wir.Cancel(); - - // Let it complete - start.Set(); - - // Wait for the work item to complete - smartThreadPool.WaitForIdle(); - - // Check the work item's result - // The work item started running after it was start its execution. - // Since the work item didn't sample the cancel, it was not aware that it - // was canceled. Therefore the result should be what the work item returned - // and not the cancel exeception - int result = (int)wir.GetResult(0, false); - - smartThreadPool.Shutdown(); - - Assert.AreEqual(1, result); - } - - - /// - /// 1. Create STP - /// 2. Create WIG - /// 3. Queue work item into the WIG - /// 4. Cancel the work items in the WIG - /// 5. Work item doesn't check for cancel - /// 6. Work item quits - /// 7. Make sure the work item result is ok, and not an exception - /// - [Test] - public void TestWIGCancelledWorkItemOK() - { - // Create a SmartThreadPool with only one thread. - SmartThreadPool smartThreadPool = new SmartThreadPool(); - - IWorkItemsGroup wig = smartThreadPool.CreateWorkItemsGroup(1); - - AutoResetEvent start = new AutoResetEvent(false); - - // Queue the work item - IWorkItemResult wir = wig.QueueWorkItem(new WorkItemCallback(this.DoWaitForCancel), start); - - // Wait for it to start executing - bool success = start.WaitOne(3000, false); - - // Make sure it was started - Assert.IsTrue(success); - - // Cancel the work item - wig.Cancel(); - - // Let it complete - start.Set(); - - // Wait for the work item to complete - smartThreadPool.WaitForIdle(); - - // Check the work item's result - // The work item started running after it was start its execution. - // Since the work item didn't sample the cancel, it was not aware that it - // was canceled. Therefore the result should be what the work item returned - // and not the cancel exeception - int result = (int)wir.GetResult(0, false); - - smartThreadPool.Shutdown(); - - Assert.AreEqual(1, result); - } - - - /// - /// 1. Create STP - /// 2. Queue work item into the STP - /// 3. Cancel the work items in the STP - /// 4. Work item doesn't check for cancel - /// 5. Work item quits - /// 6. Make sure the work item result is ok, and not an exception - /// - [Test] - public void TestSTPCancelledWorkItemOK() - { - // Create a SmartThreadPool with only one thread. - SmartThreadPool smartThreadPool = new SmartThreadPool(); - - AutoResetEvent start = new AutoResetEvent(false); - - // Queue the work item - IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoWaitForCancel), start); - - // Wait for it to start executing - bool success = start.WaitOne(3000, false); - - // Make sure it was started - Assert.IsTrue(success); - - // Cancel the work item - smartThreadPool.Cancel(); - - // Let it complete - start.Set(); - - // Wait for the work item to complete - smartThreadPool.WaitForIdle(); - - // Check the work item's result - // The work item started running after it was start its execution. - // Since the work item didn't sample the cancel, it was not aware that it - // was canceled. Therefore the result should be what the work item returned - // and not the cancel exeception - int result = (int)wir.GetResult(0, false); - - smartThreadPool.Shutdown(); - - Assert.AreEqual(1, result); - } - - - /// - /// 1. Create STP - /// 2. Queue work item - /// 3. Cancel the work item - /// 4. Work item checks the cancel and quits - /// 5. Make sure the work item result throws exception - /// - [Test] - [ExpectedException(typeof(WorkItemCancelException))] - public void TestWorkItemCanceledWorkItemCancelException() - { - // Create a SmartThreadPool with only one thread. - SmartThreadPool smartThreadPool = new SmartThreadPool(); - - AutoResetEvent start = new AutoResetEvent(false); - - // Queue the work item - IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancel), start); - - // Wait for it to start executing - bool success = start.WaitOne(3000, false); - - // Make sure it was started - Assert.IsTrue(success); - - // Cancel the work item - wir.Cancel(); - - // Let it complete - start.Set(); - - // Wait for the work item to complete - smartThreadPool.WaitForIdle(); - - try - { - // Check the work item's result - // The work item started running after it was start its execution. - // The work item samples the cancel, therefore it is aware that it - // was canceled. Using the GetResult should throw the cancel exeception - wir.GetResult(0, false); - } - finally - { - smartThreadPool.Shutdown(); - } - } - - /// - /// 1. Create STP - /// 2. Create WIG - /// 3. Queue work item into the WIG - /// 4. Cancel the work items in the WIG - /// 5. Work item checks the cancel and quits - /// 6. Make sure the work item result throws exception - /// - [Test] - [ExpectedException(typeof(WorkItemCancelException))] - public void TestWIGCancelledWorkItemCancelException() - { - // Create a SmartThreadPool with only one thread. - SmartThreadPool smartThreadPool = new SmartThreadPool(); - - IWorkItemsGroup wig = smartThreadPool.CreateWorkItemsGroup(1); - - AutoResetEvent start = new AutoResetEvent(false); - - // Queue the work item - IWorkItemResult wir = wig.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancel), start); - - // Wait for it to start executing - bool success = start.WaitOne(3000, false); - - // Make sure it was started - Assert.IsTrue(success); - - // Cancel the work item - wig.Cancel(); - - // Let it complete - start.Set(); - - // Wait for the work item to complete - smartThreadPool.WaitForIdle(); - - try - { - // Check the work item's result - // The work item started running after it was start its execution. - // The work item samples the cancel, therefore it is aware that it - // was canceled. Using the GetResult should throw the cancel exeception - wir.GetResult(0, false); - } - finally - { - smartThreadPool.Shutdown(); - } - } - - /// - /// 1. Create STP - /// 3. Queue work item into the STP - /// 4. Cancel the work items in the STP - /// 5. Work item checks the cancel and quits - /// 6. Make sure the work item result throws exception - /// - [Test] - [ExpectedException(typeof(WorkItemCancelException))] - public void TestSTPCancelledWorkItemCancelException() - { - // Create a SmartThreadPool with only one thread. - SmartThreadPool smartThreadPool = new SmartThreadPool(); - - AutoResetEvent start = new AutoResetEvent(false); - - // Queue the work item - IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancel), start); - - // Wait for it to start executing - bool success = start.WaitOne(3000, false); - - // Make sure it was started - Assert.IsTrue(success); - - // Cancel the work item - smartThreadPool.Cancel(); - - // Let it complete - start.Set(); - - // Wait for the work item to complete - smartThreadPool.WaitForIdle(); - - try - { - // Check the work item's result - // The work item started running after it was start its execution. - // The work item samples the cancel, therefore it is aware that it - // was canceled. Using the GetResult should throw the cancel exeception - wir.GetResult(0, false); - - } - finally - { - smartThreadPool.Shutdown(); - } - } - - private object DoCheckForCancelledWorkItem(object state) - { - if (!SmartThreadPool.IsWorkItemCanceled) - { - Interlocked.Increment(ref _counter); - } - - return null; - } - - private object DoWaitForCancel(object state) - { - AutoResetEvent start = state as AutoResetEvent; - - // Signal the test that the work item started - start.Set(); - - // Let the test run (or else the next line may reset the signaled event) - Thread.Sleep(10); - - // Wait for the test to cancel the work item - start.WaitOne(); - - return 1; - } - - private object DoCheckForCancel(object state) - { - AutoResetEvent start = state as AutoResetEvent; - - // Signal the test that the work item started - start.Set(); - - // Let the test run (or else the next line may reset the signaled event) - Thread.Sleep(10); - - // Wait for the test to cancel the work item - start.WaitOne(); - - // Sample if the work item was cancelled - bool cancelled = SmartThreadPool.IsWorkItemCanceled; - - return 1; - } - */ - } - + } } diff --git a/SmartThreadPool/WorkItem.cs b/SmartThreadPool/WorkItem.cs index 3b24b4a..bbacc80 100644 --- a/SmartThreadPool/WorkItem.cs +++ b/SmartThreadPool/WorkItem.cs @@ -5,22 +5,22 @@ using System.Diagnostics; namespace Amib.Threading.Internal { /// - /// Holds a callback delegate and the state for that delegate. - /// - public partial class WorkItem : IHasWorkItemPriority - { - #region WorkItemState enum + /// 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 - /// + /// + /// 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 - } + { + 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) { @@ -47,19 +47,19 @@ namespace Amib.Threading.Internal return valid; } - #endregion + #endregion - #region Fields + #region Fields - /// - /// Callback delegate for the callback. - /// - private readonly WorkItemCallback _callback; + /// + /// Callback delegate for the callback. + /// + private readonly WorkItemCallback _callback; - /// - /// State with which to call the callback delegate. - /// - private object _state; + /// + /// State with which to call the callback delegate. + /// + private object _state; #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) /// @@ -68,56 +68,56 @@ namespace Amib.Threading.Internal private readonly CallerThreadContext _callerContext; #endif /// - /// Holds the result of the mehtod - /// - private object _result; + /// 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; + /// + /// Hold the state of the work item + /// + private WorkItemState _workItemState; - /// - /// A ManualResetEvent to indicate that the result is ready - /// + /// + /// 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; + /// + /// 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; + /// + /// Represents the result state of the work item + /// + private readonly WorkItemResult _workItemResult; - /// - /// Work item info - /// - private readonly WorkItemInfo _workItemInfo; + /// + /// Work item info + /// + private readonly WorkItemInfo _workItemInfo; - /// - /// Called when the WorkItem starts - /// - private event WorkItemStateCallback _workItemStartedEvent; + /// + /// Called when the WorkItem starts + /// + private event WorkItemStateCallback _workItemStartedEvent; - /// - /// Called when the WorkItem completes - /// - private event WorkItemStateCallback _workItemCompletedEvent; + /// + /// 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 + /// WorkItemsGroup has been canceled + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; /// /// A reference to an object that indicates whatever the @@ -125,10 +125,10 @@ namespace Amib.Threading.Internal /// private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; - /// - /// The work item group this work item belong to. - /// - private readonly IWorkItemsGroup _workItemsGroup; + /// + /// The work item group this work item belong to. + /// + private readonly IWorkItemsGroup _workItemsGroup; /// /// The thread that executes this workitem. @@ -141,9 +141,9 @@ namespace Amib.Threading.Internal /// private long _expirationTime; - #region Performance Counter fields + #region Performance Counter fields + - /// @@ -156,103 +156,103 @@ namespace Amib.Threading.Internal /// private Stopwatch _processingStopwatch; - #endregion + #endregion - #endregion + #endregion - #region Properties + #region Properties - public TimeSpan WaitingTime - { - get - { + public TimeSpan WaitingTime + { + get + { return _waitingOnQueueStopwatch.Elapsed; - } - } + } + } - public TimeSpan ProcessTime - { - get - { + public TimeSpan ProcessTime + { + get + { return _processingStopwatch.Elapsed; - } - } + } + } internal WorkItemInfo WorkItemInfo { - get + get { return _workItemInfo; } } - #endregion + #endregion - #region Construction + #region Construction - /// - /// Initialize the callback holding object. - /// + /// + /// 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; + /// 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 !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) - if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) - { - _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); - } + if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) + { + _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); + } #endif - _callback = callback; - _state = state; - _workItemResult = new WorkItemResult(this); - Initialize(); - } + _callback = callback; + _state = state; + _workItemResult = new WorkItemResult(this); + Initialize(); + } - internal void 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; + _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); - } + internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) + { + return (workItemsGroup == _workItemsGroup); + } - #endregion + #endregion - #region Methods + #region Methods - internal CanceledWorkItemsGroup CanceledWorkItemsGroup - { - get { return _canceledWorkItemsGroup; } - set { _canceledWorkItemsGroup = value; } - } + internal CanceledWorkItemsGroup CanceledWorkItemsGroup + { + get { return _canceledWorkItemsGroup; } + set { _canceledWorkItemsGroup = value; } + } internal CanceledWorkItemsGroup CanceledSmartThreadPool { @@ -260,86 +260,86 @@ namespace Amib.Threading.Internal 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() - { + /// + /// 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) - { + lock (this) + { + if (IsCanceled) + { bool result = false; - if ((_workItemInfo.PostExecuteWorkItemCallback != null) && + if ((_workItemInfo.PostExecuteWorkItemCallback != null) && ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled)) - { - result = true; - } + { + result = true; + } return result; - } + } - Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); + 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); - } + SetWorkItemState(WorkItemState.InProgress); + } - return true; - } + return true; + } - /// - /// Execute the work item and the post execute - /// - public void Execute() - { + /// + /// 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(); - } + // 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(); - } + if ((currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0) + { + PostExecute(); + } _processingStopwatch.Stop(); - } + } - internal void FireWorkItemCompleted() - { - try - { - if (null != _workItemCompletedEvent) - { - _workItemCompletedEvent(this); - } - } - catch // Suppress exceptions - {} - } + internal void FireWorkItemCompleted() + { + try + { + if (null != _workItemCompletedEvent) + { + _workItemCompletedEvent(this); + } + } + catch // Suppress exceptions + { } + } internal void FireWorkItemStarted() { @@ -392,7 +392,7 @@ namespace Amib.Threading.Internal if (null == executionThread) { // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException - Thread.Sleep(60*1000); + Thread.Sleep(60 * 1000); // If after 1 minute this thread was not aborted then let it continue working. } @@ -425,100 +425,100 @@ namespace Amib.Threading.Internal } } - /// - /// Runs the post execute callback - /// - private void PostExecute() - { - if (null != _workItemInfo.PostExecuteWorkItemCallback) - { + /// + /// Runs the post execute callback + /// + private void PostExecute() + { + if (null != _workItemInfo.PostExecuteWorkItemCallback) + { try { _workItemInfo.PostExecuteWorkItemCallback(_workItemResult); } - catch (Exception e) + 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 + /// + /// 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; + internal void SetResult(object result, Exception exception) + { + _result = result; _exception = exception; - SignalComplete(false); - } + SignalComplete(false); + } - /// - /// Returns the work item result - /// - /// The work item result - internal IWorkItemResult GetWorkItemResult() - { - return _workItemResult; - } + /// + /// Returns the work item result + /// + /// The work item result + internal IWorkItemResult GetWorkItemResult() + { + return _workItemResult; + } - /// - /// Wait for all work items to complete - /// + /// + /// 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 - /// + /// 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( + /// + internal static bool WaitAll( IWaitableResult[] waitableResults, - int millisecondsTimeout, - bool exitContext, - WaitHandle cancelWaitHandle) - { + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { if (0 == waitableResults.Length) - { - return true; - } + { + return true; + } - bool success; + bool success; WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; GetWaitHandles(waitableResults, waitHandles); - if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) - { + if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) + { success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); - } - else - { - success = true; - int millisecondsLeft = millisecondsTimeout; + } + 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 }; - } + 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. + // 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)) { @@ -526,113 +526,113 @@ namespace Amib.Threading.Internal break; } - whs[0] = waitHandles[i]; + whs[0] = waitHandles[i]; int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) - { - success = false; - break; - } + { + success = false; + break; + } - if(!waitInfinitely) - { + if (!waitInfinitely) + { // Update the time left to wait millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; - } - } - } - // Release the wait handles + } + } + } + // Release the wait handles ReleaseWaitHandles(waitableResults); - return success; - } + return success; + } - /// - /// Waits for any of the work items in the specified array to complete, cancel, or timeout - /// + /// + /// 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( + /// 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; + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + WaitHandle[] waitHandles; - if (null != cancelWaitHandle) - { + if (null != cancelWaitHandle) + { waitHandles = new WaitHandle[waitableResults.Length + 1]; GetWaitHandles(waitableResults, waitHandles); waitHandles[waitableResults.Length] = cancelWaitHandle; - } - else - { + } + else + { waitHandles = new WaitHandle[waitableResults.Length]; GetWaitHandles(waitableResults, waitHandles); - } + } int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); - // Treat cancel as timeout - if (null != cancelWaitHandle) - { + // Treat cancel as timeout + if (null != cancelWaitHandle) + { if (result == waitableResults.Length) - { + { result = STPEventWaitHandle.WaitTimeout; - } - } + } + } ReleaseWaitHandles(waitableResults); - return result; - } + return result; + } - /// - /// Fill an array of wait handles with the work items wait handles. - /// + /// + /// 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( + /// An array of wait handles to fill + private static void GetWaitHandles( IWaitableResult[] waitableResults, - WaitHandle [] waitHandles) - { + 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(); - } - } + waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); + } + } - /// - /// Release the work items' wait handles - /// + /// + /// 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(); - } - } + wir.GetWorkItem().ReleaseWaitHandle(); + } + } - #endregion - - #region Private Members + #endregion + + #region Private Members private WorkItemState GetWorkItemState() - { + { lock (this) { if (WorkItemState.Completed == _workItemState) @@ -659,56 +659,56 @@ namespace Amib.Threading.Internal return _workItemState; } - } + } - /// - /// Sets the work item's state - /// - /// The state to set the work item to - private void SetWorkItemState(WorkItemState workItemState) - { - lock(this) - { + /// + /// 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; + _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(); - } - } - } + /// + /// 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() - { + internal void WorkItemIsQueued() + { _waitingOnQueueStopwatch.Start(); - } + } - #endregion - - #region Members exposed by WorkItemResult + #endregion - /// - /// 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 + #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) - { + { #if (_WINDOWS_CE) if(abortExecution) { @@ -719,14 +719,25 @@ namespace Amib.Threading.Internal bool signalComplete = false; lock (this) - { - switch(GetWorkItemState()) - { - case WorkItemState.Canceled: - //Debug.WriteLine("Work item already canceled"); - success = true; + { + 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: + case WorkItemState.Completed: //Debug.WriteLine("Work item cannot be canceled"); break; case WorkItemState.InProgress: @@ -746,169 +757,169 @@ namespace Amib.Threading.Internal 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 + 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; + //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 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; + /// + /// 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 cancel + if (WorkItemState.Canceled == GetWorkItemState()) + { + throw new WorkItemCancelException("Work item canceled"); + } - // Check for completion - if (IsCompleted) - { - e = _exception; - return _result; - } + // Check for completion + if (IsCompleted) + { + e = _exception; + return _result; + } - // If no cancelWaitHandle is provided - if (null == cancelWaitHandle) - { - WaitHandle wh = GetWaitHandle(); + // If no cancelWaitHandle is provided + if (null == cancelWaitHandle) + { + WaitHandle wh = GetWaitHandle(); bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); - ReleaseWaitHandle(); + ReleaseWaitHandle(); - if (timeout) - { - throw new WorkItemTimeoutException("Work item timeout"); - } - } - else - { - WaitHandle wh = GetWaitHandle(); + if (timeout) + { + throw new WorkItemTimeoutException("Work item timeout"); + } + } + else + { + WaitHandle wh = GetWaitHandle(); int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); - ReleaseWaitHandle(); + 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: + 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; + throw new WorkItemTimeoutException("Work item timeout"); + default: + Debug.Assert(false); + break; - } - } + } + } - // Check for cancel - if (WorkItemState.Canceled == GetWorkItemState()) - { - throw new WorkItemCancelException("Work item canceled"); - } + // Check for cancel + if (WorkItemState.Canceled == GetWorkItemState()) + { + throw new WorkItemCancelException("Work item canceled"); + } - Debug.Assert(IsCompleted); + Debug.Assert(IsCompleted); - e = _exception; + e = _exception; - // Return the result - return _result; - } + // Return the result + return _result; + } - /// - /// A wait handle to wait for completion, cancel, or timeout - /// - private WaitHandle GetWaitHandle() - { - lock(this) - { - if (null == _workItemCompleted) - { + /// + /// 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; - } + } + ++_workItemCompletedRefCount; + } + return _workItemCompleted; + } - private void ReleaseWaitHandle() - { - lock(this) - { - if (null != _workItemCompleted) - { - --_workItemCompletedRefCount; - if (0 == _workItemCompletedRefCount) - { - _workItemCompleted.Close(); - _workItemCompleted = null; - } - } - } - } + 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 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 @@ -917,65 +928,65 @@ namespace Amib.Threading.Internal { get { - lock(this) + lock (this) { return (GetWorkItemState() == WorkItemState.Canceled); } } } - #endregion + #endregion - #region IHasWorkItemPriority Members + #region IHasWorkItemPriority Members - /// - /// Returns the priority of the work item - /// - public WorkItemPriority WorkItemPriority - { - get - { - return _workItemInfo.WorkItemPriority; - } - } + /// + /// Returns the priority of the work item + /// + public WorkItemPriority WorkItemPriority + { + get + { + return _workItemInfo.WorkItemPriority; + } + } - #endregion + #endregion - internal event WorkItemStateCallback OnWorkItemStarted - { - add - { - _workItemStartedEvent += value; - } - remove - { - _workItemStartedEvent -= value; - } - } + internal event WorkItemStateCallback OnWorkItemStarted + { + add + { + _workItemStartedEvent += value; + } + remove + { + _workItemStartedEvent -= value; + } + } - internal event WorkItemStateCallback OnWorkItemCompleted - { - add - { - _workItemCompletedEvent += value; - } - remove - { - _workItemCompletedEvent -= 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; - } - } + if (_workItemInfo.DisposeOfStateObjects) + { + IDisposable disp = _state as IDisposable; + if (null != disp) + { + disp.Dispose(); + _state = null; + } + } } } }