1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003 |
- using System;
- using System.Threading;
- using System.Diagnostics;
- namespace Amib.Threading.Internal
- {
- /// <summary>
- /// Holds a callback delegate and the state for that delegate.
- /// </summary>
- public partial class WorkItem
- {
- #region WorkItemState enum
- /// <summary>
- /// Indicates the state of the work item in the thread pool
- /// </summary>
- 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
- /// <summary>
- /// Callback delegate for the callback.
- /// </summary>
- private WorkItemCallback _callback;
- private WaitCallback _callbackNoResult;
- /// <summary>
- /// State with which to call the callback delegate.
- /// </summary>
- private object _state;
- /// <summary>
- /// Stores the caller's context
- /// </summary>
- private ExecutionContext _callerContext = null;
- /// <summary>
- /// Holds the result of the mehtod
- /// </summary>
- private object _result;
- /// <summary>
- /// Hold the exception if the method threw it
- /// </summary>
- private Exception _exception;
- /// <summary>
- /// Hold the state of the work item
- /// </summary>
- private WorkItemState _workItemState;
- /// <summary>
- /// A ManualResetEvent to indicate that the result is ready
- /// </summary>
- private ManualResetEvent _workItemCompleted;
- /// <summary>
- /// A reference count to the _workItemCompleted.
- /// When it reaches to zero _workItemCompleted is Closed
- /// </summary>
- private int _workItemCompletedRefCount;
- /// <summary>
- /// Represents the result state of the work item
- /// </summary>
- private readonly WorkItemResult _workItemResult;
- /// <summary>
- /// Work item info
- /// </summary>
- private readonly WorkItemInfo _workItemInfo;
- /// <summary>
- /// Called when the WorkItem starts
- /// </summary>
- private event WorkItemStateCallback _workItemStartedEvent;
- /// <summary>
- /// Called when the WorkItem completes
- /// </summary>
- private event WorkItemStateCallback _workItemCompletedEvent;
- /// <summary>
- /// A reference to an object that indicates whatever the
- /// WorkItemsGroup has been canceled
- /// </summary>
- private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
- /// <summary>
- /// A reference to an object that indicates whatever the
- /// SmartThreadPool has been canceled
- /// </summary>
- private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
- /// <summary>
- /// The work item group this work item belong to.
- /// </summary>
- private readonly IWorkItemsGroup _workItemsGroup;
- /// <summary>
- /// 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.
- /// </summary>
- private Thread _executingThread;
- /// <summary>
- /// The absulote time when the work item will be timeout
- /// </summary>
- private long _expirationTime;
- #region Performance Counter fields
- /// <summary>
- /// Stores how long the work item waited on the stp queue
- /// </summary>
- private Stopwatch _waitingOnQueueStopwatch;
- /// <summary>
- /// Stores how much time it took the work item to execute after it went out of the queue
- /// </summary>
- 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
- /// <summary>
- /// Initialize the callback holding object.
- /// </summary>
- /// <param name="workItemsGroup">The workItemGroup of the workitem</param>
- /// <param name="workItemInfo">The WorkItemInfo of te workitem</param>
- /// <param name="callback">Callback delegate for the callback.</param>
- /// <param name="state">State with which to call the callback delegate.</param>
- ///
- /// 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 (_workItemInfo.UseCallerCallContext && !ExecutionContext.IsFlowSuppressed())
- {
- ExecutionContext ec = ExecutionContext.Capture();
- if (ec != null)
- _callerContext = ec.CreateCopy();
- ec.Dispose();
- ec = null;
- }
- _callback = callback;
- _callbackNoResult = null;
- _state = state;
- _workItemResult = new WorkItemResult(this);
- Initialize();
- }
- public WorkItem(IWorkItemsGroup workItemsGroup, WorkItemInfo workItemInfo, WaitCallback callback, object state)
- {
- _workItemsGroup = workItemsGroup;
- _workItemInfo = workItemInfo;
- if (_workItemInfo.UseCallerCallContext && !ExecutionContext.IsFlowSuppressed())
- {
- ExecutionContext ec = ExecutionContext.Capture();
- if (ec != null)
- _callerContext = ec.CreateCopy();
- ec.Dispose();
- ec = null;
- }
- _callbackNoResult = 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; }
- }
- /// <summary>
- /// Change the state of the work item to in progress if it wasn't canceled.
- /// </summary>
- /// <returns>
- /// 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.
- /// </returns>
- public bool StartingWorkItem()
- {
- _waitingOnQueueStopwatch.Stop();
- _processingStopwatch.Start();
- lock (this)
- {
- if (IsCanceled)
- {
- if ((_workItemInfo.PostExecuteWorkItemCallback != null) &&
- ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled))
- {
- return true;
- }
- return false;
- }
- 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;
- }
- /// <summary>
- /// Execute the work item and the post execute
- /// </summary>
- 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
- {
- _workItemCompletedEvent?.Invoke(this);
- }
- catch // Suppress exceptions
- { }
- }
- internal void FireWorkItemStarted()
- {
- try
- {
- _workItemStartedEvent?.Invoke(this);
- }
- catch // Suppress exceptions
- { }
- }
- /// <summary>
- /// Execute the work item
- /// </summary>
- private void ExecuteWorkItem()
- {
- Exception exception = null;
- object result = null;
- try
- {
- try
- {
- if(_callbackNoResult == null)
- {
- if(_callerContext == null)
- result = _callback(_state);
- else
- {
- ContextCallback _ccb = new ContextCallback( o =>
- {
- result =_callback(o);
- });
- ExecutionContext.Run(_callerContext, _ccb, _state);
- }
- }
- else
- {
- if (_callerContext == null)
- _callbackNoResult(_state);
- else
- {
- ContextCallback _ccb = new ContextCallback(o =>
- {
- _callbackNoResult(o);
- });
- ExecutionContext.Run(_callerContext, _ccb, _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 (!SmartThreadPool.IsWorkItemCanceled)
- {
- SetResult(result, exception);
- }
- }
- /// <summary>
- /// Runs the post execute callback
- /// </summary>
- private void PostExecute()
- {
- if (null != _workItemInfo.PostExecuteWorkItemCallback)
- {
- try
- {
- _workItemInfo.PostExecuteWorkItemCallback(_workItemResult);
- }
- catch (Exception e)
- {
- Debug.Assert(null != e);
- }
- }
- }
- /// <summary>
- /// Set the result of the work item to return
- /// </summary>
- /// <param name="result">The result of the work item</param>
- /// <param name="exception">The exception that was throw while the workitem executed, null
- /// if there was no exception.</param>
- internal void SetResult(object result, Exception exception)
- {
- _result = result;
- _exception = exception;
- SignalComplete(false);
- }
- /// <summary>
- /// Returns the work item result
- /// </summary>
- /// <returns>The work item result</returns>
- internal IWorkItemResult GetWorkItemResult()
- {
- return _workItemResult;
- }
- /// <summary>
- /// Wait for all work items to complete
- /// </summary>
- /// <param name="waitableResults">Array of work item result objects</param>
- /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
- /// <param name="exitContext">
- /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
- /// </param>
- /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
- /// <returns>
- /// true when every work item in waitableResults has completed; otherwise false.
- /// </returns>
- 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;
- }
- /// <summary>
- /// Waits for any of the work items in the specified array to complete, cancel, or timeout
- /// </summary>
- /// <param name="waitableResults">Array of work item result objects</param>
- /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
- /// <param name="exitContext">
- /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
- /// </param>
- /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
- /// <returns>
- /// 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.
- /// </returns>
- 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;
- }
- /// <summary>
- /// Fill an array of wait handles with the work items wait handles.
- /// </summary>
- /// <param name="waitableResults">An array of work item results</param>
- /// <param name="waitHandles">An array of wait handles to fill</param>
- 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();
- }
- }
- /// <summary>
- /// Release the work items' wait handles
- /// </summary>
- /// <param name="waitableResults">An array of work item results</param>
- 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;
- }
- }
- /// <summary>
- /// Sets the work item's state
- /// </summary>
- /// <param name="workItemState">The state to set the work item to</param>
- private void SetWorkItemState(WorkItemState workItemState)
- {
- lock (this)
- {
- if (IsValidStatesTransition(_workItemState, workItemState))
- {
- _workItemState = workItemState;
- }
- }
- }
- /// <summary>
- /// Signals that work item has been completed or canceled
- /// </summary>
- /// <param name="canceled">Indicates that the work item has been canceled</param>
- 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
- /// <summary>
- /// Cancel the work item if it didn't start running yet.
- /// </summary>
- /// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
- 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
- {
- // **************************
- // Stock SmartThreadPool 2.2.3 sets these to true and relies on the thread to check the
- // WorkItem cancellation status. However, OpenSimulator uses a different mechanism to notify
- // scripts of co-operative termination and the abort code also relies on this method
- // returning false in order to implement a small wait.
- //
- // Therefore, as was the case previously with STP, we will not signal successful cancellation
- // here. It's possible that OpenSimulator code could be changed in the future to remove
- // the need for this change.
- // **************************
- success = false;
- signalComplete = false;
- }
- 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;
- }
- /// <summary>
- /// 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
- /// </summary>
- /// <returns>The result of the work item</returns>
- 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;
- }
- /// <summary>
- /// 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
- /// </summary>
- /// <returns>The result of the work item</returns>
- 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;
- }
- /// <summary>
- /// A wait handle to wait for completion, cancel, or timeout
- /// </summary>
- private WaitHandle GetWaitHandle()
- {
- lock (this)
- {
- if (null == _workItemCompleted)
- {
- _workItemCompleted = new ManualResetEvent(IsCompleted);
- }
- ++_workItemCompletedRefCount;
- }
- return _workItemCompleted;
- }
- private void ReleaseWaitHandle()
- {
- lock (this)
- {
- if (null != _workItemCompleted)
- {
- --_workItemCompletedRefCount;
- if (0 == _workItemCompletedRefCount)
- {
- _workItemCompleted.Close();
- _workItemCompleted = null;
- }
- }
- }
- }
- /// <summary>
- /// Returns true when the work item has completed or canceled
- /// </summary>
- private bool IsCompleted
- {
- get
- {
- lock (this)
- {
- WorkItemState workItemState = GetWorkItemState();
- return ((workItemState == WorkItemState.Completed) ||
- (workItemState == WorkItemState.Canceled));
- }
- }
- }
- /// <summary>
- /// Returns true when the work item has canceled
- /// </summary>
- public bool IsCanceled
- {
- get
- {
- lock (this)
- {
- return (GetWorkItemState() == WorkItemState.Canceled);
- }
- }
- }
- #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(_callerContext != null)
- {
- _callerContext.Dispose();
- _callerContext = null;
- }
- if (_workItemInfo.DisposeOfStateObjects)
- {
- IDisposable disp = _state as IDisposable;
- if (null != disp)
- {
- disp.Dispose();
- _state = null;
- }
- }
- _callback = null;
- _callbackNoResult = null;
- }
- }
- }
|