diff options
author | Justin Clark-Casey (justincc) | 2013-05-01 19:01:43 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2013-05-01 19:01:43 +0100 |
commit | 206fb306a7820cf593570e35ddfa8e7c5a10e449 (patch) | |
tree | 0ef0fdf42ddc0b63224af52b62b0bad42f62e352 /ThirdParty/SmartThreadPool/SmartThreadPool.cs | |
parent | Fix CAPS to work like they should - do not send caps to the viewer if they're... (diff) | |
download | opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.zip opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.gz opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.bz2 opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.xz |
Update SmartThreadPool to latest version 2.2.3 with a major and minor change.
SmartThreadPool code comes from http://www.codeproject.com/Articles/7933/Smart-Thread-Pool
This version implements thread abort (via WorkItem.Cancel(true)), threadpool naming, max thread stack, etc. so we no longer need to manually patch those.
However, two changes have been made to stock 2.2.3.
Major change: WorkItem.Cancel(bool abortExecution) in our version does not succeed if the work item was in progress and thread abort was not specified.
This is to match previous behaviour where we handle co-operative termination via another mechanism rather than checking WorkItem.IsCanceled.
Minor change: Did not add STP's StopWatch implementation as this is only used WinCE and Silverlight and causes a build clash with System.Diagnostics.StopWatch
The reason for updating is to see if this improves http://opensimulator.org/mantis/view.php?id=6557 and http://opensimulator.org/mantis/view.php?id=6586
Diffstat (limited to 'ThirdParty/SmartThreadPool/SmartThreadPool.cs')
-rw-r--r-- | ThirdParty/SmartThreadPool/SmartThreadPool.cs | 3180 |
1 files changed, 1732 insertions, 1448 deletions
diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs index 19a0007..9256777 100644 --- a/ThirdParty/SmartThreadPool/SmartThreadPool.cs +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs | |||
@@ -1,1448 +1,1732 @@ | |||
1 | // Ami Bar | 1 | #region Release History |
2 | // amibar@gmail.com | 2 | |
3 | // | 3 | // Smart Thread Pool |
4 | // Smart thread pool in C#. | 4 | // 7 Aug 2004 - Initial release |
5 | // 7 Aug 2004 - Initial release | 5 | // |
6 | // 14 Sep 2004 - Bug fixes | 6 | // 14 Sep 2004 - Bug fixes |
7 | // 15 Oct 2004 - Added new features | 7 | // |
8 | // - Work items return result. | 8 | // 15 Oct 2004 - Added new features |
9 | // - Support waiting synchronization for multiple work items. | 9 | // - Work items return result. |
10 | // - Work items can be cancelled. | 10 | // - Support waiting synchronization for multiple work items. |
11 | // - Passage of the caller thread’s context to the thread in the pool. | 11 | // - Work items can be cancelled. |
12 | // - Minimal usage of WIN32 handles. | 12 | // - Passage of the caller thread’s context to the thread in the pool. |
13 | // - Minor bug fixes. | 13 | // - Minimal usage of WIN32 handles. |
14 | // 26 Dec 2004 - Changes: | 14 | // - Minor bug fixes. |
15 | // - Removed static constructors. | 15 | // |
16 | // - Added finalizers. | 16 | // 26 Dec 2004 - Changes: |
17 | // - Changed Exceptions so they are serializable. | 17 | // - Removed static constructors. |
18 | // - Fixed the bug in one of the SmartThreadPool constructors. | 18 | // - Added finalizers. |
19 | // - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. | 19 | // - Changed Exceptions so they are serializable. |
20 | // The SmartThreadPool.WaitAny() is still limited by the .NET Framework. | 20 | // - Fixed the bug in one of the SmartThreadPool constructors. |
21 | // - Added PostExecute with options on which cases to call it. | 21 | // - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. |
22 | // - Added option to dispose of the state objects. | 22 | // The SmartThreadPool.WaitAny() is still limited by the .NET Framework. |
23 | // - Added a WaitForIdle() method that waits until the work items queue is empty. | 23 | // - Added PostExecute with options on which cases to call it. |
24 | // - Added an STPStartInfo class for the initialization of the thread pool. | 24 | // - Added option to dispose of the state objects. |
25 | // - Changed exception handling so if a work item throws an exception it | 25 | // - Added a WaitForIdle() method that waits until the work items queue is empty. |
26 | // is rethrown at GetResult(), rather then firing an UnhandledException event. | 26 | // - Added an STPStartInfo class for the initialization of the thread pool. |
27 | // Note that PostExecute exception are always ignored. | 27 | // - Changed exception handling so if a work item throws an exception it |
28 | // 25 Mar 2005 - Changes: | 28 | // is rethrown at GetResult(), rather then firing an UnhandledException event. |
29 | // - Fixed lost of work items bug | 29 | // Note that PostExecute exception are always ignored. |
30 | // 3 Jul 2005: Changes. | 30 | // |
31 | // - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. | 31 | // 25 Mar 2005 - Changes: |
32 | // 16 Aug 2005: Changes. | 32 | // - Fixed lost of work items bug |
33 | // - Fixed bug where the InUseThreads becomes negative when canceling work items. | 33 | // |
34 | // | 34 | // 3 Jul 2005: Changes. |
35 | // 31 Jan 2006 - Changes: | 35 | // - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. |
36 | // - Added work items priority | 36 | // |
37 | // - Removed support of chained delegates in callbacks and post executes (nobody really use this) | 37 | // 16 Aug 2005: Changes. |
38 | // - Added work items groups | 38 | // - Fixed bug where the InUseThreads becomes negative when canceling work items. |
39 | // - Added work items groups idle event | 39 | // |
40 | // - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array | 40 | // 31 Jan 2006 - Changes: |
41 | // it returns true rather then throwing an exception. | 41 | // - Added work items priority |
42 | // - Added option to start the STP and the WIG as suspended | 42 | // - Removed support of chained delegates in callbacks and post executes (nobody really use this) |
43 | // - Exception behavior changed, the real exception is returned by an | 43 | // - Added work items groups |
44 | // inner exception | 44 | // - Added work items groups idle event |
45 | // - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) | 45 | // - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array |
46 | // - Added performance counters | 46 | // it returns true rather then throwing an exception. |
47 | // - Added priority to the threads in the pool | 47 | // - Added option to start the STP and the WIG as suspended |
48 | // | 48 | // - Exception behavior changed, the real exception is returned by an |
49 | // 13 Feb 2006 - Changes: | 49 | // inner exception |
50 | // - Added a call to the dispose of the Performance Counter so | 50 | // - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) |
51 | // their won't be a Performance Counter leak. | 51 | // - Added performance counters |
52 | // - Added exception catch in case the Performance Counters cannot | 52 | // - Added priority to the threads in the pool |
53 | // be created. | 53 | // |
54 | 54 | // 13 Feb 2006 - Changes: | |
55 | using System; | 55 | // - Added a call to the dispose of the Performance Counter so |
56 | using System.Security; | 56 | // their won't be a Performance Counter leak. |
57 | using System.Threading; | 57 | // - Added exception catch in case the Performance Counters cannot |
58 | using System.Collections; | 58 | // be created. |
59 | using System.Diagnostics; | 59 | // |
60 | using System.Runtime.CompilerServices; | 60 | // 17 May 2008 - Changes: |
61 | 61 | // - Changed the dispose behavior and removed the Finalizers. | |
62 | using Amib.Threading.Internal; | 62 | // - Enabled the change of the MaxThreads and MinThreads at run time. |
63 | 63 | // - Enabled the change of the Concurrency of a IWorkItemsGroup at run | |
64 | namespace Amib.Threading | 64 | // time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency |
65 | { | 65 | // refers to the MaxThreads. |
66 | #region SmartThreadPool class | 66 | // - Improved the cancel behavior. |
67 | /// <summary> | 67 | // - Added events for thread creation and termination. |
68 | /// Smart thread pool class. | 68 | // - Fixed the HttpContext context capture. |
69 | /// </summary> | 69 | // - Changed internal collections so they use generic collections |
70 | public class SmartThreadPool : IWorkItemsGroup, IDisposable | 70 | // - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup |
71 | { | 71 | // - Added support for WinCE |
72 | #region Default Constants | 72 | // - Added support for Action<T> and Func<T> |
73 | 73 | // | |
74 | /// <summary> | 74 | // 07 April 2009 - Changes: |
75 | /// Default minimum number of threads the thread pool contains. (0) | 75 | // - Added support for Silverlight and Mono |
76 | /// </summary> | 76 | // - Added Join, Choice, and Pipe to SmartThreadPool. |
77 | public const int DefaultMinWorkerThreads = 0; | 77 | // - Added local performance counters (for Mono, Silverlight, and WindowsCE) |
78 | 78 | // - Changed duration measures from DateTime.Now to Stopwatch. | |
79 | /// <summary> | 79 | // - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList<T>. |
80 | /// Default maximum number of threads the thread pool contains. (25) | 80 | // |
81 | /// </summary> | 81 | // 21 December 2009 - Changes: |
82 | public const int DefaultMaxWorkerThreads = 25; | 82 | // - Added work item timeout (passive) |
83 | 83 | // | |
84 | /// <summary> | 84 | // 20 August 2012 - Changes: |
85 | /// Default idle timeout in milliseconds. (One minute) | 85 | // - Added set name to threads |
86 | /// </summary> | 86 | // - Fixed the WorkItemsQueue.Dequeue. |
87 | public const int DefaultIdleTimeout = 60*1000; // One minute | 87 | // Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... } |
88 | 88 | // - Fixed SmartThreadPool.Pipe | |
89 | /// <summary> | 89 | // - Added IsBackground option to threads |
90 | /// Indicate to copy the security context of the caller and then use it in the call. (false) | 90 | // - Added ApartmentState to threads |
91 | /// </summary> | 91 | // - Fixed thread creation when queuing many work items at the same time. |
92 | public const bool DefaultUseCallerCallContext = false; | 92 | // |
93 | 93 | // 24 August 2012 - Changes: | |
94 | /// <summary> | 94 | // - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan |
95 | /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) | 95 | // - Added option to set MaxStackSize of threads |
96 | /// </summary> | 96 | |
97 | public const bool DefaultUseCallerHttpContext = false; | 97 | #endregion |
98 | 98 | ||
99 | /// <summary> | 99 | using System; |
100 | /// Indicate to dispose of the state objects if they support the IDispose interface. (false) | 100 | using System.Security; |
101 | /// </summary> | 101 | using System.Threading; |
102 | public const bool DefaultDisposeOfStateObjects = false; | 102 | using System.Collections; |
103 | 103 | using System.Collections.Generic; | |
104 | /// <summary> | 104 | using System.Diagnostics; |
105 | /// The default option to run the post execute | 105 | using System.Runtime.CompilerServices; |
106 | /// </summary> | 106 | |
107 | public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; | 107 | using Amib.Threading.Internal; |
108 | 108 | ||
109 | /// <summary> | 109 | namespace Amib.Threading |
110 | /// The default post execute method to run. | 110 | { |
111 | /// When null it means not to call it. | 111 | #region SmartThreadPool class |
112 | /// </summary> | 112 | /// <summary> |
113 | public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null; | 113 | /// Smart thread pool class. |
114 | 114 | /// </summary> | |
115 | /// <summary> | 115 | public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable |
116 | /// The default work item priority | 116 | { |
117 | /// </summary> | 117 | #region Public Default Constants |
118 | public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; | 118 | |
119 | 119 | /// <summary> | |
120 | /// <summary> | 120 | /// Default minimum number of threads the thread pool contains. (0) |
121 | /// The default is to work on work items as soon as they arrive | 121 | /// </summary> |
122 | /// and not to wait for the start. | 122 | public const int DefaultMinWorkerThreads = 0; |
123 | /// </summary> | 123 | |
124 | public const bool DefaultStartSuspended = false; | 124 | /// <summary> |
125 | 125 | /// Default maximum number of threads the thread pool contains. (25) | |
126 | /// <summary> | 126 | /// </summary> |
127 | /// The default is not to use the performance counters | 127 | public const int DefaultMaxWorkerThreads = 25; |
128 | /// </summary> | 128 | |
129 | public static readonly string DefaultPerformanceCounterInstanceName = null; | 129 | /// <summary> |
130 | 130 | /// Default idle timeout in milliseconds. (One minute) | |
131 | public static readonly int DefaultStackSize = 0; | 131 | /// </summary> |
132 | 132 | public const int DefaultIdleTimeout = 60*1000; // One minute | |
133 | /// <summary> | 133 | |
134 | /// The default thread priority | 134 | /// <summary> |
135 | /// </summary> | 135 | /// Indicate to copy the security context of the caller and then use it in the call. (false) |
136 | public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; | 136 | /// </summary> |
137 | 137 | public const bool DefaultUseCallerCallContext = false; | |
138 | /// <summary> | 138 | |
139 | /// The default thread pool name | 139 | /// <summary> |
140 | /// </summary> | 140 | /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) |
141 | public const string DefaultThreadPoolName = "SmartThreadPool"; | 141 | /// </summary> |
142 | 142 | public const bool DefaultUseCallerHttpContext = false; | |
143 | #endregion | 143 | |
144 | 144 | /// <summary> | |
145 | #region Member Variables | 145 | /// Indicate to dispose of the state objects if they support the IDispose interface. (false) |
146 | 146 | /// </summary> | |
147 | /// <summary> | 147 | public const bool DefaultDisposeOfStateObjects = false; |
148 | /// Contains the name of this instance of SmartThreadPool. | 148 | |
149 | /// Can be changed by the user. | 149 | /// <summary> |
150 | /// </summary> | 150 | /// The default option to run the post execute (CallToPostExecute.Always) |
151 | private string _name = DefaultThreadPoolName; | 151 | /// </summary> |
152 | 152 | public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; | |
153 | /// <summary> | 153 | |
154 | /// Hashtable of all the threads in the thread pool. | 154 | /// <summary> |
155 | /// </summary> | 155 | /// The default post execute method to run. (None) |
156 | private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable()); | 156 | /// When null it means not to call it. |
157 | 157 | /// </summary> | |
158 | /// <summary> | 158 | public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback; |
159 | /// Queue of work items. | 159 | |
160 | /// </summary> | 160 | /// <summary> |
161 | private WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); | 161 | /// The default work item priority (WorkItemPriority.Normal) |
162 | 162 | /// </summary> | |
163 | /// <summary> | 163 | public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; |
164 | /// Count the work items handled. | 164 | |
165 | /// Used by the performance counter. | 165 | /// <summary> |
166 | /// </summary> | 166 | /// The default is to work on work items as soon as they arrive |
167 | private long _workItemsProcessed = 0; | 167 | /// and not to wait for the start. (false) |
168 | 168 | /// </summary> | |
169 | /// <summary> | 169 | public const bool DefaultStartSuspended = false; |
170 | /// Number of threads that currently work (not idle). | 170 | |
171 | /// </summary> | 171 | /// <summary> |
172 | private int _inUseWorkerThreads = 0; | 172 | /// The default name to use for the performance counters instance. (null) |
173 | 173 | /// </summary> | |
174 | /// <summary> | 174 | public static readonly string DefaultPerformanceCounterInstanceName; |
175 | /// Start information to use. | 175 | |
176 | /// It is simpler than providing many constructors. | 176 | #if !(WINDOWS_PHONE) |
177 | /// </summary> | 177 | |
178 | private STPStartInfo _stpStartInfo = new STPStartInfo(); | 178 | /// <summary> |
179 | 179 | /// The default thread priority (ThreadPriority.Normal) | |
180 | /// <summary> | 180 | /// </summary> |
181 | /// Total number of work items that are stored in the work items queue | 181 | public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; |
182 | /// plus the work items that the threads in the pool are working on. | 182 | #endif |
183 | /// </summary> | 183 | /// <summary> |
184 | private int _currentWorkItemsCount = 0; | 184 | /// The default thread pool name. (SmartThreadPool) |
185 | 185 | /// </summary> | |
186 | /// <summary> | 186 | public const string DefaultThreadPoolName = "SmartThreadPool"; |
187 | /// Signaled when the thread pool is idle, i.e. no thread is busy | 187 | |
188 | /// and the work items queue is empty | 188 | /// <summary> |
189 | /// </summary> | 189 | /// The default Max Stack Size. (SmartThreadPool) |
190 | private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); | 190 | /// </summary> |
191 | 191 | public static readonly int? DefaultMaxStackSize = null; | |
192 | /// <summary> | 192 | |
193 | /// An event to signal all the threads to quit immediately. | 193 | /// <summary> |
194 | /// </summary> | 194 | /// The default fill state with params. (false) |
195 | private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); | 195 | /// It is relevant only to QueueWorkItem of Action<...>/Func<...> |
196 | 196 | /// </summary> | |
197 | /// <summary> | 197 | public const bool DefaultFillStateWithArgs = false; |
198 | /// A flag to indicate the threads to quit. | 198 | |
199 | /// </summary> | 199 | /// <summary> |
200 | private bool _shutdown = false; | 200 | /// The default thread backgroundness. (true) |
201 | 201 | /// </summary> | |
202 | /// <summary> | 202 | public const bool DefaultAreThreadsBackground = true; |
203 | /// Counts the threads created in the pool. | 203 | |
204 | /// It is used to name the threads. | 204 | #if !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
205 | /// </summary> | 205 | /// <summary> |
206 | private int _threadCounter = 0; | 206 | /// The default apartment state of a thread in the thread pool. |
207 | 207 | /// The default is ApartmentState.Unknown which means the STP will not | |
208 | /// <summary> | 208 | /// set the apartment of the thread. It will use the .NET default. |
209 | /// Indicate that the SmartThreadPool has been disposed | 209 | /// </summary> |
210 | /// </summary> | 210 | public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; |
211 | private bool _isDisposed = false; | 211 | #endif |
212 | 212 | ||
213 | /// <summary> | 213 | #endregion |
214 | /// Event to send that the thread pool is idle | 214 | |
215 | /// </summary> | 215 | #region Member Variables |
216 | private event EventHandler _stpIdle; | 216 | |
217 | 217 | /// <summary> | |
218 | /// <summary> | 218 | /// Dictionary of all the threads in the thread pool. |
219 | /// On idle event | 219 | /// </summary> |
220 | /// </summary> | 220 | private readonly SynchronizedDictionary<Thread, ThreadEntry> _workerThreads = new SynchronizedDictionary<Thread, ThreadEntry>(); |
221 | //private event WorkItemsGroupIdleHandler _onIdle; | 221 | |
222 | 222 | /// <summary> | |
223 | /// <summary> | 223 | /// Queue of work items. |
224 | /// Holds all the WorkItemsGroup instaces that have at least one | 224 | /// </summary> |
225 | /// work item int the SmartThreadPool | 225 | private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); |
226 | /// This variable is used in case of Shutdown | 226 | |
227 | /// </summary> | 227 | /// <summary> |
228 | private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable()); | 228 | /// Count the work items handled. |
229 | 229 | /// Used by the performance counter. | |
230 | /// <summary> | 230 | /// </summary> |
231 | /// A reference from each thread in the thread pool to its SmartThreadPool | 231 | private int _workItemsProcessed; |
232 | /// object container. | 232 | |
233 | /// With this variable a thread can know whatever it belongs to a | 233 | /// <summary> |
234 | /// SmartThreadPool. | 234 | /// Number of threads that currently work (not idle). |
235 | /// </summary> | 235 | /// </summary> |
236 | [ThreadStatic] | 236 | private int _inUseWorkerThreads; |
237 | private static SmartThreadPool _smartThreadPool; | 237 | |
238 | 238 | /// <summary> | |
239 | /// <summary> | 239 | /// Stores a copy of the original STPStartInfo. |
240 | /// A reference to the current work item a thread from the thread pool | 240 | /// It is used to change the MinThread and MaxThreads |
241 | /// is executing. | 241 | /// </summary> |
242 | /// </summary> | 242 | private STPStartInfo _stpStartInfo; |
243 | [ThreadStatic] | 243 | |
244 | private static WorkItem _currentWorkItem; | 244 | /// <summary> |
245 | 245 | /// Total number of work items that are stored in the work items queue | |
246 | /// <summary> | 246 | /// plus the work items that the threads in the pool are working on. |
247 | /// STP performance counters | 247 | /// </summary> |
248 | /// </summary> | 248 | private int _currentWorkItemsCount; |
249 | private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance; | 249 | |
250 | 250 | /// <summary> | |
251 | #endregion | 251 | /// Signaled when the thread pool is idle, i.e. no thread is busy |
252 | 252 | /// and the work items queue is empty | |
253 | #region Construction and Finalization | 253 | /// </summary> |
254 | 254 | //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); | |
255 | /// <summary> | 255 | private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); |
256 | /// Constructor | 256 | |
257 | /// </summary> | 257 | /// <summary> |
258 | public SmartThreadPool() | 258 | /// An event to signal all the threads to quit immediately. |
259 | { | 259 | /// </summary> |
260 | Initialize(); | 260 | //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); |
261 | } | 261 | private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false); |
262 | 262 | ||
263 | /// <summary> | 263 | /// <summary> |
264 | /// Constructor | 264 | /// A flag to indicate if the Smart Thread Pool is now suspended. |
265 | /// </summary> | 265 | /// </summary> |
266 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | 266 | private bool _isSuspended; |
267 | public SmartThreadPool(int idleTimeout) | 267 | |
268 | { | 268 | /// <summary> |
269 | _stpStartInfo.IdleTimeout = idleTimeout; | 269 | /// A flag to indicate the threads to quit. |
270 | Initialize(); | 270 | /// </summary> |
271 | } | 271 | private bool _shutdown; |
272 | 272 | ||
273 | /// <summary> | 273 | /// <summary> |
274 | /// Constructor | 274 | /// Counts the threads created in the pool. |
275 | /// </summary> | 275 | /// It is used to name the threads. |
276 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | 276 | /// </summary> |
277 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> | 277 | private int _threadCounter; |
278 | public SmartThreadPool( | 278 | |
279 | int idleTimeout, | 279 | /// <summary> |
280 | int maxWorkerThreads) | 280 | /// Indicate that the SmartThreadPool has been disposed |
281 | { | 281 | /// </summary> |
282 | _stpStartInfo.IdleTimeout = idleTimeout; | 282 | private bool _isDisposed; |
283 | _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; | 283 | |
284 | Initialize(); | 284 | /// <summary> |
285 | } | 285 | /// Holds all the WorkItemsGroup instaces that have at least one |
286 | 286 | /// work item int the SmartThreadPool | |
287 | /// <summary> | 287 | /// This variable is used in case of Shutdown |
288 | /// Constructor | 288 | /// </summary> |
289 | /// </summary> | 289 | private readonly SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup> _workItemsGroups = new SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup>(); |
290 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | 290 | |
291 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> | 291 | /// <summary> |
292 | /// <param name="minWorkerThreads">Lower limit of threads in the pool</param> | 292 | /// A common object for all the work items int the STP |
293 | public SmartThreadPool( | 293 | /// so we can mark them to cancel in O(1) |
294 | int idleTimeout, | 294 | /// </summary> |
295 | int maxWorkerThreads, | 295 | private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup(); |
296 | int minWorkerThreads) | 296 | |
297 | { | 297 | /// <summary> |
298 | _stpStartInfo.IdleTimeout = idleTimeout; | 298 | /// Windows STP performance counters |
299 | _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; | 299 | /// </summary> |
300 | _stpStartInfo.MinWorkerThreads = minWorkerThreads; | 300 | private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance; |
301 | Initialize(); | 301 | |
302 | } | 302 | /// <summary> |
303 | 303 | /// Local STP performance counters | |
304 | /// <summary> | 304 | /// </summary> |
305 | /// Constructor | 305 | private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance; |
306 | /// </summary> | 306 | |
307 | public SmartThreadPool(STPStartInfo stpStartInfo) | 307 | |
308 | { | 308 | #if (WINDOWS_PHONE) |
309 | _stpStartInfo = new STPStartInfo(stpStartInfo); | 309 | private static readonly Dictionary<int, ThreadEntry> _threadEntries = new Dictionary<int, ThreadEntry>(); |
310 | Initialize(); | 310 | #elif (_WINDOWS_CE) |
311 | } | 311 | private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot(); |
312 | 312 | #else | |
313 | private void Initialize() | 313 | [ThreadStatic] |
314 | { | 314 | private static ThreadEntry _threadEntry; |
315 | Name = _stpStartInfo.ThreadPoolName; | 315 | |
316 | ValidateSTPStartInfo(); | 316 | #endif |
317 | 317 | ||
318 | if (null != _stpStartInfo.PerformanceCounterInstanceName) | 318 | /// <summary> |
319 | { | 319 | /// An event to call after a thread is created, but before |
320 | try | 320 | /// it's first use. |
321 | { | 321 | /// </summary> |
322 | _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); | 322 | private event ThreadInitializationHandler _onThreadInitialization; |
323 | } | 323 | |
324 | catch(Exception e) | 324 | /// <summary> |
325 | { | 325 | /// An event to call when a thread is about to exit, after |
326 | Debug.WriteLine("Unable to create Performance Counters: " + e.ToString()); | 326 | /// it is no longer belong to the pool. |
327 | _pcs = NullSTPInstancePerformanceCounters.Instance; | 327 | /// </summary> |
328 | } | 328 | private event ThreadTerminationHandler _onThreadTermination; |
329 | } | 329 | |
330 | 330 | #endregion | |
331 | StartOptimalNumberOfThreads(); | 331 | |
332 | } | 332 | #region Per thread properties |
333 | 333 | ||
334 | private void StartOptimalNumberOfThreads() | 334 | /// <summary> |
335 | { | 335 | /// A reference to the current work item a thread from the thread pool |
336 | int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); | 336 | /// is executing. |
337 | threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); | 337 | /// </summary> |
338 | StartThreads(threadsCount); | 338 | internal static ThreadEntry CurrentThreadEntry |
339 | } | 339 | { |
340 | 340 | #if (WINDOWS_PHONE) | |
341 | private void ValidateSTPStartInfo() | 341 | get |
342 | { | 342 | { |
343 | if (_stpStartInfo.MinWorkerThreads < 0) | 343 | lock(_threadEntries) |
344 | { | 344 | { |
345 | throw new ArgumentOutOfRangeException( | 345 | ThreadEntry threadEntry; |
346 | "MinWorkerThreads", "MinWorkerThreads cannot be negative"); | 346 | if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry)) |
347 | } | 347 | { |
348 | 348 | return threadEntry; | |
349 | if (_stpStartInfo.MaxWorkerThreads <= 0) | 349 | } |
350 | { | 350 | } |
351 | throw new ArgumentOutOfRangeException( | 351 | return null; |
352 | "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); | 352 | } |
353 | } | 353 | set |
354 | 354 | { | |
355 | if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) | 355 | lock(_threadEntries) |
356 | { | 356 | { |
357 | throw new ArgumentOutOfRangeException( | 357 | _threadEntries[Thread.CurrentThread.ManagedThreadId] = value; |
358 | "MinWorkerThreads, maxWorkerThreads", | 358 | } |
359 | "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); | 359 | } |
360 | } | 360 | #elif (_WINDOWS_CE) |
361 | } | 361 | get |
362 | 362 | { | |
363 | private void ValidateCallback(Delegate callback) | 363 | //Thread.CurrentThread.ManagedThreadId |
364 | { | 364 | return Thread.GetData(_threadEntrySlot) as ThreadEntry; |
365 | if(callback.GetInvocationList().Length > 1) | 365 | } |
366 | { | 366 | set |
367 | throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); | 367 | { |
368 | } | 368 | Thread.SetData(_threadEntrySlot, value); |
369 | } | 369 | } |
370 | 370 | #else | |
371 | #endregion | 371 | get |
372 | 372 | { | |
373 | #region Thread Processing | 373 | return _threadEntry; |
374 | 374 | } | |
375 | /// <summary> | 375 | set |
376 | /// Waits on the queue for a work item, shutdown, or timeout. | 376 | { |
377 | /// </summary> | 377 | _threadEntry = value; |
378 | /// <returns> | 378 | } |
379 | /// Returns the WaitingCallback or null in case of timeout or shutdown. | 379 | #endif |
380 | /// </returns> | 380 | } |
381 | private WorkItem Dequeue() | 381 | #endregion |
382 | { | 382 | |
383 | WorkItem workItem = | 383 | #region Construction and Finalization |
384 | _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); | 384 | |
385 | 385 | /// <summary> | |
386 | return workItem; | 386 | /// Constructor |
387 | } | 387 | /// </summary> |
388 | 388 | public SmartThreadPool() | |
389 | /// <summary> | 389 | { |
390 | /// Put a new work item in the queue | 390 | _stpStartInfo = new STPStartInfo(); |
391 | /// </summary> | 391 | Initialize(); |
392 | /// <param name="workItem">A work item to queue</param> | 392 | } |
393 | private void Enqueue(WorkItem workItem) | 393 | |
394 | { | 394 | /// <summary> |
395 | Enqueue(workItem, true); | 395 | /// Constructor |
396 | } | 396 | /// </summary> |
397 | 397 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | |
398 | /// <summary> | 398 | public SmartThreadPool(int idleTimeout) |
399 | /// Put a new work item in the queue | 399 | { |
400 | /// </summary> | 400 | _stpStartInfo = new STPStartInfo |
401 | /// <param name="workItem">A work item to queue</param> | 401 | { |
402 | internal void Enqueue(WorkItem workItem, bool incrementWorkItems) | 402 | IdleTimeout = idleTimeout, |
403 | { | 403 | }; |
404 | // Make sure the workItem is not null | 404 | Initialize(); |
405 | Debug.Assert(null != workItem); | 405 | } |
406 | 406 | ||
407 | if (incrementWorkItems) | 407 | /// <summary> |
408 | { | 408 | /// Constructor |
409 | IncrementWorkItemsCount(); | 409 | /// </summary> |
410 | } | 410 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> |
411 | 411 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> | |
412 | _workItemsQueue.EnqueueWorkItem(workItem); | 412 | public SmartThreadPool( |
413 | workItem.WorkItemIsQueued(); | 413 | int idleTimeout, |
414 | 414 | int maxWorkerThreads) | |
415 | // If all the threads are busy then try to create a new one | 415 | { |
416 | if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count) | 416 | _stpStartInfo = new STPStartInfo |
417 | { | 417 | { |
418 | StartThreads(1); | 418 | IdleTimeout = idleTimeout, |
419 | } | 419 | MaxWorkerThreads = maxWorkerThreads, |
420 | } | 420 | }; |
421 | 421 | Initialize(); | |
422 | private void IncrementWorkItemsCount() | 422 | } |
423 | { | 423 | |
424 | _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); | 424 | /// <summary> |
425 | 425 | /// Constructor | |
426 | int count = Interlocked.Increment(ref _currentWorkItemsCount); | 426 | /// </summary> |
427 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); | 427 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> |
428 | if (count == 1) | 428 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> |
429 | { | 429 | /// <param name="minWorkerThreads">Lower limit of threads in the pool</param> |
430 | //Trace.WriteLine("STP is NOT idle"); | 430 | public SmartThreadPool( |
431 | _isIdleWaitHandle.Reset(); | 431 | int idleTimeout, |
432 | } | 432 | int maxWorkerThreads, |
433 | } | 433 | int minWorkerThreads) |
434 | 434 | { | |
435 | private void DecrementWorkItemsCount() | 435 | _stpStartInfo = new STPStartInfo |
436 | { | 436 | { |
437 | ++_workItemsProcessed; | 437 | IdleTimeout = idleTimeout, |
438 | 438 | MaxWorkerThreads = maxWorkerThreads, | |
439 | // The counter counts even if the work item was cancelled | 439 | MinWorkerThreads = minWorkerThreads, |
440 | _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); | 440 | }; |
441 | 441 | Initialize(); | |
442 | int count = Interlocked.Decrement(ref _currentWorkItemsCount); | 442 | } |
443 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); | 443 | |
444 | if (count == 0) | 444 | /// <summary> |
445 | { | 445 | /// Constructor |
446 | //Trace.WriteLine("STP is idle"); | 446 | /// </summary> |
447 | _isIdleWaitHandle.Set(); | 447 | /// <param name="stpStartInfo">A SmartThreadPool configuration that overrides the default behavior</param> |
448 | } | 448 | public SmartThreadPool(STPStartInfo stpStartInfo) |
449 | } | 449 | { |
450 | 450 | _stpStartInfo = new STPStartInfo(stpStartInfo); | |
451 | internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | 451 | Initialize(); |
452 | { | 452 | } |
453 | _workItemsGroups[workItemsGroup] = workItemsGroup; | 453 | |
454 | } | 454 | private void Initialize() |
455 | 455 | { | |
456 | internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | 456 | Name = _stpStartInfo.ThreadPoolName; |
457 | { | 457 | ValidateSTPStartInfo(); |
458 | if (_workItemsGroups.Contains(workItemsGroup)) | 458 | |
459 | { | 459 | // _stpStartInfoRW stores a read/write copy of the STPStartInfo. |
460 | _workItemsGroups.Remove(workItemsGroup); | 460 | // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten |
461 | } | 461 | |
462 | } | 462 | _isSuspended = _stpStartInfo.StartSuspended; |
463 | 463 | ||
464 | /// <summary> | 464 | #if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE) |
465 | /// Inform that the current thread is about to quit or quiting. | 465 | if (null != _stpStartInfo.PerformanceCounterInstanceName) |
466 | /// The same thread may call this method more than once. | 466 | { |
467 | /// </summary> | 467 | throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); |
468 | private void InformCompleted() | 468 | } |
469 | { | 469 | #else |
470 | // There is no need to lock the two methods together | 470 | if (null != _stpStartInfo.PerformanceCounterInstanceName) |
471 | // since only the current thread removes itself | 471 | { |
472 | // and the _workerThreads is a synchronized hashtable | 472 | try |
473 | if (_workerThreads.Contains(Thread.CurrentThread)) | 473 | { |
474 | { | 474 | _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); |
475 | _workerThreads.Remove(Thread.CurrentThread); | 475 | } |
476 | _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); | 476 | catch (Exception e) |
477 | } | 477 | { |
478 | } | 478 | Debug.WriteLine("Unable to create Performance Counters: " + e); |
479 | 479 | _windowsPCs = NullSTPInstancePerformanceCounters.Instance; | |
480 | /// <summary> | 480 | } |
481 | /// Starts new threads | 481 | } |
482 | /// </summary> | 482 | #endif |
483 | /// <param name="threadsCount">The number of threads to start</param> | 483 | |
484 | private void StartThreads(int threadsCount) | 484 | if (_stpStartInfo.EnableLocalPerformanceCounters) |
485 | { | 485 | { |
486 | if (_stpStartInfo.StartSuspended) | 486 | _localPCs = new LocalSTPInstancePerformanceCounters(); |
487 | { | 487 | } |
488 | return; | 488 | |
489 | } | 489 | // If the STP is not started suspended then start the threads. |
490 | 490 | if (!_isSuspended) | |
491 | lock(_workerThreads.SyncRoot) | 491 | { |
492 | { | 492 | StartOptimalNumberOfThreads(); |
493 | // Don't start threads on shut down | 493 | } |
494 | if (_shutdown) | 494 | } |
495 | { | 495 | |
496 | return; | 496 | private void StartOptimalNumberOfThreads() |
497 | } | 497 | { |
498 | 498 | int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); | |
499 | for(int i = 0; i < threadsCount; ++i) | 499 | threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); |
500 | { | 500 | threadsCount -= _workerThreads.Count; |
501 | // Don't create more threads then the upper limit | 501 | if (threadsCount > 0) |
502 | if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) | 502 | { |
503 | { | 503 | StartThreads(threadsCount); |
504 | return; | 504 | } |
505 | } | 505 | } |
506 | 506 | ||
507 | // Create a new thread | 507 | private void ValidateSTPStartInfo() |
508 | Thread workerThread; | 508 | { |
509 | if (_stpStartInfo.StackSize > 0) | 509 | if (_stpStartInfo.MinWorkerThreads < 0) |
510 | workerThread = new Thread(ProcessQueuedItems, _stpStartInfo.StackSize); | 510 | { |
511 | else | 511 | throw new ArgumentOutOfRangeException( |
512 | workerThread = new Thread(ProcessQueuedItems); | 512 | "MinWorkerThreads", "MinWorkerThreads cannot be negative"); |
513 | 513 | } | |
514 | // Configure the new thread and start it | 514 | |
515 | workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; | 515 | if (_stpStartInfo.MaxWorkerThreads <= 0) |
516 | workerThread.IsBackground = true; | 516 | { |
517 | workerThread.Priority = _stpStartInfo.ThreadPriority; | 517 | throw new ArgumentOutOfRangeException( |
518 | workerThread.Start(); | 518 | "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); |
519 | ++_threadCounter; | 519 | } |
520 | 520 | ||
521 | // Add the new thread to the hashtable and update its creation | 521 | if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) |
522 | // time. | 522 | { |
523 | _workerThreads[workerThread] = DateTime.Now; | 523 | throw new ArgumentOutOfRangeException( |
524 | _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); | 524 | "MinWorkerThreads, maxWorkerThreads", |
525 | } | 525 | "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); |
526 | } | 526 | } |
527 | } | 527 | } |
528 | 528 | ||
529 | /// <summary> | 529 | private static void ValidateCallback(Delegate callback) |
530 | /// A worker thread method that processes work items from the work items queue. | 530 | { |
531 | /// </summary> | 531 | if(callback.GetInvocationList().Length > 1) |
532 | private void ProcessQueuedItems() | 532 | { |
533 | { | 533 | throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); |
534 | // Initialize the _smartThreadPool variable | 534 | } |
535 | _smartThreadPool = this; | 535 | } |
536 | 536 | ||
537 | try | 537 | #endregion |
538 | { | 538 | |
539 | bool bInUseWorkerThreadsWasIncremented = false; | 539 | #region Thread Processing |
540 | 540 | ||
541 | // Process until shutdown. | 541 | /// <summary> |
542 | while(!_shutdown) | 542 | /// Waits on the queue for a work item, shutdown, or timeout. |
543 | { | 543 | /// </summary> |
544 | // Update the last time this thread was seen alive. | 544 | /// <returns> |
545 | // It's good for debugging. | 545 | /// Returns the WaitingCallback or null in case of timeout or shutdown. |
546 | _workerThreads[Thread.CurrentThread] = DateTime.Now; | 546 | /// </returns> |
547 | 547 | private WorkItem Dequeue() | |
548 | // Wait for a work item, shutdown, or timeout | 548 | { |
549 | WorkItem workItem = Dequeue(); | 549 | WorkItem workItem = |
550 | 550 | _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); | |
551 | // Update the last time this thread was seen alive. | 551 | |
552 | // It's good for debugging. | 552 | return workItem; |
553 | _workerThreads[Thread.CurrentThread] = DateTime.Now; | 553 | } |
554 | 554 | ||
555 | // On timeout or shut down. | 555 | /// <summary> |
556 | if (null == workItem) | 556 | /// Put a new work item in the queue |
557 | { | 557 | /// </summary> |
558 | // Double lock for quit. | 558 | /// <param name="workItem">A work item to queue</param> |
559 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) | 559 | internal override void Enqueue(WorkItem workItem) |
560 | { | 560 | { |
561 | lock(_workerThreads.SyncRoot) | 561 | // Make sure the workItem is not null |
562 | { | 562 | Debug.Assert(null != workItem); |
563 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) | 563 | |
564 | { | 564 | IncrementWorkItemsCount(); |
565 | // Inform that the thread is quiting and then quit. | 565 | |
566 | // This method must be called within this lock or else | 566 | workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; |
567 | // more threads will quit and the thread pool will go | 567 | _workItemsQueue.EnqueueWorkItem(workItem); |
568 | // below the lower limit. | 568 | workItem.WorkItemIsQueued(); |
569 | InformCompleted(); | 569 | |
570 | break; | 570 | // If all the threads are busy then try to create a new one |
571 | } | 571 | if (_currentWorkItemsCount > _workerThreads.Count) |
572 | } | 572 | { |
573 | } | 573 | StartThreads(1); |
574 | } | 574 | } |
575 | 575 | } | |
576 | // If we didn't quit then skip to the next iteration. | 576 | |
577 | if (null == workItem) | 577 | private void IncrementWorkItemsCount() |
578 | { | 578 | { |
579 | continue; | 579 | _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
580 | } | 580 | _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
581 | 581 | ||
582 | try | 582 | int count = Interlocked.Increment(ref _currentWorkItemsCount); |
583 | { | 583 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); |
584 | // Initialize the value to false | 584 | if (count == 1) |
585 | bInUseWorkerThreadsWasIncremented = false; | 585 | { |
586 | 586 | IsIdle = false; | |
587 | // Change the state of the work item to 'in progress' if possible. | 587 | _isIdleWaitHandle.Reset(); |
588 | // We do it here so if the work item has been canceled we won't | 588 | } |
589 | // increment the _inUseWorkerThreads. | 589 | } |
590 | // The cancel mechanism doesn't delete items from the queue, | 590 | |
591 | // it marks the work item as canceled, and when the work item | 591 | private void DecrementWorkItemsCount() |
592 | // is dequeued, we just skip it. | 592 | { |
593 | // If the post execute of work item is set to always or to | 593 | int count = Interlocked.Decrement(ref _currentWorkItemsCount); |
594 | // call when the work item is canceled then the StartingWorkItem() | 594 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); |
595 | // will return true, so the post execute can run. | 595 | if (count == 0) |
596 | if (!workItem.StartingWorkItem()) | 596 | { |
597 | { | 597 | IsIdle = true; |
598 | continue; | 598 | _isIdleWaitHandle.Set(); |
599 | } | 599 | } |
600 | 600 | ||
601 | // Execute the callback. Make sure to accurately | 601 | Interlocked.Increment(ref _workItemsProcessed); |
602 | // record how many callbacks are currently executing. | 602 | |
603 | int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); | 603 | if (!_shutdown) |
604 | _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); | 604 | { |
605 | 605 | // The counter counts even if the work item was cancelled | |
606 | // Mark that the _inUseWorkerThreads incremented, so in the finally{} | 606 | _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
607 | // statement we will decrement it correctly. | 607 | _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
608 | bInUseWorkerThreadsWasIncremented = true; | 608 | } |
609 | 609 | ||
610 | // Set the _currentWorkItem to the current work item | 610 | } |
611 | _currentWorkItem = workItem; | 611 | |
612 | 612 | internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | |
613 | lock(workItem) | 613 | { |
614 | { | 614 | _workItemsGroups[workItemsGroup] = workItemsGroup; |
615 | workItem.currentThread = Thread.CurrentThread; | 615 | } |
616 | } | 616 | |
617 | 617 | internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | |
618 | ExecuteWorkItem(workItem); | 618 | { |
619 | 619 | if (_workItemsGroups.Contains(workItemsGroup)) | |
620 | lock(workItem) | 620 | { |
621 | { | 621 | _workItemsGroups.Remove(workItemsGroup); |
622 | workItem.currentThread = null; | 622 | } |
623 | } | 623 | } |
624 | 624 | ||
625 | } | 625 | /// <summary> |
626 | catch(ThreadAbortException ex) | 626 | /// Inform that the current thread is about to quit or quiting. |
627 | { | 627 | /// The same thread may call this method more than once. |
628 | lock(workItem) | 628 | /// </summary> |
629 | { | 629 | private void InformCompleted() |
630 | workItem.currentThread = null; | 630 | { |
631 | } | 631 | // There is no need to lock the two methods together |
632 | ex.GetHashCode(); | 632 | // since only the current thread removes itself |
633 | Thread.ResetAbort(); | 633 | // and the _workerThreads is a synchronized dictionary |
634 | } | 634 | if (_workerThreads.Contains(Thread.CurrentThread)) |
635 | catch(Exception ex) | 635 | { |
636 | { | 636 | _workerThreads.Remove(Thread.CurrentThread); |
637 | ex.GetHashCode(); | 637 | _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
638 | // Do nothing | 638 | _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
639 | } | 639 | } |
640 | finally | 640 | } |
641 | { | 641 | |
642 | lock(workItem) | 642 | /// <summary> |
643 | { | 643 | /// Starts new threads |
644 | workItem.currentThread = null; | 644 | /// </summary> |
645 | } | 645 | /// <param name="threadsCount">The number of threads to start</param> |
646 | 646 | private void StartThreads(int threadsCount) | |
647 | if (null != workItem) | 647 | { |
648 | { | 648 | if (_isSuspended) |
649 | workItem.DisposeOfState(); | 649 | { |
650 | } | 650 | return; |
651 | 651 | } | |
652 | // Set the _currentWorkItem to null, since we | 652 | |
653 | // no longer run user's code. | 653 | lock(_workerThreads.SyncRoot) |
654 | _currentWorkItem = null; | 654 | { |
655 | 655 | // Don't start threads on shut down | |
656 | // Decrement the _inUseWorkerThreads only if we had | 656 | if (_shutdown) |
657 | // incremented it. Note the cancelled work items don't | 657 | { |
658 | // increment _inUseWorkerThreads. | 658 | return; |
659 | if (bInUseWorkerThreadsWasIncremented) | 659 | } |
660 | { | 660 | |
661 | int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); | 661 | for(int i = 0; i < threadsCount; ++i) |
662 | _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); | 662 | { |
663 | } | 663 | // Don't create more threads then the upper limit |
664 | 664 | if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) | |
665 | // Notify that the work item has been completed. | 665 | { |
666 | // WorkItemsGroup may enqueue their next work item. | 666 | return; |
667 | workItem.FireWorkItemCompleted(); | 667 | } |
668 | 668 | ||
669 | // Decrement the number of work items here so the idle | 669 | // Create a new thread |
670 | // ManualResetEvent won't fluctuate. | 670 | |
671 | DecrementWorkItemsCount(); | 671 | #if (_SILVERLIGHT) || (WINDOWS_PHONE) |
672 | } | 672 | Thread workerThread = new Thread(ProcessQueuedItems); |
673 | } | 673 | #else |
674 | } | 674 | Thread workerThread = |
675 | catch(ThreadAbortException tae) | 675 | _stpStartInfo.MaxStackSize.HasValue |
676 | { | 676 | ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) |
677 | tae.GetHashCode(); | 677 | : new Thread(ProcessQueuedItems); |
678 | // Handle the abort exception gracfully. | 678 | #endif |
679 | Thread.ResetAbort(); | 679 | // Configure the new thread and start it |
680 | } | 680 | workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; |
681 | catch(Exception e) | 681 | workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; |
682 | { | 682 | |
683 | Debug.Assert(null != e); | 683 | #if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE) |
684 | } | 684 | if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) |
685 | finally | 685 | { |
686 | { | 686 | workerThread.SetApartmentState(_stpStartInfo.ApartmentState); |
687 | InformCompleted(); | 687 | } |
688 | } | 688 | #endif |
689 | } | 689 | |
690 | 690 | #if !(_SILVERLIGHT) && !(WINDOWS_PHONE) | |
691 | private void ExecuteWorkItem(WorkItem workItem) | 691 | workerThread.Priority = _stpStartInfo.ThreadPriority; |
692 | { | 692 | #endif |
693 | _pcs.SampleWorkItemsWaitTime(workItem.WaitingTime); | 693 | workerThread.Start(); |
694 | try | 694 | ++_threadCounter; |
695 | { | 695 | |
696 | workItem.Execute(); | 696 | // Add it to the dictionary and update its creation time. |
697 | } | 697 | _workerThreads[workerThread] = new ThreadEntry(this); |
698 | catch | 698 | |
699 | { | 699 | _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
700 | throw; | 700 | _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
701 | } | 701 | } |
702 | finally | 702 | } |
703 | { | 703 | } |
704 | _pcs.SampleWorkItemsProcessTime(workItem.ProcessTime); | 704 | |
705 | } | 705 | /// <summary> |
706 | } | 706 | /// A worker thread method that processes work items from the work items queue. |
707 | 707 | /// </summary> | |
708 | 708 | private void ProcessQueuedItems() | |
709 | #endregion | 709 | { |
710 | 710 | // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks | |
711 | #region Public Methods | 711 | // of the dictionary. |
712 | 712 | CurrentThreadEntry = _workerThreads[Thread.CurrentThread]; | |
713 | /// <summary> | 713 | |
714 | /// Queue a work item | 714 | FireOnThreadInitialization(); |
715 | /// </summary> | 715 | |
716 | /// <param name="callback">A callback to execute</param> | 716 | try |
717 | /// <returns>Returns a work item result</returns> | 717 | { |
718 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback) | 718 | bool bInUseWorkerThreadsWasIncremented = false; |
719 | { | 719 | |
720 | ValidateNotDisposed(); | 720 | // Process until shutdown. |
721 | ValidateCallback(callback); | 721 | while(!_shutdown) |
722 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback); | 722 | { |
723 | Enqueue(workItem); | 723 | // Update the last time this thread was seen alive. |
724 | return workItem.GetWorkItemResult(); | 724 | // It's good for debugging. |
725 | } | 725 | CurrentThreadEntry.IAmAlive(); |
726 | 726 | ||
727 | /// <summary> | 727 | // The following block handles the when the MaxWorkerThreads has been |
728 | /// Queue a work item | 728 | // incremented by the user at run-time. |
729 | /// </summary> | 729 | // Double lock for quit. |
730 | /// <param name="callback">A callback to execute</param> | 730 | if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) |
731 | /// <param name="workItemPriority">The priority of the work item</param> | 731 | { |
732 | /// <returns>Returns a work item result</returns> | 732 | lock (_workerThreads.SyncRoot) |
733 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) | 733 | { |
734 | { | 734 | if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) |
735 | ValidateNotDisposed(); | 735 | { |
736 | ValidateCallback(callback); | 736 | // Inform that the thread is quiting and then quit. |
737 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority); | 737 | // This method must be called within this lock or else |
738 | Enqueue(workItem); | 738 | // more threads will quit and the thread pool will go |
739 | return workItem.GetWorkItemResult(); | 739 | // below the lower limit. |
740 | } | 740 | InformCompleted(); |
741 | 741 | break; | |
742 | /// <summary> | 742 | } |
743 | /// Queue a work item | 743 | } |
744 | /// </summary> | 744 | } |
745 | /// <param name="workItemInfo">Work item info</param> | 745 | |
746 | /// <param name="callback">A callback to execute</param> | 746 | // Wait for a work item, shutdown, or timeout |
747 | /// <returns>Returns a work item result</returns> | 747 | WorkItem workItem = Dequeue(); |
748 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) | 748 | |
749 | { | 749 | // Update the last time this thread was seen alive. |
750 | ValidateNotDisposed(); | 750 | // It's good for debugging. |
751 | ValidateCallback(callback); | 751 | CurrentThreadEntry.IAmAlive(); |
752 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback); | 752 | |
753 | Enqueue(workItem); | 753 | // On timeout or shut down. |
754 | return workItem.GetWorkItemResult(); | 754 | if (null == workItem) |
755 | } | 755 | { |
756 | 756 | // Double lock for quit. | |
757 | /// <summary> | 757 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) |
758 | /// Queue a work item | 758 | { |
759 | /// </summary> | 759 | lock(_workerThreads.SyncRoot) |
760 | /// <param name="callback">A callback to execute</param> | 760 | { |
761 | /// <param name="state"> | 761 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) |
762 | /// The context object of the work item. Used for passing arguments to the work item. | 762 | { |
763 | /// </param> | 763 | // Inform that the thread is quiting and then quit. |
764 | /// <returns>Returns a work item result</returns> | 764 | // This method must be called within this lock or else |
765 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) | 765 | // more threads will quit and the thread pool will go |
766 | { | 766 | // below the lower limit. |
767 | ValidateNotDisposed(); | 767 | InformCompleted(); |
768 | ValidateCallback(callback); | 768 | break; |
769 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state); | 769 | } |
770 | Enqueue(workItem); | 770 | } |
771 | return workItem.GetWorkItemResult(); | 771 | } |
772 | } | 772 | } |
773 | 773 | ||
774 | /// <summary> | 774 | // If we didn't quit then skip to the next iteration. |
775 | /// Queue a work item | 775 | if (null == workItem) |
776 | /// </summary> | 776 | { |
777 | /// <param name="callback">A callback to execute</param> | 777 | continue; |
778 | /// <param name="state"> | 778 | } |
779 | /// The context object of the work item. Used for passing arguments to the work item. | 779 | |
780 | /// </param> | 780 | try |
781 | /// <param name="workItemPriority">The work item priority</param> | 781 | { |
782 | /// <returns>Returns a work item result</returns> | 782 | // Initialize the value to false |
783 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) | 783 | bInUseWorkerThreadsWasIncremented = false; |
784 | { | 784 | |
785 | ValidateNotDisposed(); | 785 | // Set the Current Work Item of the thread. |
786 | ValidateCallback(callback); | 786 | // Store the Current Work Item before the workItem.StartingWorkItem() is called, |
787 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority); | 787 | // so WorkItem.Cancel can work when the work item is between InQueue and InProgress |
788 | Enqueue(workItem); | 788 | // states. |
789 | return workItem.GetWorkItemResult(); | 789 | // If the work item has been cancelled BEFORE the workItem.StartingWorkItem() |
790 | } | 790 | // (work item is in InQueue state) then workItem.StartingWorkItem() will return false. |
791 | 791 | // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then | |
792 | /// <summary> | 792 | // (work item is in InProgress state) then the thread will be aborted |
793 | /// Queue a work item | 793 | CurrentThreadEntry.CurrentWorkItem = workItem; |
794 | /// </summary> | 794 | |
795 | /// <param name="workItemInfo">Work item information</param> | 795 | // Change the state of the work item to 'in progress' if possible. |
796 | /// <param name="callback">A callback to execute</param> | 796 | // We do it here so if the work item has been canceled we won't |
797 | /// <param name="state"> | 797 | // increment the _inUseWorkerThreads. |
798 | /// The context object of the work item. Used for passing arguments to the work item. | 798 | // The cancel mechanism doesn't delete items from the queue, |
799 | /// </param> | 799 | // it marks the work item as canceled, and when the work item |
800 | /// <returns>Returns a work item result</returns> | 800 | // is dequeued, we just skip it. |
801 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) | 801 | // If the post execute of work item is set to always or to |
802 | { | 802 | // call when the work item is canceled then the StartingWorkItem() |
803 | ValidateNotDisposed(); | 803 | // will return true, so the post execute can run. |
804 | ValidateCallback(callback); | 804 | if (!workItem.StartingWorkItem()) |
805 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state); | 805 | { |
806 | Enqueue(workItem); | 806 | continue; |
807 | return workItem.GetWorkItemResult(); | 807 | } |
808 | } | 808 | |
809 | 809 | // Execute the callback. Make sure to accurately | |
810 | /// <summary> | 810 | // record how many callbacks are currently executing. |
811 | /// Queue a work item | 811 | int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); |
812 | /// </summary> | 812 | _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
813 | /// <param name="callback">A callback to execute</param> | 813 | _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
814 | /// <param name="state"> | 814 | |
815 | /// The context object of the work item. Used for passing arguments to the work item. | 815 | // Mark that the _inUseWorkerThreads incremented, so in the finally{} |
816 | /// </param> | 816 | // statement we will decrement it correctly. |
817 | /// <param name="postExecuteWorkItemCallback"> | 817 | bInUseWorkerThreadsWasIncremented = true; |
818 | /// A delegate to call after the callback completion | 818 | |
819 | /// </param> | 819 | workItem.FireWorkItemStarted(); |
820 | /// <returns>Returns a work item result</returns> | 820 | |
821 | public IWorkItemResult QueueWorkItem( | 821 | ExecuteWorkItem(workItem); |
822 | WorkItemCallback callback, | 822 | } |
823 | object state, | 823 | catch(Exception ex) |
824 | PostExecuteWorkItemCallback postExecuteWorkItemCallback) | 824 | { |
825 | { | 825 | ex.GetHashCode(); |
826 | ValidateNotDisposed(); | 826 | // Do nothing |
827 | ValidateCallback(callback); | 827 | } |
828 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback); | 828 | finally |
829 | Enqueue(workItem); | 829 | { |
830 | return workItem.GetWorkItemResult(); | 830 | workItem.DisposeOfState(); |
831 | } | 831 | |
832 | 832 | // Set the CurrentWorkItem to null, since we | |
833 | /// <summary> | 833 | // no longer run user's code. |
834 | /// Queue a work item | 834 | CurrentThreadEntry.CurrentWorkItem = null; |
835 | /// </summary> | 835 | |
836 | /// <param name="callback">A callback to execute</param> | 836 | // Decrement the _inUseWorkerThreads only if we had |
837 | /// <param name="state"> | 837 | // incremented it. Note the cancelled work items don't |
838 | /// The context object of the work item. Used for passing arguments to the work item. | 838 | // increment _inUseWorkerThreads. |
839 | /// </param> | 839 | if (bInUseWorkerThreadsWasIncremented) |
840 | /// <param name="postExecuteWorkItemCallback"> | 840 | { |
841 | /// A delegate to call after the callback completion | 841 | int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); |
842 | /// </param> | 842 | _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
843 | /// <param name="workItemPriority">The work item priority</param> | 843 | _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
844 | /// <returns>Returns a work item result</returns> | 844 | } |
845 | public IWorkItemResult QueueWorkItem( | 845 | |
846 | WorkItemCallback callback, | 846 | // Notify that the work item has been completed. |
847 | object state, | 847 | // WorkItemsGroup may enqueue their next work item. |
848 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 848 | workItem.FireWorkItemCompleted(); |
849 | WorkItemPriority workItemPriority) | 849 | |
850 | { | 850 | // Decrement the number of work items here so the idle |
851 | ValidateNotDisposed(); | 851 | // ManualResetEvent won't fluctuate. |
852 | ValidateCallback(callback); | 852 | DecrementWorkItemsCount(); |
853 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); | 853 | } |
854 | Enqueue(workItem); | 854 | } |
855 | return workItem.GetWorkItemResult(); | 855 | } |
856 | } | 856 | catch(ThreadAbortException tae) |
857 | 857 | { | |
858 | /// <summary> | 858 | tae.GetHashCode(); |
859 | /// Queue a work item | 859 | // Handle the abort exception gracfully. |
860 | /// </summary> | 860 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
861 | /// <param name="callback">A callback to execute</param> | 861 | Thread.ResetAbort(); |
862 | /// <param name="state"> | 862 | #endif |
863 | /// The context object of the work item. Used for passing arguments to the work item. | 863 | } |
864 | /// </param> | 864 | catch(Exception e) |
865 | /// <param name="postExecuteWorkItemCallback"> | 865 | { |
866 | /// A delegate to call after the callback completion | 866 | Debug.Assert(null != e); |
867 | /// </param> | 867 | } |
868 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 868 | finally |
869 | /// <returns>Returns a work item result</returns> | 869 | { |
870 | public IWorkItemResult QueueWorkItem( | 870 | InformCompleted(); |
871 | WorkItemCallback callback, | 871 | FireOnThreadTermination(); |
872 | object state, | 872 | } |
873 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 873 | } |
874 | CallToPostExecute callToPostExecute) | 874 | |
875 | { | 875 | private void ExecuteWorkItem(WorkItem workItem) |
876 | ValidateNotDisposed(); | 876 | { |
877 | ValidateCallback(callback); | 877 | _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); |
878 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); | 878 | _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); |
879 | Enqueue(workItem); | 879 | try |
880 | return workItem.GetWorkItemResult(); | 880 | { |
881 | } | 881 | workItem.Execute(); |
882 | 882 | } | |
883 | /// <summary> | 883 | finally |
884 | /// Queue a work item | 884 | { |
885 | /// </summary> | 885 | _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); |
886 | /// <param name="callback">A callback to execute</param> | 886 | _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); |
887 | /// <param name="state"> | 887 | } |
888 | /// The context object of the work item. Used for passing arguments to the work item. | 888 | } |
889 | /// </param> | 889 | |
890 | /// <param name="postExecuteWorkItemCallback"> | 890 | |
891 | /// A delegate to call after the callback completion | 891 | #endregion |
892 | /// </param> | 892 | |
893 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 893 | #region Public Methods |
894 | /// <param name="workItemPriority">The work item priority</param> | 894 | |
895 | /// <returns>Returns a work item result</returns> | 895 | private void ValidateWaitForIdle() |
896 | public IWorkItemResult QueueWorkItem( | 896 | { |
897 | WorkItemCallback callback, | 897 | if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this) |
898 | object state, | 898 | { |
899 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 899 | throw new NotSupportedException( |
900 | CallToPostExecute callToPostExecute, | 900 | "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); |
901 | WorkItemPriority workItemPriority) | 901 | } |
902 | { | 902 | } |
903 | ValidateNotDisposed(); | 903 | |
904 | ValidateCallback(callback); | 904 | internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) |
905 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); | 905 | { |
906 | Enqueue(workItem); | 906 | if (null == CurrentThreadEntry) |
907 | return workItem.GetWorkItemResult(); | 907 | { |
908 | } | 908 | return; |
909 | 909 | } | |
910 | /// <summary> | 910 | |
911 | /// Wait for the thread pool to be idle | 911 | WorkItem workItem = CurrentThreadEntry.CurrentWorkItem; |
912 | /// </summary> | 912 | ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem); |
913 | public void WaitForIdle() | 913 | if ((null != workItemsGroup) && |
914 | { | 914 | (null != workItem) && |
915 | WaitForIdle(Timeout.Infinite); | 915 | CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup)) |
916 | } | 916 | { |
917 | 917 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); | |
918 | /// <summary> | 918 | } |
919 | /// Wait for the thread pool to be idle | 919 | } |
920 | /// </summary> | 920 | |
921 | public bool WaitForIdle(TimeSpan timeout) | 921 | [MethodImpl(MethodImplOptions.NoInlining)] |
922 | { | 922 | private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) |
923 | return WaitForIdle((int)timeout.TotalMilliseconds); | 923 | { |
924 | } | 924 | if ((null != workItemsGroup) && |
925 | 925 | (null != workItem) && | |
926 | /// <summary> | 926 | workItem.WasQueuedBy(workItemsGroup)) |
927 | /// Wait for the thread pool to be idle | 927 | { |
928 | /// </summary> | 928 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); |
929 | public bool WaitForIdle(int millisecondsTimeout) | 929 | } |
930 | { | 930 | } |
931 | ValidateWaitForIdle(); | 931 | |
932 | return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); | 932 | /// <summary> |
933 | } | 933 | /// Force the SmartThreadPool to shutdown |
934 | 934 | /// </summary> | |
935 | private void ValidateWaitForIdle() | 935 | public void Shutdown() |
936 | { | 936 | { |
937 | if(_smartThreadPool == this) | 937 | Shutdown(true, 0); |
938 | { | 938 | } |
939 | throw new NotSupportedException( | 939 | |
940 | "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); | 940 | /// <summary> |
941 | } | 941 | /// Force the SmartThreadPool to shutdown with timeout |
942 | } | 942 | /// </summary> |
943 | 943 | public void Shutdown(bool forceAbort, TimeSpan timeout) | |
944 | internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) | 944 | { |
945 | { | 945 | Shutdown(forceAbort, (int)timeout.TotalMilliseconds); |
946 | ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem); | 946 | } |
947 | if ((null != workItemsGroup) && | 947 | |
948 | (null != SmartThreadPool._currentWorkItem) && | 948 | /// <summary> |
949 | SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup)) | 949 | /// Empties the queue of work items and abort the threads in the pool. |
950 | { | 950 | /// </summary> |
951 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); | 951 | public void Shutdown(bool forceAbort, int millisecondsTimeout) |
952 | } | 952 | { |
953 | } | 953 | ValidateNotDisposed(); |
954 | 954 | ||
955 | [MethodImpl(MethodImplOptions.NoInlining)] | 955 | ISTPInstancePerformanceCounters pcs = _windowsPCs; |
956 | private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) | 956 | |
957 | { | 957 | if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs) |
958 | if ((null != workItemsGroup) && | 958 | { |
959 | (null != workItem) && | 959 | // Set the _pcs to "null" to stop updating the performance |
960 | workItem.WasQueuedBy(workItemsGroup)) | 960 | // counters |
961 | { | 961 | _windowsPCs = NullSTPInstancePerformanceCounters.Instance; |
962 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); | 962 | |
963 | } | 963 | pcs.Dispose(); |
964 | } | 964 | } |
965 | 965 | ||
966 | 966 | Thread [] threads; | |
967 | 967 | lock(_workerThreads.SyncRoot) | |
968 | /// <summary> | 968 | { |
969 | /// Force the SmartThreadPool to shutdown | 969 | // Shutdown the work items queue |
970 | /// </summary> | 970 | _workItemsQueue.Dispose(); |
971 | public void Shutdown() | 971 | |
972 | { | 972 | // Signal the threads to exit |
973 | Shutdown(true, 0); | 973 | _shutdown = true; |
974 | } | 974 | _shuttingDownEvent.Set(); |
975 | 975 | ||
976 | public void Shutdown(bool forceAbort, TimeSpan timeout) | 976 | // Make a copy of the threads' references in the pool |
977 | { | 977 | threads = new Thread [_workerThreads.Count]; |
978 | Shutdown(forceAbort, (int)timeout.TotalMilliseconds); | 978 | _workerThreads.Keys.CopyTo(threads, 0); |
979 | } | 979 | } |
980 | 980 | ||
981 | /// <summary> | 981 | int millisecondsLeft = millisecondsTimeout; |
982 | /// Empties the queue of work items and abort the threads in the pool. | 982 | Stopwatch stopwatch = Stopwatch.StartNew(); |
983 | /// </summary> | 983 | //DateTime start = DateTime.UtcNow; |
984 | public void Shutdown(bool forceAbort, int millisecondsTimeout) | 984 | bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); |
985 | { | 985 | bool timeout = false; |
986 | ValidateNotDisposed(); | 986 | |
987 | 987 | // Each iteration we update the time left for the timeout. | |
988 | ISTPInstancePerformanceCounters pcs = _pcs; | 988 | foreach(Thread thread in threads) |
989 | 989 | { | |
990 | if (NullSTPInstancePerformanceCounters.Instance != _pcs) | 990 | // Join don't work with negative numbers |
991 | { | 991 | if (!waitInfinitely && (millisecondsLeft < 0)) |
992 | _pcs.Dispose(); | 992 | { |
993 | // Set the _pcs to "null" to stop updating the performance | 993 | timeout = true; |
994 | // counters | 994 | break; |
995 | _pcs = NullSTPInstancePerformanceCounters.Instance; | 995 | } |
996 | } | 996 | |
997 | 997 | // Wait for the thread to terminate | |
998 | Thread [] threads = null; | 998 | bool success = thread.Join(millisecondsLeft); |
999 | lock(_workerThreads.SyncRoot) | 999 | if(!success) |
1000 | { | 1000 | { |
1001 | // Shutdown the work items queue | 1001 | timeout = true; |
1002 | _workItemsQueue.Dispose(); | 1002 | break; |
1003 | 1003 | } | |
1004 | // Signal the threads to exit | 1004 | |
1005 | _shutdown = true; | 1005 | if(!waitInfinitely) |
1006 | _shuttingDownEvent.Set(); | 1006 | { |
1007 | 1007 | // Update the time left to wait | |
1008 | // Make a copy of the threads' references in the pool | 1008 | //TimeSpan ts = DateTime.UtcNow - start; |
1009 | threads = new Thread [_workerThreads.Count]; | 1009 | millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; |
1010 | _workerThreads.Keys.CopyTo(threads, 0); | 1010 | } |
1011 | } | 1011 | } |
1012 | 1012 | ||
1013 | int millisecondsLeft = millisecondsTimeout; | 1013 | if (timeout && forceAbort) |
1014 | DateTime start = DateTime.Now; | 1014 | { |
1015 | bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); | 1015 | // Abort the threads in the pool |
1016 | bool timeout = false; | 1016 | foreach(Thread thread in threads) |
1017 | 1017 | { | |
1018 | // Each iteration we update the time left for the timeout. | 1018 | |
1019 | foreach(Thread thread in threads) | 1019 | if ((thread != null) |
1020 | { | 1020 | #if !(_WINDOWS_CE) |
1021 | // Join don't work with negative numbers | 1021 | && thread.IsAlive |
1022 | if (!waitInfinitely && (millisecondsLeft < 0)) | 1022 | #endif |
1023 | { | 1023 | ) |
1024 | timeout = true; | 1024 | { |
1025 | break; | 1025 | try |
1026 | } | 1026 | { |
1027 | 1027 | thread.Abort(); // Shutdown | |
1028 | // Wait for the thread to terminate | 1028 | } |
1029 | bool success = thread.Join(millisecondsLeft); | 1029 | catch(SecurityException e) |
1030 | if(!success) | 1030 | { |
1031 | { | 1031 | e.GetHashCode(); |
1032 | timeout = true; | 1032 | } |
1033 | break; | 1033 | catch(ThreadStateException ex) |
1034 | } | 1034 | { |
1035 | 1035 | ex.GetHashCode(); | |
1036 | if(!waitInfinitely) | 1036 | // In case the thread has been terminated |
1037 | { | 1037 | // after the check if it is alive. |
1038 | // Update the time left to wait | 1038 | } |
1039 | TimeSpan ts = DateTime.Now - start; | 1039 | } |
1040 | millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds; | 1040 | } |
1041 | } | 1041 | } |
1042 | } | 1042 | } |
1043 | 1043 | ||
1044 | if (timeout && forceAbort) | 1044 | /// <summary> |
1045 | { | 1045 | /// Wait for all work items to complete |
1046 | // Abort the threads in the pool | 1046 | /// </summary> |
1047 | foreach(Thread thread in threads) | 1047 | /// <param name="waitableResults">Array of work item result objects</param> |
1048 | { | 1048 | /// <returns> |
1049 | if ((thread != null) && thread.IsAlive) | 1049 | /// true when every work item in workItemResults has completed; otherwise false. |
1050 | { | 1050 | /// </returns> |
1051 | try | 1051 | public static bool WaitAll( |
1052 | { | 1052 | IWaitableResult [] waitableResults) |
1053 | thread.Abort("Shutdown"); | 1053 | { |
1054 | } | 1054 | return WaitAll(waitableResults, Timeout.Infinite, true); |
1055 | catch(SecurityException e) | 1055 | } |
1056 | { | 1056 | |
1057 | e.GetHashCode(); | 1057 | /// <summary> |
1058 | } | 1058 | /// Wait for all work items to complete |
1059 | catch(ThreadStateException ex) | 1059 | /// </summary> |
1060 | { | 1060 | /// <param name="waitableResults">Array of work item result objects</param> |
1061 | ex.GetHashCode(); | 1061 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1062 | // In case the thread has been terminated | 1062 | /// <param name="exitContext"> |
1063 | // after the check if it is alive. | 1063 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1064 | } | 1064 | /// </param> |
1065 | } | 1065 | /// <returns> |
1066 | } | 1066 | /// true when every work item in workItemResults has completed; otherwise false. |
1067 | } | 1067 | /// </returns> |
1068 | 1068 | public static bool WaitAll( | |
1069 | // Dispose of the performance counters | 1069 | IWaitableResult [] waitableResults, |
1070 | pcs.Dispose(); | 1070 | TimeSpan timeout, |
1071 | } | 1071 | bool exitContext) |
1072 | 1072 | { | |
1073 | /// <summary> | 1073 | return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext); |
1074 | /// Wait for all work items to complete | 1074 | } |
1075 | /// </summary> | 1075 | |
1076 | /// <param name="workItemResults">Array of work item result objects</param> | 1076 | /// <summary> |
1077 | /// <returns> | 1077 | /// Wait for all work items to complete |
1078 | /// true when every work item in workItemResults has completed; otherwise false. | 1078 | /// </summary> |
1079 | /// </returns> | 1079 | /// <param name="waitableResults">Array of work item result objects</param> |
1080 | public static bool WaitAll( | 1080 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1081 | IWorkItemResult [] workItemResults) | 1081 | /// <param name="exitContext"> |
1082 | { | 1082 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1083 | return WaitAll(workItemResults, Timeout.Infinite, true); | 1083 | /// </param> |
1084 | } | 1084 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1085 | 1085 | /// <returns> | |
1086 | /// <summary> | 1086 | /// true when every work item in workItemResults has completed; otherwise false. |
1087 | /// Wait for all work items to complete | 1087 | /// </returns> |
1088 | /// </summary> | 1088 | public static bool WaitAll( |
1089 | /// <param name="workItemResults">Array of work item result objects</param> | 1089 | IWaitableResult[] waitableResults, |
1090 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1090 | TimeSpan timeout, |
1091 | /// <param name="exitContext"> | 1091 | bool exitContext, |
1092 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1092 | WaitHandle cancelWaitHandle) |
1093 | /// </param> | 1093 | { |
1094 | /// <returns> | 1094 | return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); |
1095 | /// true when every work item in workItemResults has completed; otherwise false. | 1095 | } |
1096 | /// </returns> | 1096 | |
1097 | public static bool WaitAll( | 1097 | /// <summary> |
1098 | IWorkItemResult [] workItemResults, | 1098 | /// Wait for all work items to complete |
1099 | TimeSpan timeout, | 1099 | /// </summary> |
1100 | bool exitContext) | 1100 | /// <param name="waitableResults">Array of work item result objects</param> |
1101 | { | 1101 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1102 | return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext); | 1102 | /// <param name="exitContext"> |
1103 | } | 1103 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1104 | 1104 | /// </param> | |
1105 | /// <summary> | 1105 | /// <returns> |
1106 | /// Wait for all work items to complete | 1106 | /// true when every work item in workItemResults has completed; otherwise false. |
1107 | /// </summary> | 1107 | /// </returns> |
1108 | /// <param name="workItemResults">Array of work item result objects</param> | 1108 | public static bool WaitAll( |
1109 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1109 | IWaitableResult [] waitableResults, |
1110 | /// <param name="exitContext"> | 1110 | int millisecondsTimeout, |
1111 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1111 | bool exitContext) |
1112 | /// </param> | 1112 | { |
1113 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1113 | return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null); |
1114 | /// <returns> | 1114 | } |
1115 | /// true when every work item in workItemResults has completed; otherwise false. | 1115 | |
1116 | /// </returns> | 1116 | /// <summary> |
1117 | public static bool WaitAll( | 1117 | /// Wait for all work items to complete |
1118 | IWorkItemResult [] workItemResults, | 1118 | /// </summary> |
1119 | TimeSpan timeout, | 1119 | /// <param name="waitableResults">Array of work item result objects</param> |
1120 | bool exitContext, | 1120 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1121 | WaitHandle cancelWaitHandle) | 1121 | /// <param name="exitContext"> |
1122 | { | 1122 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1123 | return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); | 1123 | /// </param> |
1124 | } | 1124 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1125 | 1125 | /// <returns> | |
1126 | /// <summary> | 1126 | /// true when every work item in workItemResults has completed; otherwise false. |
1127 | /// Wait for all work items to complete | 1127 | /// </returns> |
1128 | /// </summary> | 1128 | public static bool WaitAll( |
1129 | /// <param name="workItemResults">Array of work item result objects</param> | 1129 | IWaitableResult[] waitableResults, |
1130 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1130 | int millisecondsTimeout, |
1131 | /// <param name="exitContext"> | 1131 | bool exitContext, |
1132 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1132 | WaitHandle cancelWaitHandle) |
1133 | /// </param> | 1133 | { |
1134 | /// <returns> | 1134 | return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); |
1135 | /// true when every work item in workItemResults has completed; otherwise false. | 1135 | } |
1136 | /// </returns> | 1136 | |
1137 | public static bool WaitAll( | 1137 | |
1138 | IWorkItemResult [] workItemResults, | 1138 | /// <summary> |
1139 | int millisecondsTimeout, | 1139 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1140 | bool exitContext) | 1140 | /// </summary> |
1141 | { | 1141 | /// <param name="waitableResults">Array of work item result objects</param> |
1142 | return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null); | 1142 | /// <returns> |
1143 | } | 1143 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. |
1144 | 1144 | /// </returns> | |
1145 | /// <summary> | 1145 | public static int WaitAny( |
1146 | /// Wait for all work items to complete | 1146 | IWaitableResult [] waitableResults) |
1147 | /// </summary> | 1147 | { |
1148 | /// <param name="workItemResults">Array of work item result objects</param> | 1148 | return WaitAny(waitableResults, Timeout.Infinite, true); |
1149 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1149 | } |
1150 | /// <param name="exitContext"> | 1150 | |
1151 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1151 | /// <summary> |
1152 | /// </param> | 1152 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1153 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1153 | /// </summary> |
1154 | /// <returns> | 1154 | /// <param name="waitableResults">Array of work item result objects</param> |
1155 | /// true when every work item in workItemResults has completed; otherwise false. | 1155 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1156 | /// </returns> | 1156 | /// <param name="exitContext"> |
1157 | public static bool WaitAll( | 1157 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1158 | IWorkItemResult [] workItemResults, | 1158 | /// </param> |
1159 | int millisecondsTimeout, | 1159 | /// <returns> |
1160 | bool exitContext, | 1160 | /// 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. |
1161 | WaitHandle cancelWaitHandle) | 1161 | /// </returns> |
1162 | { | 1162 | public static int WaitAny( |
1163 | return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); | 1163 | IWaitableResult[] waitableResults, |
1164 | } | 1164 | TimeSpan timeout, |
1165 | 1165 | bool exitContext) | |
1166 | 1166 | { | |
1167 | /// <summary> | 1167 | return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext); |
1168 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1168 | } |
1169 | /// </summary> | 1169 | |
1170 | /// <param name="workItemResults">Array of work item result objects</param> | 1170 | /// <summary> |
1171 | /// <returns> | 1171 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1172 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. | 1172 | /// </summary> |
1173 | /// </returns> | 1173 | /// <param name="waitableResults">Array of work item result objects</param> |
1174 | public static int WaitAny( | 1174 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1175 | IWorkItemResult [] workItemResults) | 1175 | /// <param name="exitContext"> |
1176 | { | 1176 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1177 | return WaitAny(workItemResults, Timeout.Infinite, true); | 1177 | /// </param> |
1178 | } | 1178 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1179 | 1179 | /// <returns> | |
1180 | /// <summary> | 1180 | /// 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. |
1181 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1181 | /// </returns> |
1182 | /// </summary> | 1182 | public static int WaitAny( |
1183 | /// <param name="workItemResults">Array of work item result objects</param> | 1183 | IWaitableResult [] waitableResults, |
1184 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1184 | TimeSpan timeout, |
1185 | /// <param name="exitContext"> | 1185 | bool exitContext, |
1186 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1186 | WaitHandle cancelWaitHandle) |
1187 | /// </param> | 1187 | { |
1188 | /// <returns> | 1188 | return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); |
1189 | /// 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. | 1189 | } |
1190 | /// </returns> | 1190 | |
1191 | public static int WaitAny( | 1191 | /// <summary> |
1192 | IWorkItemResult [] workItemResults, | 1192 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1193 | TimeSpan timeout, | 1193 | /// </summary> |
1194 | bool exitContext) | 1194 | /// <param name="waitableResults">Array of work item result objects</param> |
1195 | { | 1195 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1196 | return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext); | 1196 | /// <param name="exitContext"> |
1197 | } | 1197 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1198 | 1198 | /// </param> | |
1199 | /// <summary> | 1199 | /// <returns> |
1200 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1200 | /// 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. |
1201 | /// </summary> | 1201 | /// </returns> |
1202 | /// <param name="workItemResults">Array of work item result objects</param> | 1202 | public static int WaitAny( |
1203 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1203 | IWaitableResult [] waitableResults, |
1204 | /// <param name="exitContext"> | 1204 | int millisecondsTimeout, |
1205 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1205 | bool exitContext) |
1206 | /// </param> | 1206 | { |
1207 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1207 | return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null); |
1208 | /// <returns> | 1208 | } |
1209 | /// 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. | 1209 | |
1210 | /// </returns> | 1210 | /// <summary> |
1211 | public static int WaitAny( | 1211 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1212 | IWorkItemResult [] workItemResults, | 1212 | /// </summary> |
1213 | TimeSpan timeout, | 1213 | /// <param name="waitableResults">Array of work item result objects</param> |
1214 | bool exitContext, | 1214 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1215 | WaitHandle cancelWaitHandle) | 1215 | /// <param name="exitContext"> |
1216 | { | 1216 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1217 | return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); | 1217 | /// </param> |
1218 | } | 1218 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1219 | 1219 | /// <returns> | |
1220 | /// <summary> | 1220 | /// 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. |
1221 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1221 | /// </returns> |
1222 | /// </summary> | 1222 | public static int WaitAny( |
1223 | /// <param name="workItemResults">Array of work item result objects</param> | 1223 | IWaitableResult [] waitableResults, |
1224 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1224 | int millisecondsTimeout, |
1225 | /// <param name="exitContext"> | 1225 | bool exitContext, |
1226 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1226 | WaitHandle cancelWaitHandle) |
1227 | /// </param> | 1227 | { |
1228 | /// <returns> | 1228 | return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); |
1229 | /// 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. | 1229 | } |
1230 | /// </returns> | 1230 | |
1231 | public static int WaitAny( | 1231 | /// <summary> |
1232 | IWorkItemResult [] workItemResults, | 1232 | /// Creates a new WorkItemsGroup. |
1233 | int millisecondsTimeout, | 1233 | /// </summary> |
1234 | bool exitContext) | 1234 | /// <param name="concurrency">The number of work items that can be run concurrently</param> |
1235 | { | 1235 | /// <returns>A reference to the WorkItemsGroup</returns> |
1236 | return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null); | 1236 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) |
1237 | } | 1237 | { |
1238 | 1238 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); | |
1239 | /// <summary> | 1239 | return workItemsGroup; |
1240 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1240 | } |
1241 | /// </summary> | 1241 | |
1242 | /// <param name="workItemResults">Array of work item result objects</param> | 1242 | /// <summary> |
1243 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1243 | /// Creates a new WorkItemsGroup. |
1244 | /// <param name="exitContext"> | 1244 | /// </summary> |
1245 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1245 | /// <param name="concurrency">The number of work items that can be run concurrently</param> |
1246 | /// </param> | 1246 | /// <param name="wigStartInfo">A WorkItemsGroup configuration that overrides the default behavior</param> |
1247 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1247 | /// <returns>A reference to the WorkItemsGroup</returns> |
1248 | /// <returns> | 1248 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) |
1249 | /// 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. | 1249 | { |
1250 | /// </returns> | 1250 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); |
1251 | public static int WaitAny( | 1251 | return workItemsGroup; |
1252 | IWorkItemResult [] workItemResults, | 1252 | } |
1253 | int millisecondsTimeout, | 1253 | |
1254 | bool exitContext, | 1254 | #region Fire Thread's Events |
1255 | WaitHandle cancelWaitHandle) | 1255 | |
1256 | { | 1256 | private void FireOnThreadInitialization() |
1257 | return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); | 1257 | { |
1258 | } | 1258 | if (null != _onThreadInitialization) |
1259 | 1259 | { | |
1260 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) | 1260 | foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList()) |
1261 | { | 1261 | { |
1262 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); | 1262 | try |
1263 | return workItemsGroup; | 1263 | { |
1264 | } | 1264 | tih(); |
1265 | 1265 | } | |
1266 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) | 1266 | catch (Exception e) |
1267 | { | 1267 | { |
1268 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); | 1268 | e.GetHashCode(); |
1269 | return workItemsGroup; | 1269 | Debug.Assert(false); |
1270 | } | 1270 | throw; |
1271 | 1271 | } | |
1272 | public event WorkItemsGroupIdleHandler OnIdle | 1272 | } |
1273 | { | 1273 | } |
1274 | add | 1274 | } |
1275 | { | 1275 | |
1276 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | 1276 | private void FireOnThreadTermination() |
1277 | //_onIdle += value; | 1277 | { |
1278 | } | 1278 | if (null != _onThreadTermination) |
1279 | remove | 1279 | { |
1280 | { | 1280 | foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList()) |
1281 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | 1281 | { |
1282 | //_onIdle -= value; | 1282 | try |
1283 | } | 1283 | { |
1284 | } | 1284 | tth(); |
1285 | 1285 | } | |
1286 | public void Cancel() | 1286 | catch (Exception e) |
1287 | { | 1287 | { |
1288 | ICollection workItemsGroups = _workItemsGroups.Values; | 1288 | e.GetHashCode(); |
1289 | foreach(WorkItemsGroup workItemsGroup in workItemsGroups) | 1289 | Debug.Assert(false); |
1290 | { | 1290 | throw; |
1291 | workItemsGroup.Cancel(); | 1291 | } |
1292 | } | 1292 | } |
1293 | } | 1293 | } |
1294 | 1294 | } | |
1295 | public void Start() | 1295 | |
1296 | { | 1296 | #endregion |
1297 | lock (this) | 1297 | |
1298 | { | 1298 | /// <summary> |
1299 | if (!this._stpStartInfo.StartSuspended) | 1299 | /// This event is fired when a thread is created. |
1300 | { | 1300 | /// Use it to initialize a thread before the work items use it. |
1301 | return; | 1301 | /// </summary> |
1302 | } | 1302 | public event ThreadInitializationHandler OnThreadInitialization |
1303 | _stpStartInfo.StartSuspended = false; | 1303 | { |
1304 | } | 1304 | add { _onThreadInitialization += value; } |
1305 | 1305 | remove { _onThreadInitialization -= value; } | |
1306 | ICollection workItemsGroups = _workItemsGroups.Values; | 1306 | } |
1307 | foreach(WorkItemsGroup workItemsGroup in workItemsGroups) | 1307 | |
1308 | { | 1308 | /// <summary> |
1309 | workItemsGroup.OnSTPIsStarting(); | 1309 | /// This event is fired when a thread is terminating. |
1310 | } | 1310 | /// Use it for cleanup. |
1311 | 1311 | /// </summary> | |
1312 | StartOptimalNumberOfThreads(); | 1312 | public event ThreadTerminationHandler OnThreadTermination |
1313 | } | 1313 | { |
1314 | 1314 | add { _onThreadTermination += value; } | |
1315 | #endregion | 1315 | remove { _onThreadTermination -= value; } |
1316 | 1316 | } | |
1317 | #region Properties | 1317 | |
1318 | 1318 | ||
1319 | /// <summary> | 1319 | internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig) |
1320 | /// Get/Set the name of the SmartThreadPool instance | 1320 | { |
1321 | /// </summary> | 1321 | foreach (ThreadEntry threadEntry in _workerThreads.Values) |
1322 | public string Name | 1322 | { |
1323 | { | 1323 | WorkItem workItem = threadEntry.CurrentWorkItem; |
1324 | get | 1324 | if (null != workItem && |
1325 | { | 1325 | workItem.WasQueuedBy(wig) && |
1326 | return _name; | 1326 | !workItem.IsCanceled) |
1327 | } | 1327 | { |
1328 | 1328 | threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); | |
1329 | set | 1329 | } |
1330 | { | 1330 | } |
1331 | _name = value; | 1331 | } |
1332 | } | 1332 | |
1333 | } | 1333 | |
1334 | 1334 | ||
1335 | /// <summary> | 1335 | #endregion |
1336 | /// Get the lower limit of threads in the pool. | 1336 | |
1337 | /// </summary> | 1337 | #region Properties |
1338 | public int MinThreads | 1338 | |
1339 | { | 1339 | /// <summary> |
1340 | get | 1340 | /// Get/Set the lower limit of threads in the pool. |
1341 | { | 1341 | /// </summary> |
1342 | ValidateNotDisposed(); | 1342 | public int MinThreads |
1343 | return _stpStartInfo.MinWorkerThreads; | 1343 | { |
1344 | } | 1344 | get |
1345 | } | 1345 | { |
1346 | 1346 | ValidateNotDisposed(); | |
1347 | /// <summary> | 1347 | return _stpStartInfo.MinWorkerThreads; |
1348 | /// Get the upper limit of threads in the pool. | 1348 | } |
1349 | /// </summary> | 1349 | set |
1350 | public int MaxThreads | 1350 | { |
1351 | { | 1351 | Debug.Assert(value >= 0); |
1352 | get | 1352 | Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads); |
1353 | { | 1353 | if (_stpStartInfo.MaxWorkerThreads < value) |
1354 | ValidateNotDisposed(); | 1354 | { |
1355 | return _stpStartInfo.MaxWorkerThreads; | 1355 | _stpStartInfo.MaxWorkerThreads = value; |
1356 | } | 1356 | } |
1357 | } | 1357 | _stpStartInfo.MinWorkerThreads = value; |
1358 | /// <summary> | 1358 | StartOptimalNumberOfThreads(); |
1359 | /// Get the number of threads in the thread pool. | 1359 | } |
1360 | /// Should be between the lower and the upper limits. | 1360 | } |
1361 | /// </summary> | 1361 | |
1362 | public int ActiveThreads | 1362 | /// <summary> |
1363 | { | 1363 | /// Get/Set the upper limit of threads in the pool. |
1364 | get | 1364 | /// </summary> |
1365 | { | 1365 | public int MaxThreads |
1366 | ValidateNotDisposed(); | 1366 | { |
1367 | return _workerThreads.Count; | 1367 | get |
1368 | } | 1368 | { |
1369 | } | 1369 | ValidateNotDisposed(); |
1370 | 1370 | return _stpStartInfo.MaxWorkerThreads; | |
1371 | /// <summary> | 1371 | } |
1372 | /// Get the number of busy (not idle) threads in the thread pool. | 1372 | |
1373 | /// </summary> | 1373 | set |
1374 | public int InUseThreads | 1374 | { |
1375 | { | 1375 | Debug.Assert(value > 0); |
1376 | get | 1376 | Debug.Assert(value >= _stpStartInfo.MinWorkerThreads); |
1377 | { | 1377 | if (_stpStartInfo.MinWorkerThreads > value) |
1378 | ValidateNotDisposed(); | 1378 | { |
1379 | return _inUseWorkerThreads; | 1379 | _stpStartInfo.MinWorkerThreads = value; |
1380 | } | 1380 | } |
1381 | } | 1381 | _stpStartInfo.MaxWorkerThreads = value; |
1382 | 1382 | StartOptimalNumberOfThreads(); | |
1383 | /// <summary> | 1383 | } |
1384 | /// Get the number of work items in the queue. | 1384 | } |
1385 | /// </summary> | 1385 | /// <summary> |
1386 | public int WaitingCallbacks | 1386 | /// Get the number of threads in the thread pool. |
1387 | { | 1387 | /// Should be between the lower and the upper limits. |
1388 | get | 1388 | /// </summary> |
1389 | { | 1389 | public int ActiveThreads |
1390 | ValidateNotDisposed(); | 1390 | { |
1391 | return _workItemsQueue.Count; | 1391 | get |
1392 | } | 1392 | { |
1393 | } | 1393 | ValidateNotDisposed(); |
1394 | 1394 | return _workerThreads.Count; | |
1395 | 1395 | } | |
1396 | public event EventHandler Idle | 1396 | } |
1397 | { | 1397 | |
1398 | add | 1398 | /// <summary> |
1399 | { | 1399 | /// Get the number of busy (not idle) threads in the thread pool. |
1400 | _stpIdle += value; | 1400 | /// </summary> |
1401 | } | 1401 | public int InUseThreads |
1402 | 1402 | { | |
1403 | remove | 1403 | get |
1404 | { | 1404 | { |
1405 | _stpIdle -= value; | 1405 | ValidateNotDisposed(); |
1406 | } | 1406 | return _inUseWorkerThreads; |
1407 | } | 1407 | } |
1408 | 1408 | } | |
1409 | #endregion | 1409 | |
1410 | 1410 | /// <summary> | |
1411 | #region IDisposable Members | 1411 | /// Returns true if the current running work item has been cancelled. |
1412 | 1412 | /// Must be used within the work item's callback method. | |
1413 | // ~SmartThreadPool() | 1413 | /// The work item should sample this value in order to know if it |
1414 | // { | 1414 | /// needs to quit before its completion. |
1415 | // Dispose(); | 1415 | /// </summary> |
1416 | // } | 1416 | public static bool IsWorkItemCanceled |
1417 | 1417 | { | |
1418 | public void Dispose() | 1418 | get |
1419 | { | 1419 | { |
1420 | if (!_isDisposed) | 1420 | return CurrentThreadEntry.CurrentWorkItem.IsCanceled; |
1421 | { | 1421 | } |
1422 | if (!_shutdown) | 1422 | } |
1423 | { | 1423 | |
1424 | Shutdown(); | 1424 | /// <summary> |
1425 | } | 1425 | /// Checks if the work item has been cancelled, and if yes then abort the thread. |
1426 | 1426 | /// Can be used with Cancel and timeout | |
1427 | if (null != _shuttingDownEvent) | 1427 | /// </summary> |
1428 | { | 1428 | public static void AbortOnWorkItemCancel() |
1429 | _shuttingDownEvent.Close(); | 1429 | { |
1430 | _shuttingDownEvent = null; | 1430 | if (IsWorkItemCanceled) |
1431 | } | 1431 | { |
1432 | _workerThreads.Clear(); | 1432 | Thread.CurrentThread.Abort(); |
1433 | _isDisposed = true; | 1433 | } |
1434 | GC.SuppressFinalize(this); | 1434 | } |
1435 | } | 1435 | |
1436 | } | 1436 | /// <summary> |
1437 | 1437 | /// Thread Pool start information (readonly) | |
1438 | private void ValidateNotDisposed() | 1438 | /// </summary> |
1439 | { | 1439 | public STPStartInfo STPStartInfo |
1440 | if(_isDisposed) | 1440 | { |
1441 | { | 1441 | get |
1442 | throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); | 1442 | { |
1443 | } | 1443 | return _stpStartInfo.AsReadOnly(); |
1444 | } | 1444 | } |
1445 | #endregion | 1445 | } |
1446 | } | 1446 | |
1447 | #endregion | 1447 | public bool IsShuttingdown |
1448 | } | 1448 | { |
1449 | get { return _shutdown; } | ||
1450 | } | ||
1451 | |||
1452 | /// <summary> | ||
1453 | /// Return the local calculated performance counters | ||
1454 | /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true. | ||
1455 | /// </summary> | ||
1456 | public ISTPPerformanceCountersReader PerformanceCountersReader | ||
1457 | { | ||
1458 | get { return (ISTPPerformanceCountersReader)_localPCs; } | ||
1459 | } | ||
1460 | |||
1461 | #endregion | ||
1462 | |||
1463 | #region IDisposable Members | ||
1464 | |||
1465 | public void Dispose() | ||
1466 | { | ||
1467 | if (!_isDisposed) | ||
1468 | { | ||
1469 | if (!_shutdown) | ||
1470 | { | ||
1471 | Shutdown(); | ||
1472 | } | ||
1473 | |||
1474 | if (null != _shuttingDownEvent) | ||
1475 | { | ||
1476 | _shuttingDownEvent.Close(); | ||
1477 | _shuttingDownEvent = null; | ||
1478 | } | ||
1479 | _workerThreads.Clear(); | ||
1480 | |||
1481 | if (null != _isIdleWaitHandle) | ||
1482 | { | ||
1483 | _isIdleWaitHandle.Close(); | ||
1484 | _isIdleWaitHandle = null; | ||
1485 | } | ||
1486 | |||
1487 | _isDisposed = true; | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | private void ValidateNotDisposed() | ||
1492 | { | ||
1493 | if(_isDisposed) | ||
1494 | { | ||
1495 | throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); | ||
1496 | } | ||
1497 | } | ||
1498 | #endregion | ||
1499 | |||
1500 | #region WorkItemsGroupBase Overrides | ||
1501 | |||
1502 | /// <summary> | ||
1503 | /// Get/Set the maximum number of work items that execute cocurrency on the thread pool | ||
1504 | /// </summary> | ||
1505 | public override int Concurrency | ||
1506 | { | ||
1507 | get { return MaxThreads; } | ||
1508 | set { MaxThreads = value; } | ||
1509 | } | ||
1510 | |||
1511 | /// <summary> | ||
1512 | /// Get the number of work items in the queue. | ||
1513 | /// </summary> | ||
1514 | public override int WaitingCallbacks | ||
1515 | { | ||
1516 | get | ||
1517 | { | ||
1518 | ValidateNotDisposed(); | ||
1519 | return _workItemsQueue.Count; | ||
1520 | } | ||
1521 | } | ||
1522 | |||
1523 | /// <summary> | ||
1524 | /// Get an array with all the state objects of the currently running items. | ||
1525 | /// The array represents a snap shot and impact performance. | ||
1526 | /// </summary> | ||
1527 | public override object[] GetStates() | ||
1528 | { | ||
1529 | object[] states = _workItemsQueue.GetStates(); | ||
1530 | return states; | ||
1531 | } | ||
1532 | |||
1533 | /// <summary> | ||
1534 | /// WorkItemsGroup start information (readonly) | ||
1535 | /// </summary> | ||
1536 | public override WIGStartInfo WIGStartInfo | ||
1537 | { | ||
1538 | get { return _stpStartInfo.AsReadOnly(); } | ||
1539 | } | ||
1540 | |||
1541 | /// <summary> | ||
1542 | /// Start the thread pool if it was started suspended. | ||
1543 | /// If it is already running, this method is ignored. | ||
1544 | /// </summary> | ||
1545 | public override void Start() | ||
1546 | { | ||
1547 | if (!_isSuspended) | ||
1548 | { | ||
1549 | return; | ||
1550 | } | ||
1551 | _isSuspended = false; | ||
1552 | |||
1553 | ICollection workItemsGroups = _workItemsGroups.Values; | ||
1554 | foreach (WorkItemsGroup workItemsGroup in workItemsGroups) | ||
1555 | { | ||
1556 | workItemsGroup.OnSTPIsStarting(); | ||
1557 | } | ||
1558 | |||
1559 | StartOptimalNumberOfThreads(); | ||
1560 | } | ||
1561 | |||
1562 | /// <summary> | ||
1563 | /// Cancel all work items using thread abortion | ||
1564 | /// </summary> | ||
1565 | /// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param> | ||
1566 | public override void Cancel(bool abortExecution) | ||
1567 | { | ||
1568 | _canceledSmartThreadPool.IsCanceled = true; | ||
1569 | _canceledSmartThreadPool = new CanceledWorkItemsGroup(); | ||
1570 | |||
1571 | ICollection workItemsGroups = _workItemsGroups.Values; | ||
1572 | foreach (WorkItemsGroup workItemsGroup in workItemsGroups) | ||
1573 | { | ||
1574 | workItemsGroup.Cancel(abortExecution); | ||
1575 | } | ||
1576 | |||
1577 | if (abortExecution) | ||
1578 | { | ||
1579 | foreach (ThreadEntry threadEntry in _workerThreads.Values) | ||
1580 | { | ||
1581 | WorkItem workItem = threadEntry.CurrentWorkItem; | ||
1582 | if (null != workItem && | ||
1583 | threadEntry.AssociatedSmartThreadPool == this && | ||
1584 | !workItem.IsCanceled) | ||
1585 | { | ||
1586 | threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); | ||
1587 | } | ||
1588 | } | ||
1589 | } | ||
1590 | } | ||
1591 | |||
1592 | /// <summary> | ||
1593 | /// Wait for the thread pool to be idle | ||
1594 | /// </summary> | ||
1595 | public override bool WaitForIdle(int millisecondsTimeout) | ||
1596 | { | ||
1597 | ValidateWaitForIdle(); | ||
1598 | return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); | ||
1599 | } | ||
1600 | |||
1601 | /// <summary> | ||
1602 | /// This event is fired when all work items are completed. | ||
1603 | /// (When IsIdle changes to true) | ||
1604 | /// This event only work on WorkItemsGroup. On SmartThreadPool | ||
1605 | /// it throws the NotImplementedException. | ||
1606 | /// </summary> | ||
1607 | public override event WorkItemsGroupIdleHandler OnIdle | ||
1608 | { | ||
1609 | add | ||
1610 | { | ||
1611 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | ||
1612 | //_onIdle += value; | ||
1613 | } | ||
1614 | remove | ||
1615 | { | ||
1616 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | ||
1617 | //_onIdle -= value; | ||
1618 | } | ||
1619 | } | ||
1620 | |||
1621 | internal override void PreQueueWorkItem() | ||
1622 | { | ||
1623 | ValidateNotDisposed(); | ||
1624 | } | ||
1625 | |||
1626 | #endregion | ||
1627 | |||
1628 | #region Join, Choice, Pipe, etc. | ||
1629 | |||
1630 | /// <summary> | ||
1631 | /// Executes all actions in parallel. | ||
1632 | /// Returns when they all finish. | ||
1633 | /// </summary> | ||
1634 | /// <param name="actions">Actions to execute</param> | ||
1635 | public void Join(IEnumerable<Action> actions) | ||
1636 | { | ||
1637 | WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; | ||
1638 | IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); | ||
1639 | foreach (Action action in actions) | ||
1640 | { | ||
1641 | workItemsGroup.QueueWorkItem(action); | ||
1642 | } | ||
1643 | workItemsGroup.Start(); | ||
1644 | workItemsGroup.WaitForIdle(); | ||
1645 | } | ||
1646 | |||
1647 | /// <summary> | ||
1648 | /// Executes all actions in parallel. | ||
1649 | /// Returns when they all finish. | ||
1650 | /// </summary> | ||
1651 | /// <param name="actions">Actions to execute</param> | ||
1652 | public void Join(params Action[] actions) | ||
1653 | { | ||
1654 | Join((IEnumerable<Action>)actions); | ||
1655 | } | ||
1656 | |||
1657 | private class ChoiceIndex | ||
1658 | { | ||
1659 | public int _index = -1; | ||
1660 | } | ||
1661 | |||
1662 | /// <summary> | ||
1663 | /// Executes all actions in parallel | ||
1664 | /// Returns when the first one completes | ||
1665 | /// </summary> | ||
1666 | /// <param name="actions">Actions to execute</param> | ||
1667 | public int Choice(IEnumerable<Action> actions) | ||
1668 | { | ||
1669 | WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; | ||
1670 | IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); | ||
1671 | |||
1672 | ManualResetEvent anActionCompleted = new ManualResetEvent(false); | ||
1673 | |||
1674 | ChoiceIndex choiceIndex = new ChoiceIndex(); | ||
1675 | |||
1676 | int i = 0; | ||
1677 | foreach (Action action in actions) | ||
1678 | { | ||
1679 | Action act = action; | ||
1680 | int value = i; | ||
1681 | workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); }); | ||
1682 | ++i; | ||
1683 | } | ||
1684 | workItemsGroup.Start(); | ||
1685 | anActionCompleted.WaitOne(); | ||
1686 | |||
1687 | return choiceIndex._index; | ||
1688 | } | ||
1689 | |||
1690 | /// <summary> | ||
1691 | /// Executes all actions in parallel | ||
1692 | /// Returns when the first one completes | ||
1693 | /// </summary> | ||
1694 | /// <param name="actions">Actions to execute</param> | ||
1695 | public int Choice(params Action[] actions) | ||
1696 | { | ||
1697 | return Choice((IEnumerable<Action>)actions); | ||
1698 | } | ||
1699 | |||
1700 | /// <summary> | ||
1701 | /// Executes actions in sequence asynchronously. | ||
1702 | /// Returns immediately. | ||
1703 | /// </summary> | ||
1704 | /// <param name="pipeState">A state context that passes </param> | ||
1705 | /// <param name="actions">Actions to execute in the order they should run</param> | ||
1706 | public void Pipe<T>(T pipeState, IEnumerable<Action<T>> actions) | ||
1707 | { | ||
1708 | WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; | ||
1709 | IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo); | ||
1710 | foreach (Action<T> action in actions) | ||
1711 | { | ||
1712 | Action<T> act = action; | ||
1713 | workItemsGroup.QueueWorkItem(() => act(pipeState)); | ||
1714 | } | ||
1715 | workItemsGroup.Start(); | ||
1716 | workItemsGroup.WaitForIdle(); | ||
1717 | } | ||
1718 | |||
1719 | /// <summary> | ||
1720 | /// Executes actions in sequence asynchronously. | ||
1721 | /// Returns immediately. | ||
1722 | /// </summary> | ||
1723 | /// <param name="pipeState"></param> | ||
1724 | /// <param name="actions">Actions to execute in the order they should run</param> | ||
1725 | public void Pipe<T>(T pipeState, params Action<T>[] actions) | ||
1726 | { | ||
1727 | Pipe(pipeState, (IEnumerable<Action<T>>)actions); | ||
1728 | } | ||
1729 | #endregion | ||
1730 | } | ||
1731 | #endregion | ||
1732 | } | ||