2013-05-01 22:00:46 +00:00
#region Release History
// Smart Thread Pool
// 7 Aug 2004 - Initial release
/ /
2017-01-05 19:07:37 +00:00
// 14 Sep 2004 - Bug fixes
2013-05-01 22:00:46 +00:00
/ /
// 15 Oct 2004 - Added new features
// - Work items return result.
// - Support waiting synchronization for multiple work items.
// - Work items can be cancelled.
// - Passage of the caller thread’ s context to the thread in the pool.
// - Minimal usage of WIN32 handles.
// - Minor bug fixes.
/ /
// 26 Dec 2004 - Changes:
// - Removed static constructors.
// - Added finalizers.
// - Changed Exceptions so they are serializable.
// - Fixed the bug in one of the SmartThreadPool constructors.
2017-01-05 19:07:37 +00:00
// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters.
2013-05-01 22:00:46 +00:00
// The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
// - Added PostExecute with options on which cases to call it.
// - Added option to dispose of the state objects.
// - Added a WaitForIdle() method that waits until the work items queue is empty.
// - Added an STPStartInfo class for the initialization of the thread pool.
2017-01-05 19:07:37 +00:00
// - Changed exception handling so if a work item throws an exception it
2013-05-01 22:00:46 +00:00
// is rethrown at GetResult(), rather then firing an UnhandledException event.
// Note that PostExecute exception are always ignored.
/ /
// 25 Mar 2005 - Changes:
// - Fixed lost of work items bug
/ /
// 3 Jul 2005: Changes.
2017-01-05 19:07:37 +00:00
// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed.
2013-05-01 22:00:46 +00:00
/ /
// 16 Aug 2005: Changes.
2017-01-05 19:07:37 +00:00
// - Fixed bug where the InUseThreads becomes negative when canceling work items.
2013-05-01 22:00:46 +00:00
/ /
// 31 Jan 2006 - Changes:
// - Added work items priority
// - Removed support of chained delegates in callbacks and post executes (nobody really use this)
// - Added work items groups
// - Added work items groups idle event
// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array
// it returns true rather then throwing an exception.
// - Added option to start the STP and the WIG as suspended
2017-01-05 19:07:37 +00:00
// - Exception behavior changed, the real exception is returned by an
2013-05-01 22:00:46 +00:00
// inner exception
// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.)
// - Added performance counters
// - Added priority to the threads in the pool
/ /
// 13 Feb 2006 - Changes:
// - Added a call to the dispose of the Performance Counter so
// their won't be a Performance Counter leak.
2017-01-05 19:07:37 +00:00
// - Added exception catch in case the Performance Counters cannot
2013-05-01 22:00:46 +00:00
// be created.
/ /
// 17 May 2008 - Changes:
// - Changed the dispose behavior and removed the Finalizers.
// - Enabled the change of the MaxThreads and MinThreads at run time.
2017-01-05 19:07:37 +00:00
// - Enabled the change of the Concurrency of a IWorkItemsGroup at run
// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency
// refers to the MaxThreads.
2013-05-01 22:00:46 +00:00
// - Improved the cancel behavior.
2017-01-05 19:07:37 +00:00
// - Added events for thread creation and termination.
2013-05-01 22:00:46 +00:00
// - Fixed the HttpContext context capture.
// - Changed internal collections so they use generic collections
// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup
// - Added support for WinCE
// - Added support for Action<T> and Func<T>
/ /
// 07 April 2009 - Changes:
// - Added support for Silverlight and Mono
// - Added Join, Choice, and Pipe to SmartThreadPool.
// - Added local performance counters (for Mono, Silverlight, and WindowsCE)
// - Changed duration measures from DateTime.Now to Stopwatch.
// - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList<T>.
/ /
// 21 December 2009 - Changes:
// - Added work item timeout (passive)
/ /
// 20 August 2012 - Changes:
// - Added set name to threads
2017-01-05 19:07:37 +00:00
// - Fixed the WorkItemsQueue.Dequeue.
2013-05-01 22:00:46 +00:00
// Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... }
// - Fixed SmartThreadPool.Pipe
// - Added IsBackground option to threads
// - Added ApartmentState to threads
// - Fixed thread creation when queuing many work items at the same time.
/ /
// 24 August 2012 - Changes:
// - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan
2017-01-05 19:07:37 +00:00
// - Added option to set MaxStackSize of threads
2013-05-01 22:00:46 +00:00
# endregion
using System ;
using System.Security ;
using System.Threading ;
using System.Collections ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Runtime.CompilerServices ;
using Amib.Threading.Internal ;
namespace Amib.Threading
{
2017-01-05 19:07:37 +00:00
#region SmartThreadPool class
/// <summary>
/// Smart thread pool class.
/// </summary>
public partial class SmartThreadPool : WorkItemsGroupBase , IDisposable
{
#region Public Default Constants
/// <summary>
/// Default minimum number of threads the thread pool contains. (0)
/// </summary>
public const int DefaultMinWorkerThreads = 0 ;
/// <summary>
/// Default maximum number of threads the thread pool contains. (25)
/// </summary>
public const int DefaultMaxWorkerThreads = 25 ;
/// <summary>
/// Default idle timeout in milliseconds. (One minute)
/// </summary>
public const int DefaultIdleTimeout = 60 * 1000 ; // One minute
/// <summary>
/// Indicate to copy the security context of the caller and then use it in the call. (false)
/// </summary>
public const bool DefaultUseCallerCallContext = false ;
/// <summary>
/// Indicate to copy the HTTP context of the caller and then use it in the call. (false)
/// </summary>
public const bool DefaultUseCallerHttpContext = false ;
/// <summary>
/// Indicate to dispose of the state objects if they support the IDispose interface. (false)
/// </summary>
public const bool DefaultDisposeOfStateObjects = false ;
/// <summary>
2013-05-01 22:00:46 +00:00
/// The default option to run the post execute (CallToPostExecute.Always)
2017-01-05 19:07:37 +00:00
/// </summary>
public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute . Always ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// The default post execute method to run. (None)
/// When null it means not to call it.
/// </summary>
public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
2013-05-01 22:00:46 +00:00
/// The default work item priority (WorkItemPriority.Normal)
2017-01-05 19:07:37 +00:00
/// </summary>
public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority . Normal ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// The default is to work on work items as soon as they arrive
/// and not to wait for the start. (false)
/// </summary>
public const bool DefaultStartSuspended = false ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
2013-05-01 22:00:46 +00:00
/// The default name to use for the performance counters instance. (null)
2017-01-05 19:07:37 +00:00
/// </summary>
public static readonly string DefaultPerformanceCounterInstanceName ;
2013-05-01 22:00:46 +00:00
#if !(WINDOWS_PHONE)
2017-01-05 19:07:37 +00:00
/// <summary>
2013-05-01 22:00:46 +00:00
/// The default thread priority (ThreadPriority.Normal)
2017-01-05 19:07:37 +00:00
/// </summary>
public const ThreadPriority DefaultThreadPriority = ThreadPriority . Normal ;
2013-05-01 22:00:46 +00:00
# endif
/// <summary>
/// The default thread pool name. (SmartThreadPool)
/// </summary>
public const string DefaultThreadPoolName = "SmartThreadPool" ;
/// <summary>
/// The default Max Stack Size. (SmartThreadPool)
/// </summary>
public static readonly int? DefaultMaxStackSize = null ;
/// <summary>
/// The default fill state with params. (false)
/// It is relevant only to QueueWorkItem of Action<...>/Func<...>
/// </summary>
public const bool DefaultFillStateWithArgs = false ;
/// <summary>
/// The default thread backgroundness. (true)
/// </summary>
public const bool DefaultAreThreadsBackground = true ;
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
/// <summary>
2017-01-05 19:07:37 +00:00
/// The default apartment state of a thread in the thread pool.
/// The default is ApartmentState.Unknown which means the STP will not
2013-05-01 22:00:46 +00:00
/// set the apartment of the thread. It will use the .NET default.
/// </summary>
public const ApartmentState DefaultApartmentState = ApartmentState . Unknown ;
# endif
2017-01-05 19:07:37 +00:00
# endregion
2013-05-01 22:00:46 +00:00
#region Member Variables
2017-01-05 19:07:37 +00:00
/// <summary>
/// Dictionary of all the threads in the thread pool.
/// </summary>
2013-05-01 22:00:46 +00:00
private readonly SynchronizedDictionary < Thread , ThreadEntry > _workerThreads = new SynchronizedDictionary < Thread , ThreadEntry > ( ) ;
2017-01-05 19:07:37 +00:00
/// <summary>
/// Queue of work items.
/// </summary>
private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue ( ) ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Count the work items handled.
/// Used by the performance counter.
/// </summary>
private int _workItemsProcessed ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Number of threads that currently work (not idle).
/// </summary>
private int _inUseWorkerThreads ;
2013-05-01 22:00:46 +00:00
/// <summary>
/// Stores a copy of the original STPStartInfo.
/// It is used to change the MinThread and MaxThreads
/// </summary>
private STPStartInfo _stpStartInfo ;
2017-01-05 19:07:37 +00:00
/// <summary>
/// Total number of work items that are stored in the work items queue
/// plus the work items that the threads in the pool are working on.
/// </summary>
private int _currentWorkItemsCount ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Signaled when the thread pool is idle, i.e. no thread is busy
/// and the work items queue is empty
/// </summary>
//private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory . CreateManualResetEvent ( true ) ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// An event to signal all the threads to quit immediately.
/// </summary>
//private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory . CreateManualResetEvent ( false ) ;
2013-05-01 22:00:46 +00:00
/// <summary>
/// A flag to indicate if the Smart Thread Pool is now suspended.
/// </summary>
private bool _isSuspended ;
2017-01-05 19:07:37 +00:00
/// <summary>
/// A flag to indicate the threads to quit.
/// </summary>
private bool _shutdown ;
/// <summary>
/// Counts the threads created in the pool.
/// It is used to name the threads.
/// </summary>
private int _threadCounter ;
/// <summary>
/// Indicate that the SmartThreadPool has been disposed
/// </summary>
private bool _isDisposed ;
/// <summary>
/// Holds all the WorkItemsGroup instaces that have at least one
/// work item int the SmartThreadPool
/// This variable is used in case of Shutdown
/// </summary>
2013-05-01 22:00:46 +00:00
private readonly SynchronizedDictionary < IWorkItemsGroup , IWorkItemsGroup > _workItemsGroups = new SynchronizedDictionary < IWorkItemsGroup , IWorkItemsGroup > ( ) ;
/// <summary>
/// A common object for all the work items int the STP
/// so we can mark them to cancel in O(1)
/// </summary>
private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup ( ) ;
/// <summary>
/// Windows STP performance counters
/// </summary>
private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters . Instance ;
/// <summary>
/// Local STP performance counters
/// </summary>
private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters . Instance ;
2017-01-05 19:07:37 +00:00
#if (WINDOWS_PHONE)
2013-05-01 22:00:46 +00:00
private static readonly Dictionary < int , ThreadEntry > _threadEntries = new Dictionary < int , ThreadEntry > ( ) ;
#elif (_WINDOWS_CE)
private static LocalDataStoreSlot _threadEntrySlot = Thread . AllocateDataSlot ( ) ;
# else
[ThreadStatic]
private static ThreadEntry _threadEntry ;
# endif
/// <summary>
2017-01-05 19:07:37 +00:00
/// An event to call after a thread is created, but before
2013-05-01 22:00:46 +00:00
/// it's first use.
/// </summary>
private event ThreadInitializationHandler _onThreadInitialization ;
/// <summary>
2017-01-05 19:07:37 +00:00
/// An event to call when a thread is about to exit, after
2013-05-01 22:00:46 +00:00
/// it is no longer belong to the pool.
/// </summary>
private event ThreadTerminationHandler _onThreadTermination ;
# endregion
#region Per thread properties
/// <summary>
2017-01-05 19:07:37 +00:00
/// A reference to the current work item a thread from the thread pool
2013-05-01 22:00:46 +00:00
/// is executing.
/// </summary>
internal static ThreadEntry CurrentThreadEntry
{
#if (WINDOWS_PHONE)
get
{
lock ( _threadEntries )
{
ThreadEntry threadEntry ;
if ( _threadEntries . TryGetValue ( Thread . CurrentThread . ManagedThreadId , out threadEntry ) )
{
return threadEntry ;
}
}
return null ;
}
set
{
lock ( _threadEntries )
{
_threadEntries [ Thread . CurrentThread . ManagedThreadId ] = value ;
}
}
#elif (_WINDOWS_CE)
get
{
//Thread.CurrentThread.ManagedThreadId
return Thread . GetData ( _threadEntrySlot ) as ThreadEntry ;
}
set
{
Thread . SetData ( _threadEntrySlot , value ) ;
}
# else
get
{
return _threadEntry ;
}
set
{
_threadEntry = value ;
}
# endif
}
# endregion
#region Construction and Finalization
/// <summary>
2017-01-05 19:07:37 +00:00
/// Constructor
/// </summary>
public SmartThreadPool ( )
{
2013-05-01 22:00:46 +00:00
_stpStartInfo = new STPStartInfo ( ) ;
Initialize ( ) ;
2017-01-05 19:07:37 +00:00
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="idleTimeout">Idle timeout in milliseconds</param>
public SmartThreadPool ( int idleTimeout )
{
2013-05-01 22:00:46 +00:00
_stpStartInfo = new STPStartInfo
{
IdleTimeout = idleTimeout ,
} ;
2017-01-05 19:07:37 +00:00
Initialize ( ) ;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="idleTimeout">Idle timeout in milliseconds</param>
/// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
public SmartThreadPool (
int idleTimeout ,
int maxWorkerThreads )
{
2013-05-01 22:00:46 +00:00
_stpStartInfo = new STPStartInfo
{
IdleTimeout = idleTimeout ,
MaxWorkerThreads = maxWorkerThreads ,
} ;
2017-01-05 19:07:37 +00:00
Initialize ( ) ;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="idleTimeout">Idle timeout in milliseconds</param>
/// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
/// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
public SmartThreadPool (
int idleTimeout ,
int maxWorkerThreads ,
int minWorkerThreads )
{
2013-05-01 22:00:46 +00:00
_stpStartInfo = new STPStartInfo
{
IdleTimeout = idleTimeout ,
MaxWorkerThreads = maxWorkerThreads ,
MinWorkerThreads = minWorkerThreads ,
} ;
2017-01-05 19:07:37 +00:00
Initialize ( ) ;
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Constructor
/// </summary>
/// <param name="stpStartInfo">A SmartThreadPool configuration that overrides the default behavior</param>
2017-01-05 19:07:37 +00:00
public SmartThreadPool ( STPStartInfo stpStartInfo )
{
_stpStartInfo = new STPStartInfo ( stpStartInfo ) ;
Initialize ( ) ;
}
private void Initialize ( )
{
2013-05-01 22:00:46 +00:00
Name = _stpStartInfo . ThreadPoolName ;
2017-01-05 19:07:37 +00:00
ValidateSTPStartInfo ( ) ;
2013-05-01 22:00:46 +00:00
// _stpStartInfoRW stores a read/write copy of the STPStartInfo.
// Actually only MaxWorkerThreads and MinWorkerThreads are overwritten
_isSuspended = _stpStartInfo . StartSuspended ;
#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE)
2017-01-05 19:07:37 +00:00
if ( null ! = _stpStartInfo . PerformanceCounterInstanceName )
{
2013-05-01 22:00:46 +00:00
throw new NotSupportedException ( "Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters" ) ;
}
# else
if ( null ! = _stpStartInfo . PerformanceCounterInstanceName )
{
try
{
_windowsPCs = new STPInstancePerformanceCounters ( _stpStartInfo . PerformanceCounterInstanceName ) ;
}
catch ( Exception e )
{
Debug . WriteLine ( "Unable to create Performance Counters: " + e ) ;
_windowsPCs = NullSTPInstancePerformanceCounters . Instance ;
}
}
# endif
if ( _stpStartInfo . EnableLocalPerformanceCounters )
{
_localPCs = new LocalSTPInstancePerformanceCounters ( ) ;
}
2017-01-05 19:07:37 +00:00
// If the STP is not started suspended then start the threads.
2013-05-01 22:00:46 +00:00
if ( ! _isSuspended )
{
StartOptimalNumberOfThreads ( ) ;
}
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
private void StartOptimalNumberOfThreads ( )
{
int threadsCount = Math . Max ( _workItemsQueue . Count , _stpStartInfo . MinWorkerThreads ) ;
2013-05-01 22:00:46 +00:00
threadsCount = Math . Min ( threadsCount , _stpStartInfo . MaxWorkerThreads ) ;
threadsCount - = _workerThreads . Count ;
if ( threadsCount > 0 )
{
StartThreads ( threadsCount ) ;
}
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
private void ValidateSTPStartInfo ( )
{
2013-05-01 22:00:46 +00:00
if ( _stpStartInfo . MinWorkerThreads < 0 )
2017-01-05 19:07:37 +00:00
{
throw new ArgumentOutOfRangeException (
"MinWorkerThreads" , "MinWorkerThreads cannot be negative" ) ;
}
2013-05-01 22:00:46 +00:00
if ( _stpStartInfo . MaxWorkerThreads < = 0 )
2017-01-05 19:07:37 +00:00
{
throw new ArgumentOutOfRangeException (
"MaxWorkerThreads" , "MaxWorkerThreads must be greater than zero" ) ;
}
2013-05-01 22:00:46 +00:00
if ( _stpStartInfo . MinWorkerThreads > _stpStartInfo . MaxWorkerThreads )
2017-01-05 19:07:37 +00:00
{
throw new ArgumentOutOfRangeException (
"MinWorkerThreads, maxWorkerThreads" ,
"MaxWorkerThreads must be greater or equal to MinWorkerThreads" ) ;
}
}
private static void ValidateCallback ( Delegate callback )
{
if ( callback . GetInvocationList ( ) . Length > 1 )
{
throw new NotSupportedException ( "SmartThreadPool doesn't support delegates chains" ) ;
}
}
# endregion
#region Thread Processing
/// <summary>
/// Waits on the queue for a work item, shutdown, or timeout.
/// </summary>
/// <returns>
/// Returns the WaitingCallback or null in case of timeout or shutdown.
/// </returns>
private WorkItem Dequeue ( )
{
WorkItem workItem =
2013-05-01 22:00:46 +00:00
_workItemsQueue . DequeueWorkItem ( _stpStartInfo . IdleTimeout , _shuttingDownEvent ) ;
2017-01-05 19:07:37 +00:00
return workItem ;
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Put a new work item in the queue
/// </summary>
/// <param name="workItem">A work item to queue</param>
internal override void Enqueue ( WorkItem workItem )
{
// Make sure the workItem is not null
Debug . Assert ( null ! = workItem ) ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
IncrementWorkItemsCount ( ) ;
2013-05-01 22:00:46 +00:00
workItem . CanceledSmartThreadPool = _canceledSmartThreadPool ;
2017-01-05 19:07:37 +00:00
_workItemsQueue . EnqueueWorkItem ( workItem ) ;
workItem . WorkItemIsQueued ( ) ;
// If all the threads are busy then try to create a new one
if ( _currentWorkItemsCount > _workerThreads . Count )
{
StartThreads ( 1 ) ;
}
}
private void IncrementWorkItemsCount ( )
{
_windowsPCs . SampleWorkItems ( _workItemsQueue . Count , _workItemsProcessed ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleWorkItems ( _workItemsQueue . Count , _workItemsProcessed ) ;
2017-01-05 19:07:37 +00:00
int count = Interlocked . Increment ( ref _currentWorkItemsCount ) ;
//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
if ( count = = 1 )
{
2013-05-01 22:00:46 +00:00
IsIdle = false ;
_isIdleWaitHandle . Reset ( ) ;
2017-01-05 19:07:37 +00:00
}
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
private void DecrementWorkItemsCount ( )
{
2013-05-01 22:00:46 +00:00
int count = Interlocked . Decrement ( ref _currentWorkItemsCount ) ;
//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
if ( count = = 0 )
{
IsIdle = true ;
_isIdleWaitHandle . Set ( ) ;
}
Interlocked . Increment ( ref _workItemsProcessed ) ;
if ( ! _shutdown )
{
2017-01-05 19:07:37 +00:00
// The counter counts even if the work item was cancelled
_windowsPCs . SampleWorkItems ( _workItemsQueue . Count , _workItemsProcessed ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleWorkItems ( _workItemsQueue . Count , _workItemsProcessed ) ;
}
2017-01-05 19:07:37 +00:00
}
internal void RegisterWorkItemsGroup ( IWorkItemsGroup workItemsGroup )
{
_workItemsGroups [ workItemsGroup ] = workItemsGroup ;
}
internal void UnregisterWorkItemsGroup ( IWorkItemsGroup workItemsGroup )
{
if ( _workItemsGroups . Contains ( workItemsGroup ) )
{
_workItemsGroups . Remove ( workItemsGroup ) ;
}
}
/// <summary>
/// Inform that the current thread is about to quit or quiting.
/// The same thread may call this method more than once.
/// </summary>
private void InformCompleted ( )
{
// There is no need to lock the two methods together
// since only the current thread removes itself
// and the _workerThreads is a synchronized dictionary
if ( _workerThreads . Contains ( Thread . CurrentThread ) )
{
_workerThreads . Remove ( Thread . CurrentThread ) ;
_windowsPCs . SampleThreads ( _workerThreads . Count , _inUseWorkerThreads ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleThreads ( _workerThreads . Count , _inUseWorkerThreads ) ;
2017-01-05 19:07:37 +00:00
}
}
/// <summary>
/// Starts new threads
/// </summary>
/// <param name="threadsCount">The number of threads to start</param>
private void StartThreads ( int threadsCount )
{
2013-05-01 22:00:46 +00:00
if ( _isSuspended )
2017-01-05 19:07:37 +00:00
{
return ;
}
lock ( _workerThreads . SyncRoot )
{
// Don't start threads on shut down
if ( _shutdown )
{
return ;
}
for ( int i = 0 ; i < threadsCount ; + + i )
{
// Don't create more threads then the upper limit
2013-05-01 22:00:46 +00:00
if ( _workerThreads . Count > = _stpStartInfo . MaxWorkerThreads )
2017-01-05 19:07:37 +00:00
{
return ;
}
2013-05-01 22:00:46 +00:00
// Create a new thread
#if (_SILVERLIGHT) || (WINDOWS_PHONE)
2017-01-05 19:07:37 +00:00
Thread workerThread = new Thread ( ProcessQueuedItems ) ;
2013-05-01 22:00:46 +00:00
# else
Thread workerThread =
_stpStartInfo . MaxStackSize . HasValue
? new Thread ( ProcessQueuedItems , _stpStartInfo . MaxStackSize . Value )
: new Thread ( ProcessQueuedItems ) ;
# endif
2017-01-05 19:07:37 +00:00
// Configure the new thread and start it
2013-05-01 22:00:46 +00:00
workerThread . IsBackground = _stpStartInfo . AreThreadsBackground ;
#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE)
if ( _stpStartInfo . ApartmentState ! = ApartmentState . Unknown )
{
workerThread . SetApartmentState ( _stpStartInfo . ApartmentState ) ;
}
# endif
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
workerThread . Priority = _stpStartInfo . ThreadPriority ;
# endif
2014-09-18 00:03:54 +00:00
workerThread . Name = string . Format ( "STP:{0}:{1}" , Name , _threadCounter ) ;
2016-05-05 14:21:15 +00:00
workerThread . Start ( ) ;
+ + _threadCounter ;
2013-05-01 22:00:46 +00:00
// Add it to the dictionary and update its creation time.
_workerThreads [ workerThread ] = new ThreadEntry ( this ) ;
2017-01-05 19:07:37 +00:00
_windowsPCs . SampleThreads ( _workerThreads . Count , _inUseWorkerThreads ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleThreads ( _workerThreads . Count , _inUseWorkerThreads ) ;
2017-01-05 19:07:37 +00:00
}
}
}
/// <summary>
/// A worker thread method that processes work items from the work items queue.
/// </summary>
private void ProcessQueuedItems ( )
{
2013-05-01 22:00:46 +00:00
// Keep the entry of the dictionary as thread's variable to avoid the synchronization locks
// of the dictionary.
CurrentThreadEntry = _workerThreads [ Thread . CurrentThread ] ;
FireOnThreadInitialization ( ) ;
2017-01-05 19:07:37 +00:00
try
{
bool bInUseWorkerThreadsWasIncremented = false ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
// Process until shutdown.
while ( ! _shutdown )
{
// Update the last time this thread was seen alive.
// It's good for debugging.
2013-05-01 22:00:46 +00:00
CurrentThreadEntry . IAmAlive ( ) ;
// The following block handles the when the MaxWorkerThreads has been
// incremented by the user at run-time.
// Double lock for quit.
if ( _workerThreads . Count > _stpStartInfo . MaxWorkerThreads )
{
lock ( _workerThreads . SyncRoot )
{
if ( _workerThreads . Count > _stpStartInfo . MaxWorkerThreads )
{
// Inform that the thread is quiting and then quit.
// This method must be called within this lock or else
// more threads will quit and the thread pool will go
// below the lower limit.
InformCompleted ( ) ;
break ;
}
}
}
2017-01-05 19:07:37 +00:00
// Wait for a work item, shutdown, or timeout
WorkItem workItem = Dequeue ( ) ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
// Update the last time this thread was seen alive.
// It's good for debugging.
2013-05-01 22:00:46 +00:00
CurrentThreadEntry . IAmAlive ( ) ;
2017-01-05 19:07:37 +00:00
// On timeout or shut down.
if ( null = = workItem )
{
// Double lock for quit.
2013-05-01 22:00:46 +00:00
if ( _workerThreads . Count > _stpStartInfo . MinWorkerThreads )
2017-01-05 19:07:37 +00:00
{
lock ( _workerThreads . SyncRoot )
{
2013-05-01 22:00:46 +00:00
if ( _workerThreads . Count > _stpStartInfo . MinWorkerThreads )
2017-01-05 19:07:37 +00:00
{
// Inform that the thread is quiting and then quit.
// This method must be called within this lock or else
// more threads will quit and the thread pool will go
// below the lower limit.
InformCompleted ( ) ;
break ;
}
}
}
}
// If we didn't quit then skip to the next iteration.
if ( null = = workItem )
{
continue ;
}
try
{
// Initialize the value to false
bInUseWorkerThreadsWasIncremented = false ;
2013-05-01 22:00:46 +00:00
// Set the Current Work Item of the thread.
2017-01-05 19:07:37 +00:00
// Store the Current Work Item before the workItem.StartingWorkItem() is called,
// so WorkItem.Cancel can work when the work item is between InQueue and InProgress
2013-05-01 22:00:46 +00:00
// states.
2017-01-05 19:07:37 +00:00
// If the work item has been cancelled BEFORE the workItem.StartingWorkItem()
2013-05-01 22:00:46 +00:00
// (work item is in InQueue state) then workItem.StartingWorkItem() will return false.
// If the work item has been cancelled AFTER the workItem.StartingWorkItem() then
// (work item is in InProgress state) then the thread will be aborted
CurrentThreadEntry . CurrentWorkItem = workItem ;
2017-01-05 19:07:37 +00:00
// Change the state of the work item to 'in progress' if possible.
// We do it here so if the work item has been canceled we won't
// increment the _inUseWorkerThreads.
// The cancel mechanism doesn't delete items from the queue,
// it marks the work item as canceled, and when the work item
// is dequeued, we just skip it.
// If the post execute of work item is set to always or to
// call when the work item is canceled then the StartingWorkItem()
// will return true, so the post execute can run.
if ( ! workItem . StartingWorkItem ( ) )
{
continue ;
}
// Execute the callback. Make sure to accurately
// record how many callbacks are currently executing.
int inUseWorkerThreads = Interlocked . Increment ( ref _inUseWorkerThreads ) ;
_windowsPCs . SampleThreads ( _workerThreads . Count , inUseWorkerThreads ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleThreads ( _workerThreads . Count , inUseWorkerThreads ) ;
2017-01-05 19:07:37 +00:00
// Mark that the _inUseWorkerThreads incremented, so in the finally{}
// statement we will decrement it correctly.
bInUseWorkerThreadsWasIncremented = true ;
2013-05-01 22:00:46 +00:00
workItem . FireWorkItemStarted ( ) ;
2017-01-05 19:07:37 +00:00
ExecuteWorkItem ( workItem ) ;
}
catch ( Exception ex )
{
2013-05-01 22:00:46 +00:00
ex . GetHashCode ( ) ;
2017-01-05 19:07:37 +00:00
// Do nothing
}
finally
{
workItem . DisposeOfState ( ) ;
// Set the CurrentWorkItem to null, since we
// no longer run user's code.
2013-05-01 22:00:46 +00:00
CurrentThreadEntry . CurrentWorkItem = null ;
2017-01-05 19:07:37 +00:00
// Decrement the _inUseWorkerThreads only if we had
// incremented it. Note the cancelled work items don't
// increment _inUseWorkerThreads.
if ( bInUseWorkerThreadsWasIncremented )
{
int inUseWorkerThreads = Interlocked . Decrement ( ref _inUseWorkerThreads ) ;
_windowsPCs . SampleThreads ( _workerThreads . Count , inUseWorkerThreads ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleThreads ( _workerThreads . Count , inUseWorkerThreads ) ;
2017-01-05 19:07:37 +00:00
}
// Notify that the work item has been completed.
// WorkItemsGroup may enqueue their next work item.
workItem . FireWorkItemCompleted ( ) ;
// Decrement the number of work items here so the idle
// ManualResetEvent won't fluctuate.
DecrementWorkItemsCount ( ) ;
}
}
}
catch ( ThreadAbortException tae )
{
2013-05-01 22:00:46 +00:00
tae . GetHashCode ( ) ;
// Handle the abort exception gracfully.
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
2017-01-05 19:07:37 +00:00
Thread . ResetAbort ( ) ;
2013-05-01 22:00:46 +00:00
# endif
}
2017-01-05 19:07:37 +00:00
catch ( Exception e )
{
Debug . Assert ( null ! = e ) ;
}
finally
{
InformCompleted ( ) ;
2013-05-01 22:00:46 +00:00
FireOnThreadTermination ( ) ;
2017-01-05 19:07:37 +00:00
}
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
private void ExecuteWorkItem ( WorkItem workItem )
{
_windowsPCs . SampleWorkItemsWaitTime ( workItem . WaitingTime ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleWorkItemsWaitTime ( workItem . WaitingTime ) ;
2017-01-05 19:07:37 +00:00
try
{
workItem . Execute ( ) ;
}
finally
{
_windowsPCs . SampleWorkItemsProcessTime ( workItem . ProcessTime ) ;
2013-05-01 22:00:46 +00:00
_localPCs . SampleWorkItemsProcessTime ( workItem . ProcessTime ) ;
2017-01-05 19:07:37 +00:00
}
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
# endregion
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
#region Public Methods
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
private void ValidateWaitForIdle ( )
{
2013-05-01 22:00:46 +00:00
if ( null ! = CurrentThreadEntry & & CurrentThreadEntry . AssociatedSmartThreadPool = = this )
2017-01-05 19:07:37 +00:00
{
throw new NotSupportedException (
"WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock" ) ;
}
}
internal static void ValidateWorkItemsGroupWaitForIdle ( IWorkItemsGroup workItemsGroup )
{
2013-05-01 22:00:46 +00:00
if ( null = = CurrentThreadEntry )
{
return ;
}
WorkItem workItem = CurrentThreadEntry . CurrentWorkItem ;
ValidateWorkItemsGroupWaitForIdleImpl ( workItemsGroup , workItem ) ;
2017-01-05 19:07:37 +00:00
if ( ( null ! = workItemsGroup ) & &
2013-05-01 22:00:46 +00:00
( null ! = workItem ) & &
CurrentThreadEntry . CurrentWorkItem . WasQueuedBy ( workItemsGroup ) )
2017-01-05 19:07:37 +00:00
{
throw new NotSupportedException ( "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock" ) ;
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ValidateWorkItemsGroupWaitForIdleImpl ( IWorkItemsGroup workItemsGroup , WorkItem workItem )
{
if ( ( null ! = workItemsGroup ) & &
( null ! = workItem ) & &
workItem . WasQueuedBy ( workItemsGroup ) )
{
throw new NotSupportedException ( "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock" ) ;
}
}
/// <summary>
/// Force the SmartThreadPool to shutdown
/// </summary>
public void Shutdown ( )
{
Shutdown ( true , 0 ) ;
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Force the SmartThreadPool to shutdown with timeout
/// </summary>
public void Shutdown ( bool forceAbort , TimeSpan timeout )
2017-01-05 19:07:37 +00:00
{
Shutdown ( forceAbort , ( int ) timeout . TotalMilliseconds ) ;
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Empties the queue of work items and abort the threads in the pool.
/// </summary>
public void Shutdown ( bool forceAbort , int millisecondsTimeout )
{
ValidateNotDisposed ( ) ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
ISTPInstancePerformanceCounters pcs = _windowsPCs ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
if ( NullSTPInstancePerformanceCounters . Instance ! = _windowsPCs )
{
// Set the _pcs to "null" to stop updating the performance
// counters
_windowsPCs = NullSTPInstancePerformanceCounters . Instance ;
2013-05-01 22:00:46 +00:00
pcs . Dispose ( ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
Thread [ ] threads ;
lock ( _workerThreads . SyncRoot )
{
// Shutdown the work items queue
_workItemsQueue . Dispose ( ) ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
// Signal the threads to exit
_shutdown = true ;
_shuttingDownEvent . Set ( ) ;
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
// Make a copy of the threads' references in the pool
threads = new Thread [ _workerThreads . Count ] ;
_workerThreads . Keys . CopyTo ( threads , 0 ) ;
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
int millisecondsLeft = millisecondsTimeout ;
2013-05-01 22:00:46 +00:00
Stopwatch stopwatch = Stopwatch . StartNew ( ) ;
//DateTime start = DateTime.UtcNow;
2017-01-05 19:07:37 +00:00
bool waitInfinitely = ( Timeout . Infinite = = millisecondsTimeout ) ;
bool timeout = false ;
// Each iteration we update the time left for the timeout.
foreach ( Thread thread in threads )
{
// Join don't work with negative numbers
if ( ! waitInfinitely & & ( millisecondsLeft < 0 ) )
{
timeout = true ;
break ;
}
// Wait for the thread to terminate
bool success = thread . Join ( millisecondsLeft ) ;
if ( ! success )
{
timeout = true ;
break ;
}
if ( ! waitInfinitely )
{
// Update the time left to wait
2013-05-01 22:00:46 +00:00
//TimeSpan ts = DateTime.UtcNow - start;
millisecondsLeft = millisecondsTimeout - ( int ) stopwatch . ElapsedMilliseconds ;
2017-01-05 19:07:37 +00:00
}
}
if ( timeout & & forceAbort )
{
// Abort the threads in the pool
foreach ( Thread thread in threads )
{
if ( ( thread ! = null )
2013-05-01 22:00:46 +00:00
#if !(_WINDOWS_CE)
& & thread . IsAlive
2017-01-05 19:07:37 +00:00
# endif
2013-05-01 22:00:46 +00:00
)
2017-01-05 19:07:37 +00:00
{
try
{
2013-05-01 22:00:46 +00:00
thread . Abort ( ) ; // Shutdown
2017-01-05 19:07:37 +00:00
}
catch ( SecurityException e )
{
2013-05-01 22:00:46 +00:00
e . GetHashCode ( ) ;
2017-01-05 19:07:37 +00:00
}
catch ( ThreadStateException ex )
{
2013-05-01 22:00:46 +00:00
ex . GetHashCode ( ) ;
2017-01-05 19:07:37 +00:00
// In case the thread has been terminated
// after the check if it is alive.
}
}
}
}
}
/// <summary>
/// Wait for all work items to complete
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <returns>
/// true when every work item in workItemResults has completed; otherwise false.
/// </returns>
public static bool WaitAll (
IWaitableResult [ ] waitableResults )
{
2013-05-01 22:00:46 +00:00
return WaitAll ( waitableResults , Timeout . Infinite , true ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Wait for all work items to complete
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds 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>
/// <returns>
/// true when every work item in workItemResults has completed; otherwise false.
/// </returns>
public static bool WaitAll (
IWaitableResult [ ] waitableResults ,
TimeSpan timeout ,
bool exitContext )
{
2013-05-01 22:00:46 +00:00
return WaitAll ( waitableResults , ( int ) timeout . TotalMilliseconds , exitContext ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Wait for all work items to complete
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds 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 workItemResults has completed; otherwise false.
/// </returns>
public static bool WaitAll (
IWaitableResult [ ] waitableResults ,
TimeSpan timeout ,
bool exitContext ,
WaitHandle cancelWaitHandle )
{
2013-05-01 22:00:46 +00:00
return WaitAll ( waitableResults , ( int ) timeout . TotalMilliseconds , exitContext , cancelWaitHandle ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Wait for all work items to complete
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <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>
/// <returns>
/// true when every work item in workItemResults has completed; otherwise false.
/// </returns>
public static bool WaitAll (
IWaitableResult [ ] waitableResults ,
int millisecondsTimeout ,
bool exitContext )
{
2013-05-01 22:00:46 +00:00
return WorkItem . WaitAll ( waitableResults , millisecondsTimeout , exitContext , null ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Wait for all work items to complete
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <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 workItemResults has completed; otherwise false.
/// </returns>
public static bool WaitAll (
IWaitableResult [ ] waitableResults ,
int millisecondsTimeout ,
bool exitContext ,
WaitHandle cancelWaitHandle )
{
2013-05-01 22:00:46 +00:00
return WorkItem . WaitAll ( waitableResults , millisecondsTimeout , exitContext , cancelWaitHandle ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <returns>
/// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
/// </returns>
public static int WaitAny (
IWaitableResult [ ] waitableResults )
{
2013-05-01 22:00:46 +00:00
return WaitAny ( waitableResults , Timeout . Infinite , true ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds 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>
/// <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>
public static int WaitAny (
2013-05-01 22:00:46 +00:00
IWaitableResult [ ] waitableResults ,
2017-01-05 19:07:37 +00:00
TimeSpan timeout ,
bool exitContext )
{
2013-05-01 22:00:46 +00:00
return WaitAny ( waitableResults , ( int ) timeout . TotalMilliseconds , exitContext ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds 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>
public static int WaitAny (
IWaitableResult [ ] waitableResults ,
TimeSpan timeout ,
bool exitContext ,
WaitHandle cancelWaitHandle )
{
2013-05-01 22:00:46 +00:00
return WaitAny ( waitableResults , ( int ) timeout . TotalMilliseconds , exitContext , cancelWaitHandle ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <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>
/// <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>
public static int WaitAny (
IWaitableResult [ ] waitableResults ,
int millisecondsTimeout ,
bool exitContext )
{
2013-05-01 22:00:46 +00:00
return WorkItem . WaitAny ( waitableResults , millisecondsTimeout , exitContext , null ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary>
2013-05-01 22:00:46 +00:00
/// <param name="waitableResults">Array of work item result objects</param>
2017-01-05 19:07:37 +00:00
/// <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>
public static int WaitAny (
IWaitableResult [ ] waitableResults ,
int millisecondsTimeout ,
bool exitContext ,
WaitHandle cancelWaitHandle )
{
2013-05-01 22:00:46 +00:00
return WorkItem . WaitAny ( waitableResults , millisecondsTimeout , exitContext , cancelWaitHandle ) ;
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Creates a new WorkItemsGroup.
/// </summary>
/// <param name="concurrency">The number of work items that can be run concurrently</param>
/// <returns>A reference to the WorkItemsGroup</returns>
2017-01-05 19:07:37 +00:00
public IWorkItemsGroup CreateWorkItemsGroup ( int concurrency )
{
2013-05-01 22:00:46 +00:00
IWorkItemsGroup workItemsGroup = new WorkItemsGroup ( this , concurrency , _stpStartInfo ) ;
2017-01-05 19:07:37 +00:00
return workItemsGroup ;
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Creates a new WorkItemsGroup.
/// </summary>
/// <param name="concurrency">The number of work items that can be run concurrently</param>
/// <param name="wigStartInfo">A WorkItemsGroup configuration that overrides the default behavior</param>
/// <returns>A reference to the WorkItemsGroup</returns>
2017-01-05 19:07:37 +00:00
public IWorkItemsGroup CreateWorkItemsGroup ( int concurrency , WIGStartInfo wigStartInfo )
{
IWorkItemsGroup workItemsGroup = new WorkItemsGroup ( this , concurrency , wigStartInfo ) ;
return workItemsGroup ;
}
2013-05-01 22:00:46 +00:00
#region Fire Thread's Events
private void FireOnThreadInitialization ( )
{
if ( null ! = _onThreadInitialization )
{
foreach ( ThreadInitializationHandler tih in _onThreadInitialization . GetInvocationList ( ) )
{
try
{
tih ( ) ;
}
catch ( Exception e )
{
e . GetHashCode ( ) ;
Debug . Assert ( false ) ;
throw ;
}
}
}
}
private void FireOnThreadTermination ( )
{
if ( null ! = _onThreadTermination )
{
foreach ( ThreadTerminationHandler tth in _onThreadTermination . GetInvocationList ( ) )
{
try
{
tth ( ) ;
}
catch ( Exception e )
{
e . GetHashCode ( ) ;
Debug . Assert ( false ) ;
throw ;
}
}
}
}
# endregion
/// <summary>
/// This event is fired when a thread is created.
/// Use it to initialize a thread before the work items use it.
/// </summary>
public event ThreadInitializationHandler OnThreadInitialization
{
add { _onThreadInitialization + = value ; }
remove { _onThreadInitialization - = value ; }
}
/// <summary>
/// This event is fired when a thread is terminating.
/// Use it for cleanup.
/// </summary>
public event ThreadTerminationHandler OnThreadTermination
{
add { _onThreadTermination + = value ; }
remove { _onThreadTermination - = value ; }
}
internal void CancelAbortWorkItemsGroup ( WorkItemsGroup wig )
{
foreach ( ThreadEntry threadEntry in _workerThreads . Values )
{
WorkItem workItem = threadEntry . CurrentWorkItem ;
if ( null ! = workItem & &
workItem . WasQueuedBy ( wig ) & &
! workItem . IsCanceled )
{
threadEntry . CurrentWorkItem . GetWorkItemResult ( ) . Cancel ( true ) ;
}
}
}
2017-01-05 19:07:37 +00:00
# endregion
#region Properties
2013-05-01 22:00:46 +00:00
2017-01-05 19:07:37 +00:00
/// <summary>
/// Get/Set the lower limit of threads in the pool.
/// </summary>
public int MinThreads
{
get
{
ValidateNotDisposed ( ) ;
return _stpStartInfo . MinWorkerThreads ;
}
2013-05-01 22:00:46 +00:00
set
{
Debug . Assert ( value > = 0 ) ;
Debug . Assert ( value < = _stpStartInfo . MaxWorkerThreads ) ;
if ( _stpStartInfo . MaxWorkerThreads < value )
{
_stpStartInfo . MaxWorkerThreads = value ;
}
_stpStartInfo . MinWorkerThreads = value ;
StartOptimalNumberOfThreads ( ) ;
}
2017-01-05 19:07:37 +00:00
}
/// <summary>
/// Get/Set the upper limit of threads in the pool.
/// </summary>
public int MaxThreads
{
get
{
ValidateNotDisposed ( ) ;
return _stpStartInfo . MaxWorkerThreads ;
}
set
{
2013-05-01 22:00:46 +00:00
Debug . Assert ( value > 0 ) ;
Debug . Assert ( value > = _stpStartInfo . MinWorkerThreads ) ;
if ( _stpStartInfo . MinWorkerThreads > value )
{
_stpStartInfo . MinWorkerThreads = value ;
}
_stpStartInfo . MaxWorkerThreads = value ;
StartOptimalNumberOfThreads ( ) ;
2017-01-05 19:07:37 +00:00
}
}
/// <summary>
/// Get the number of threads in the thread pool.
/// Should be between the lower and the upper limits.
/// </summary>
public int ActiveThreads
{
get
{
ValidateNotDisposed ( ) ;
return _workerThreads . Count ;
}
}
/// <summary>
/// Get the number of busy (not idle) threads in the thread pool.
/// </summary>
public int InUseThreads
{
get
{
ValidateNotDisposed ( ) ;
return _inUseWorkerThreads ;
}
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Returns true if the current running work item has been cancelled.
/// Must be used within the work item's callback method.
/// The work item should sample this value in order to know if it
/// needs to quit before its completion.
/// </summary>
public static bool IsWorkItemCanceled
{
get
{
return CurrentThreadEntry . CurrentWorkItem . IsCanceled ;
}
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Checks if the work item has been cancelled, and if yes then abort the thread.
/// Can be used with Cancel and timeout
/// </summary>
public static void AbortOnWorkItemCancel ( )
{
if ( IsWorkItemCanceled )
{
Thread . CurrentThread . Abort ( ) ;
}
}
/// <summary>
/// Thread Pool start information (readonly)
/// </summary>
public STPStartInfo STPStartInfo
{
2017-01-05 19:07:37 +00:00
get
2013-05-01 22:00:46 +00:00
{
2017-01-05 19:07:37 +00:00
return _stpStartInfo . AsReadOnly ( ) ;
2013-05-01 22:00:46 +00:00
}
}
2017-01-05 19:07:37 +00:00
public bool IsShuttingdown
{
2013-05-01 22:00:46 +00:00
get { return _shutdown ; }
2017-01-05 19:07:37 +00:00
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Return the local calculated performance counters
/// Available only if STPStartInfo.EnableLocalPerformanceCounters is true.
/// </summary>
public ISTPPerformanceCountersReader PerformanceCountersReader
{
get { return ( ISTPPerformanceCountersReader ) _localPCs ; }
}
# endregion
#region IDisposable Members
public void Dispose ( )
{
if ( ! _isDisposed )
{
if ( ! _shutdown )
{
Shutdown ( ) ;
}
if ( null ! = _shuttingDownEvent )
{
_shuttingDownEvent . Close ( ) ;
_shuttingDownEvent = null ;
}
_workerThreads . Clear ( ) ;
2017-01-05 19:07:37 +00:00
2013-05-01 22:00:46 +00:00
if ( null ! = _isIdleWaitHandle )
{
_isIdleWaitHandle . Close ( ) ;
_isIdleWaitHandle = null ;
}
2016-08-22 04:47:19 +00:00
if ( _stpStartInfo . EnableLocalPerformanceCounters )
_localPCs . Dispose ( ) ;
2013-05-01 22:00:46 +00:00
_isDisposed = true ;
}
}
private void ValidateNotDisposed ( )
{
if ( _isDisposed )
{
throw new ObjectDisposedException ( GetType ( ) . ToString ( ) , "The SmartThreadPool has been shutdown" ) ;
}
}
# endregion
#region WorkItemsGroupBase Overrides
/// <summary>
/// Get/Set the maximum number of work items that execute cocurrency on the thread pool
/// </summary>
public override int Concurrency
2017-01-05 19:07:37 +00:00
{
get { return MaxThreads ; }
set { MaxThreads = value ; }
}
/// <summary>
/// Get the number of work items in the queue.
/// </summary>
public override int WaitingCallbacks
{
get
{
ValidateNotDisposed ( ) ;
return _workItemsQueue . Count ;
}
}
2013-05-01 22:00:46 +00:00
/// <summary>
/// Get an array with all the state objects of the currently running items.
/// The array represents a snap shot and impact performance.
/// </summary>
public override object [ ] GetStates ( )
{
object [ ] states = _workItemsQueue . GetStates ( ) ;
return states ;
}
/// <summary>
/// WorkItemsGroup start information (readonly)
/// </summary>
public override WIGStartInfo WIGStartInfo
{
get { return _stpStartInfo . AsReadOnly ( ) ; }
}
2017-01-05 19:07:37 +00:00
/// <summary>
2013-05-01 22:00:46 +00:00
/// Start the thread pool if it was started suspended.
/// If it is already running, this method is ignored.
/// </summary>
public override void Start ( )
{
if ( ! _isSuspended )
{
return ;
}
_isSuspended = false ;
ICollection workItemsGroups = _workItemsGroups . Values ;
foreach ( WorkItemsGroup workItemsGroup in workItemsGroups )
{
workItemsGroup . OnSTPIsStarting ( ) ;
}
StartOptimalNumberOfThreads ( ) ;
}
/// <summary>
/// Cancel all work items using thread abortion
/// </summary>
/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
public override void Cancel ( bool abortExecution )
{
_canceledSmartThreadPool . IsCanceled = true ;
_canceledSmartThreadPool = new CanceledWorkItemsGroup ( ) ;
ICollection workItemsGroups = _workItemsGroups . Values ;
foreach ( WorkItemsGroup workItemsGroup in workItemsGroups )
{
workItemsGroup . Cancel ( abortExecution ) ;
}
if ( abortExecution )
{
foreach ( ThreadEntry threadEntry in _workerThreads . Values )
{
WorkItem workItem = threadEntry . CurrentWorkItem ;
if ( null ! = workItem & &
threadEntry . AssociatedSmartThreadPool = = this & &
! workItem . IsCanceled )
{
threadEntry . CurrentWorkItem . GetWorkItemResult ( ) . Cancel ( true ) ;
}
}
}
}
2017-01-05 19:07:37 +00:00
/// <summary>
2013-05-01 22:00:46 +00:00
/// Wait for the thread pool to be idle
/// </summary>
public override bool WaitForIdle ( int millisecondsTimeout )
{
ValidateWaitForIdle ( ) ;
return STPEventWaitHandle . WaitOne ( _isIdleWaitHandle , millisecondsTimeout , false ) ;
}
/// <summary>
/// This event is fired when all work items are completed.
/// (When IsIdle changes to true)
/// This event only work on WorkItemsGroup. On SmartThreadPool
/// it throws the NotImplementedException.
/// </summary>
public override event WorkItemsGroupIdleHandler OnIdle
{
add
{
throw new NotImplementedException ( "This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature." ) ;
//_onIdle += value;
}
remove
{
throw new NotImplementedException ( "This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature." ) ;
//_onIdle -= value;
}
}
2017-01-05 19:07:37 +00:00
internal override void PreQueueWorkItem ( )
2013-05-01 22:00:46 +00:00
{
2017-01-05 19:07:37 +00:00
ValidateNotDisposed ( ) ;
2013-05-01 22:00:46 +00:00
}
# endregion
#region Join, Choice, Pipe, etc.
/// <summary>
/// Executes all actions in parallel.
/// Returns when they all finish.
/// </summary>
/// <param name="actions">Actions to execute</param>
public void Join ( IEnumerable < Action > actions )
{
WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true } ;
IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup ( int . MaxValue , wigStartInfo ) ;
foreach ( Action action in actions )
{
workItemsGroup . QueueWorkItem ( action ) ;
}
workItemsGroup . Start ( ) ;
workItemsGroup . WaitForIdle ( ) ;
}
/// <summary>
/// Executes all actions in parallel.
/// Returns when they all finish.
/// </summary>
/// <param name="actions">Actions to execute</param>
public void Join ( params Action [ ] actions )
{
Join ( ( IEnumerable < Action > ) actions ) ;
}
private class ChoiceIndex
{
public int _index = - 1 ;
}
/// <summary>
/// Executes all actions in parallel
/// Returns when the first one completes
/// </summary>
/// <param name="actions">Actions to execute</param>
public int Choice ( IEnumerable < Action > actions )
{
WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true } ;
IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup ( int . MaxValue , wigStartInfo ) ;
ManualResetEvent anActionCompleted = new ManualResetEvent ( false ) ;
ChoiceIndex choiceIndex = new ChoiceIndex ( ) ;
2017-01-05 19:07:37 +00:00
2013-05-01 22:00:46 +00:00
int i = 0 ;
foreach ( Action action in actions )
{
Action act = action ;
int value = i ;
workItemsGroup . QueueWorkItem ( ( ) = > { act ( ) ; Interlocked . CompareExchange ( ref choiceIndex . _index , value , - 1 ) ; anActionCompleted . Set ( ) ; } ) ;
+ + i ;
}
2017-01-05 19:07:37 +00:00
workItemsGroup . Start ( ) ;
2013-05-01 22:00:46 +00:00
anActionCompleted . WaitOne ( ) ;
2016-08-22 03:13:05 +00:00
anActionCompleted . Dispose ( ) ;
2013-05-01 22:00:46 +00:00
return choiceIndex . _index ;
}
/// <summary>
/// Executes all actions in parallel
/// Returns when the first one completes
/// </summary>
/// <param name="actions">Actions to execute</param>
public int Choice ( params Action [ ] actions )
2017-01-05 19:07:37 +00:00
{
2013-05-01 22:00:46 +00:00
return Choice ( ( IEnumerable < Action > ) actions ) ;
}
/// <summary>
/// Executes actions in sequence asynchronously.
/// Returns immediately.
/// </summary>
/// <param name="pipeState">A state context that passes </param>
/// <param name="actions">Actions to execute in the order they should run</param>
public void Pipe < T > ( T pipeState , IEnumerable < Action < T > > actions )
{
WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true } ;
IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup ( 1 , wigStartInfo ) ;
foreach ( Action < T > action in actions )
{
Action < T > act = action ;
workItemsGroup . QueueWorkItem ( ( ) = > act ( pipeState ) ) ;
}
workItemsGroup . Start ( ) ;
workItemsGroup . WaitForIdle ( ) ;
}
/// <summary>
/// Executes actions in sequence asynchronously.
/// Returns immediately.
/// </summary>
/// <param name="pipeState"></param>
/// <param name="actions">Actions to execute in the order they should run</param>
public void Pipe < T > ( T pipeState , params Action < T > [ ] actions )
{
Pipe ( pipeState , ( IEnumerable < Action < T > > ) actions ) ;
}
# endregion
2017-01-05 19:07:37 +00:00
}
# endregion
2013-05-01 22:00:46 +00:00
}