2008-05-30 12:27:06 +00:00
using System ;
using System.Threading ;
using System.Diagnostics ;
namespace Amib.Threading.Internal
{
/// <summary>
/// Holds a callback delegate and the state for that delegate.
/// </summary>
2013-05-01 18:01:43 +00:00
public partial class WorkItem : IHasWorkItemPriority
2008-05-30 12:27:06 +00:00
{
#region WorkItemState enum
/// <summary>
/// Indicates the state of the work item in the thread pool
/// </summary>
private enum WorkItemState
{
2013-05-01 18:01:43 +00:00
InQueue = 0 , // Nexts: InProgress, Canceled
InProgress = 1 , // Nexts: Completed, Canceled
Completed = 2 , // Stays Completed
Canceled = 3 , // Stays Canceled
2008-05-30 12:27:06 +00:00
}
2013-05-01 18:01:43 +00:00
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 ;
}
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
return valid ;
}
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
# endregion
#region Fields
2008-05-30 12:27:06 +00:00
/// <summary>
/// Callback delegate for the callback.
/// </summary>
2013-05-01 18:01:43 +00:00
private readonly WorkItemCallback _callback ;
2008-05-30 12:27:06 +00:00
/// <summary>
/// State with which to call the callback delegate.
/// </summary>
private object _state ;
2013-05-01 18:01:43 +00:00
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
2008-05-30 12:27:06 +00:00
/// <summary>
/// Stores the caller's context
/// </summary>
2013-05-01 18:01:43 +00:00
private readonly CallerThreadContext _callerContext ;
# endif
2008-05-30 12:27:06 +00:00
/// <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>
2017-01-05 19:07:37 +00:00
/// A reference count to the _workItemCompleted.
2008-05-30 12:27:06 +00:00
/// When it reaches to zero _workItemCompleted is Closed
/// </summary>
private int _workItemCompletedRefCount ;
/// <summary>
/// Represents the result state of the work item
/// </summary>
2013-05-01 18:01:43 +00:00
private readonly WorkItemResult _workItemResult ;
2008-05-30 12:27:06 +00:00
/// <summary>
/// Work item info
/// </summary>
2013-05-01 18:01:43 +00:00
private readonly WorkItemInfo _workItemInfo ;
2008-05-30 12:27:06 +00:00
/// <summary>
/// Called when the WorkItem starts
/// </summary>
private event WorkItemStateCallback _workItemStartedEvent ;
/// <summary>
/// Called when the WorkItem completes
/// </summary>
private event WorkItemStateCallback _workItemCompletedEvent ;
/// <summary>
2017-01-05 19:07:37 +00:00
/// A reference to an object that indicates whatever the
2008-05-30 12:27:06 +00:00
/// WorkItemsGroup has been canceled
/// </summary>
private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup . NotCanceledWorkItemsGroup ;
2013-05-01 18:01:43 +00:00
/// <summary>
2017-01-05 19:07:37 +00:00
/// A reference to an object that indicates whatever the
2013-05-01 18:01:43 +00:00
/// SmartThreadPool has been canceled
/// </summary>
private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup . NotCanceledWorkItemsGroup ;
2008-05-30 12:27:06 +00:00
/// <summary>
/// The work item group this work item belong to.
/// </summary>
2013-05-01 18:01:43 +00:00
private readonly IWorkItemsGroup _workItemsGroup ;
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
/// <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 ;
2008-05-30 12:27:06 +00:00
/// <summary>
2013-05-01 18:01:43 +00:00
/// The absulote time when the work item will be timeout
2008-05-30 12:27:06 +00:00
/// </summary>
2013-05-01 18:01:43 +00:00
private long _expirationTime ;
#region Performance Counter fields
2008-05-30 12:27:06 +00:00
/// <summary>
2013-05-01 18:01:43 +00:00
/// Stores how long the work item waited on the stp queue
2008-05-30 12:27:06 +00:00
/// </summary>
2013-05-01 18:01:43 +00:00
private Stopwatch _waitingOnQueueStopwatch ;
2008-05-30 12:27:06 +00:00
/// <summary>
2013-05-01 18:01:43 +00:00
/// Stores how much time it took the work item to execute after it went out of the queue
2008-05-30 12:27:06 +00:00
/// </summary>
2013-05-01 18:01:43 +00:00
private Stopwatch _processingStopwatch ;
2008-05-30 12:27:06 +00:00
# endregion
# endregion
#region Properties
public TimeSpan WaitingTime
{
2013-05-01 18:01:43 +00:00
get
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
return _waitingOnQueueStopwatch . Elapsed ;
2008-05-30 12:27:06 +00:00
}
}
public TimeSpan ProcessTime
{
2013-05-01 18:01:43 +00:00
get
{
return _processingStopwatch . Elapsed ;
}
}
internal WorkItemInfo WorkItemInfo
{
get
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
return _workItemInfo ;
2008-05-30 12:27:06 +00:00
}
}
# endregion
#region Construction
/// <summary>
/// Initialize the callback holding object.
/// </summary>
2013-05-01 18:01:43 +00:00
/// <param name="workItemsGroup">The workItemGroup of the workitem</param>
/// <param name="workItemInfo">The WorkItemInfo of te workitem</param>
2008-05-30 12:27:06 +00:00
/// <param name="callback">Callback delegate for the callback.</param>
/// <param name="state">State with which to call the callback delegate.</param>
2017-01-05 19:07:37 +00:00
///
2008-05-30 12:27:06 +00:00
/// We assume that the WorkItem object is created within the thread
/// that meant to run the callback
public WorkItem (
IWorkItemsGroup workItemsGroup ,
WorkItemInfo workItemInfo ,
2013-05-01 18:01:43 +00:00
WorkItemCallback callback ,
2008-05-30 12:27:06 +00:00
object state )
{
_workItemsGroup = workItemsGroup ;
_workItemInfo = workItemInfo ;
2013-05-01 18:01:43 +00:00
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
2008-05-30 12:27:06 +00:00
if ( _workItemInfo . UseCallerCallContext | | _workItemInfo . UseCallerHttpContext )
{
_callerContext = CallerThreadContext . Capture ( _workItemInfo . UseCallerCallContext , _workItemInfo . UseCallerHttpContext ) ;
}
2013-05-01 18:01:43 +00:00
# endif
2008-05-30 12:27:06 +00:00
_callback = callback ;
_state = state ;
_workItemResult = new WorkItemResult ( this ) ;
Initialize ( ) ;
}
internal void Initialize ( )
{
2013-05-01 18:01:43 +00:00
// The _workItemState is changed directly instead of using the SetWorkItemState
// method since we don't want to go throught IsValidStateTransition.
2008-05-30 12:27:06 +00:00
_workItemState = WorkItemState . InQueue ;
2013-05-01 18:01:43 +00:00
2008-05-30 12:27:06 +00:00
_workItemCompleted = null ;
_workItemCompletedRefCount = 0 ;
2013-05-01 18:01:43 +00:00
_waitingOnQueueStopwatch = new Stopwatch ( ) ;
_processingStopwatch = new Stopwatch ( ) ;
_expirationTime =
_workItemInfo . Timeout > 0 ?
DateTime . UtcNow . Ticks + _workItemInfo . Timeout * TimeSpan . TicksPerMillisecond :
long . MaxValue ;
2008-05-30 12:27:06 +00:00
}
internal bool WasQueuedBy ( IWorkItemsGroup workItemsGroup )
{
return ( workItemsGroup = = _workItemsGroup ) ;
}
# endregion
#region Methods
2013-05-01 18:01:43 +00:00
internal CanceledWorkItemsGroup CanceledWorkItemsGroup
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
get { return _canceledWorkItemsGroup ; }
set { _canceledWorkItemsGroup = value ; }
}
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
internal CanceledWorkItemsGroup CanceledSmartThreadPool
{
get { return _canceledSmartThreadPool ; }
set { _canceledSmartThreadPool = value ; }
2008-05-30 12:27:06 +00:00
}
/// <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 ( )
{
2013-05-01 18:01:43 +00:00
_waitingOnQueueStopwatch . Stop ( ) ;
_processingStopwatch . Start ( ) ;
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
if ( IsCanceled )
{
bool result = false ;
if ( ( _workItemInfo . PostExecuteWorkItemCallback ! = null ) & &
( ( _workItemInfo . CallToPostExecute & CallToPostExecute . WhenWorkItemCanceled ) = = CallToPostExecute . WhenWorkItemCanceled ) )
{
result = true ;
}
return result ;
}
Debug . Assert ( WorkItemState . InQueue = = GetWorkItemState ( ) ) ;
2013-05-01 18:01:43 +00:00
// No need for a lock yet, only after the state has changed to InProgress
_executingThread = Thread . CurrentThread ;
2008-05-30 12:27:06 +00:00
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
2013-05-01 18:01:43 +00:00
switch ( GetWorkItemState ( ) )
2008-05-30 12:27:06 +00:00
{
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 ( ) ;
}
2013-05-01 18:01:43 +00:00
_processingStopwatch . Stop ( ) ;
2008-05-30 12:27:06 +00:00
}
internal void FireWorkItemCompleted ( )
{
try
{
if ( null ! = _workItemCompletedEvent )
{
_workItemCompletedEvent ( this ) ;
}
}
2013-05-01 18:01:43 +00:00
catch // Suppress exceptions
{ }
}
internal void FireWorkItemStarted ( )
{
try
{
if ( null ! = _workItemStartedEvent )
{
_workItemStartedEvent ( this ) ;
}
}
catch // Suppress exceptions
{ }
2008-05-30 12:27:06 +00:00
}
/// <summary>
/// Execute the work item
/// </summary>
private void ExecuteWorkItem ( )
{
2013-05-01 18:01:43 +00:00
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
2008-05-30 12:27:06 +00:00
CallerThreadContext ctc = null ;
if ( null ! = _callerContext )
{
ctc = CallerThreadContext . Capture ( _callerContext . CapturedCallContext , _callerContext . CapturedHttpContext ) ;
CallerThreadContext . Apply ( _callerContext ) ;
}
2013-05-01 18:01:43 +00:00
# endif
2008-05-30 12:27:06 +00:00
Exception exception = null ;
object result = null ;
try
{
2013-05-01 18:01:43 +00:00
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.
}
2008-05-30 12:27:06 +00:00
}
2013-05-01 18:01:43 +00:00
// We must treat the ThreadAbortException or else it will be stored in the exception variable
catch ( ThreadAbortException tae )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
tae . GetHashCode ( ) ;
// Check if the work item was cancelled
2017-01-05 19:07:37 +00:00
// If we got a ThreadAbortException and the STP is not shutting down, it means the
2013-05-01 18:01:43 +00:00
// work items was cancelled.
if ( ! SmartThreadPool . CurrentThreadEntry . AssociatedSmartThreadPool . IsShuttingdown )
{
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
Thread . ResetAbort ( ) ;
# endif
}
2008-05-30 12:27:06 +00:00
}
2013-05-01 18:01:43 +00:00
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
2008-05-30 12:27:06 +00:00
if ( null ! = _callerContext )
{
CallerThreadContext . Apply ( ctc ) ;
}
2013-05-01 18:01:43 +00:00
# endif
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
if ( ! SmartThreadPool . IsWorkItemCanceled )
{
SetResult ( result , exception ) ;
}
2008-05-30 12:27:06 +00:00
}
/// <summary>
/// Runs the post execute callback
/// </summary>
private void PostExecute ( )
{
if ( null ! = _workItemInfo . PostExecuteWorkItemCallback )
{
try
{
2013-05-01 18:01:43 +00:00
_workItemInfo . PostExecuteWorkItemCallback ( _workItemResult ) ;
2008-05-30 12:27:06 +00:00
}
2013-05-01 18:01:43 +00:00
catch ( Exception e )
2008-05-30 12:27:06 +00:00
{
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>
2013-05-01 18:01:43 +00:00
/// <param name="exception">The exception that was throw while the workitem executed, null
/// if there was no exception.</param>
2008-05-30 12:27:06 +00:00
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>
2013-05-01 18:01:43 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2008-05-30 12:27:06 +00:00
/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
/// <param name="exitContext">
2017-01-05 19:07:37 +00:00
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
2008-05-30 12:27:06 +00:00
/// </param>
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
/// <returns>
2013-05-01 18:01:43 +00:00
/// true when every work item in waitableResults has completed; otherwise false.
2008-05-30 12:27:06 +00:00
/// </returns>
internal static bool WaitAll (
2013-05-01 18:01:43 +00:00
IWaitableResult [ ] waitableResults ,
2008-05-30 12:27:06 +00:00
int millisecondsTimeout ,
bool exitContext ,
WaitHandle cancelWaitHandle )
{
2013-05-01 18:01:43 +00:00
if ( 0 = = waitableResults . Length )
2008-05-30 12:27:06 +00:00
{
return true ;
}
bool success ;
2013-05-01 18:01:43 +00:00
WaitHandle [ ] waitHandles = new WaitHandle [ waitableResults . Length ] ;
GetWaitHandles ( waitableResults , waitHandles ) ;
2008-05-30 12:27:06 +00:00
if ( ( null = = cancelWaitHandle ) & & ( waitHandles . Length < = 64 ) )
{
2013-05-01 18:01:43 +00:00
success = STPEventWaitHandle . WaitAll ( waitHandles , millisecondsTimeout , exitContext ) ;
2008-05-30 12:27:06 +00:00
}
else
{
success = true ;
int millisecondsLeft = millisecondsTimeout ;
2013-05-01 18:01:43 +00:00
Stopwatch stopwatch = Stopwatch . StartNew ( ) ;
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
WaitHandle [ ] whs ;
2008-05-30 12:27:06 +00:00
if ( null ! = cancelWaitHandle )
{
2013-05-01 18:01:43 +00:00
whs = new WaitHandle [ ] { null , cancelWaitHandle } ;
2008-05-30 12:27:06 +00:00
}
else
{
2013-05-01 18:01:43 +00:00
whs = new WaitHandle [ ] { null } ;
2008-05-30 12:27:06 +00:00
}
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.
2013-05-01 18:01:43 +00:00
for ( int i = 0 ; i < waitableResults . Length ; + + i )
2008-05-30 12:27:06 +00:00
{
// WaitAny don't work with negative numbers
if ( ! waitInfinitely & & ( millisecondsLeft < 0 ) )
{
success = false ;
break ;
}
whs [ 0 ] = waitHandles [ i ] ;
2013-05-01 18:01:43 +00:00
int result = STPEventWaitHandle . WaitAny ( whs , millisecondsLeft , exitContext ) ;
if ( ( result > 0 ) | | ( STPEventWaitHandle . WaitTimeout = = result ) )
2008-05-30 12:27:06 +00:00
{
success = false ;
break ;
}
2013-05-01 18:01:43 +00:00
if ( ! waitInfinitely )
2008-05-30 12:27:06 +00:00
{
// Update the time left to wait
2013-05-01 18:01:43 +00:00
millisecondsLeft = millisecondsTimeout - ( int ) stopwatch . ElapsedMilliseconds ;
2008-05-30 12:27:06 +00:00
}
}
}
// Release the wait handles
2013-05-01 18:01:43 +00:00
ReleaseWaitHandles ( waitableResults ) ;
2008-05-30 12:27:06 +00:00
return success ;
}
/// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary>
2013-05-01 18:01:43 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2008-05-30 12:27:06 +00:00
/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
/// <param name="exitContext">
2017-01-05 19:07:37 +00:00
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
2008-05-30 12:27:06 +00:00
/// </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>
2013-05-01 18:01:43 +00:00
internal static int WaitAny (
IWaitableResult [ ] waitableResults ,
2008-05-30 12:27:06 +00:00
int millisecondsTimeout ,
bool exitContext ,
WaitHandle cancelWaitHandle )
{
2013-05-01 18:01:43 +00:00
WaitHandle [ ] waitHandles ;
2008-05-30 12:27:06 +00:00
if ( null ! = cancelWaitHandle )
{
2013-05-01 18:01:43 +00:00
waitHandles = new WaitHandle [ waitableResults . Length + 1 ] ;
GetWaitHandles ( waitableResults , waitHandles ) ;
waitHandles [ waitableResults . Length ] = cancelWaitHandle ;
2008-05-30 12:27:06 +00:00
}
else
{
2013-05-01 18:01:43 +00:00
waitHandles = new WaitHandle [ waitableResults . Length ] ;
GetWaitHandles ( waitableResults , waitHandles ) ;
2008-05-30 12:27:06 +00:00
}
2013-05-01 18:01:43 +00:00
int result = STPEventWaitHandle . WaitAny ( waitHandles , millisecondsTimeout , exitContext ) ;
2008-05-30 12:27:06 +00:00
// Treat cancel as timeout
if ( null ! = cancelWaitHandle )
{
2013-05-01 18:01:43 +00:00
if ( result = = waitableResults . Length )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
result = STPEventWaitHandle . WaitTimeout ;
2008-05-30 12:27:06 +00:00
}
}
2013-05-01 18:01:43 +00:00
ReleaseWaitHandles ( waitableResults ) ;
2008-05-30 12:27:06 +00:00
return result ;
}
/// <summary>
/// Fill an array of wait handles with the work items wait handles.
/// </summary>
2013-05-01 18:01:43 +00:00
/// <param name="waitableResults">An array of work item results</param>
2008-05-30 12:27:06 +00:00
/// <param name="waitHandles">An array of wait handles to fill</param>
private static void GetWaitHandles (
2013-05-01 18:01:43 +00:00
IWaitableResult [ ] waitableResults ,
WaitHandle [ ] waitHandles )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
for ( int i = 0 ; i < waitableResults . Length ; + + i )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
WorkItemResult wir = waitableResults [ i ] . GetWorkItemResult ( ) as WorkItemResult ;
Debug . Assert ( null ! = wir , "All waitableResults must be WorkItemResult objects" ) ;
2008-05-30 12:27:06 +00:00
waitHandles [ i ] = wir . GetWorkItem ( ) . GetWaitHandle ( ) ;
}
}
/// <summary>
/// Release the work items' wait handles
/// </summary>
2013-05-01 18:01:43 +00:00
/// <param name="waitableResults">An array of work item results</param>
private static void ReleaseWaitHandles ( IWaitableResult [ ] waitableResults )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
for ( int i = 0 ; i < waitableResults . Length ; + + i )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
WorkItemResult wir = ( WorkItemResult ) waitableResults [ i ] . GetWorkItemResult ( ) ;
2008-05-30 12:27:06 +00:00
wir . GetWorkItem ( ) . ReleaseWaitHandle ( ) ;
}
}
# endregion
2013-05-01 18:01:43 +00:00
2008-05-30 12:27:06 +00:00
#region Private Members
private WorkItemState GetWorkItemState ( )
{
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
if ( WorkItemState . Completed = = _workItemState )
{
return _workItemState ;
}
long nowTicks = DateTime . UtcNow . Ticks ;
2008-05-30 12:27:06 +00:00
2013-05-01 18:01:43 +00:00
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 ;
}
2008-05-30 12:27:06 +00:00
}
2013-05-01 18:01:43 +00:00
2008-05-30 12:27:06 +00:00
/// <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 )
{
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
if ( IsValidStatesTransition ( _workItemState , workItemState ) )
{
_workItemState = workItemState ;
}
2008-05-30 12:27:06 +00:00
}
}
/// <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 ) ;
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
// If someone is waiting then signal.
if ( null ! = _workItemCompleted )
{
_workItemCompleted . Set ( ) ;
}
}
}
internal void WorkItemIsQueued ( )
{
2013-05-01 18:01:43 +00:00
_waitingOnQueueStopwatch . Start ( ) ;
2008-05-30 12:27:06 +00:00
}
# endregion
2013-05-01 18:01:43 +00:00
2008-05-30 12:27:06 +00:00
#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>
2013-05-01 18:01:43 +00:00
private bool Cancel ( bool abortExecution )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
#if (_WINDOWS_CE)
if ( abortExecution )
{
throw new ArgumentOutOfRangeException ( "abortExecution" , "WindowsCE doesn't support this feature" ) ;
}
# endif
bool success = false ;
bool signalComplete = false ;
lock ( this )
2008-05-30 12:27:06 +00:00
{
2013-05-01 18:01:43 +00:00
switch ( GetWorkItemState ( ) )
2008-05-30 12:27:06 +00:00
{
case WorkItemState . Canceled :
//Debug.WriteLine("Work item already canceled");
2013-05-01 18:01:43 +00:00
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;
}
2017-01-05 19:07:37 +00:00
}
2013-05-01 18:01:43 +00:00
success = true ;
break ;
2008-05-30 12:27:06 +00:00
case WorkItemState . Completed :
//Debug.WriteLine("Work item cannot be canceled");
2013-05-01 18:01:43 +00:00
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
{
2013-05-01 18:29:46 +00:00
// **************************
// 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.
// **************************
2013-05-01 18:01:43 +00:00
success = false ;
signalComplete = false ;
}
break ;
2008-05-30 12:27:06 +00:00
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
2013-05-01 18:01:43 +00:00
signalComplete = true ;
2008-05-30 12:27:06 +00:00
//Debug.WriteLine("Work item canceled");
2013-05-01 18:01:43 +00:00
success = true ;
break ;
}
if ( signalComplete )
{
SignalComplete ( true ) ;
2008-05-30 12:27:06 +00:00
}
}
2013-05-01 18:01:43 +00:00
return success ;
2008-05-30 12:27:06 +00:00
}
/// <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 )
{
2013-05-01 18:01:43 +00:00
Exception e ;
2008-05-30 12:27:06 +00:00
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 ( ) ;
2013-05-01 18:01:43 +00:00
bool timeout = ! STPEventWaitHandle . WaitOne ( wh , millisecondsTimeout , exitContext ) ;
2008-05-30 12:27:06 +00:00
ReleaseWaitHandle ( ) ;
if ( timeout )
{
throw new WorkItemTimeoutException ( "Work item timeout" ) ;
}
}
else
{
WaitHandle wh = GetWaitHandle ( ) ;
2013-05-01 18:01:43 +00:00
int result = STPEventWaitHandle . WaitAny ( new WaitHandle [ ] { wh , cancelWaitHandle } ) ;
2008-05-30 12:27:06 +00:00
ReleaseWaitHandle ( ) ;
2013-05-01 18:01:43 +00:00
switch ( result )
2008-05-30 12:27:06 +00:00
{
case 0 :
// The work item signaled
2017-01-05 19:07:37 +00:00
// Note that the signal could be also as a result of canceling the
2008-05-30 12:27:06 +00:00
// work item (not the get result)
break ;
case 1 :
2013-05-01 18:01:43 +00:00
case STPEventWaitHandle . WaitTimeout :
2008-05-30 12:27:06 +00:00
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>
2017-01-05 19:07:37 +00:00
/// A wait handle to wait for completion, cancel, or timeout
2008-05-30 12:27:06 +00:00
/// </summary>
private WaitHandle GetWaitHandle ( )
{
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
if ( null = = _workItemCompleted )
{
2013-05-01 18:01:43 +00:00
_workItemCompleted = EventWaitHandleFactory . CreateManualResetEvent ( IsCompleted ) ;
2008-05-30 12:27:06 +00:00
}
+ + _workItemCompletedRefCount ;
}
return _workItemCompleted ;
}
private void ReleaseWaitHandle ( )
{
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
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
{
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
WorkItemState workItemState = GetWorkItemState ( ) ;
2013-05-01 18:01:43 +00:00
return ( ( workItemState = = WorkItemState . Completed ) | |
2008-05-30 12:27:06 +00:00
( workItemState = = WorkItemState . Canceled ) ) ;
}
}
}
/// <summary>
/// Returns true when the work item has canceled
/// </summary>
public bool IsCanceled
{
get
{
2013-05-01 18:01:43 +00:00
lock ( this )
2008-05-30 12:27:06 +00:00
{
return ( GetWorkItemState ( ) = = WorkItemState . Canceled ) ;
}
}
}
# endregion
#region IHasWorkItemPriority Members
/// <summary>
/// Returns the priority of the work item
/// </summary>
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 ;
}
}
}
}
}