aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ThirdParty/SmartThreadPool/SmartThreadPool.cs
diff options
context:
space:
mode:
authorTeravus Ovares2008-05-30 12:27:06 +0000
committerTeravus Ovares2008-05-30 12:27:06 +0000
commit1a47ff8094ee414a47aebd310826906d89428a09 (patch)
tree0e90b3a33f43ff8617a077bb57b86d6b28e63e71 /ThirdParty/SmartThreadPool/SmartThreadPool.cs
parent* Fixed a dangling event hook that I added. (diff)
downloadopensim-SC-1a47ff8094ee414a47aebd310826906d89428a09.zip
opensim-SC-1a47ff8094ee414a47aebd310826906d89428a09.tar.gz
opensim-SC-1a47ff8094ee414a47aebd310826906d89428a09.tar.bz2
opensim-SC-1a47ff8094ee414a47aebd310826906d89428a09.tar.xz
* This is Melanie's XEngine script engine. I've not tested this real well, however, it's confirmed to compile and OpenSimulator to run successfully without this script engine active.
Diffstat (limited to 'ThirdParty/SmartThreadPool/SmartThreadPool.cs')
-rw-r--r--ThirdParty/SmartThreadPool/SmartThreadPool.cs1438
1 files changed, 1438 insertions, 0 deletions
diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs
new file mode 100644
index 0000000..c21984e
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs
@@ -0,0 +1,1438 @@
1// Ami Bar
2// amibar@gmail.com
3//
4// Smart thread pool in C#.
5// 7 Aug 2004 - Initial release
6// 14 Sep 2004 - Bug fixes
7// 15 Oct 2004 - Added new features
8// - Work items return result.
9// - Support waiting synchronization for multiple work items.
10// - Work items can be cancelled.
11// - Passage of the caller thread’s context to the thread in the pool.
12// - Minimal usage of WIN32 handles.
13// - Minor bug fixes.
14// 26 Dec 2004 - Changes:
15// - Removed static constructors.
16// - Added finalizers.
17// - Changed Exceptions so they are serializable.
18// - Fixed the bug in one of the SmartThreadPool constructors.
19// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters.
20// The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
21// - Added PostExecute with options on which cases to call it.
22// - Added option to dispose of the state objects.
23// - Added a WaitForIdle() method that waits until the work items queue is empty.
24// - Added an STPStartInfo class for the initialization of the thread pool.
25// - Changed exception handling so if a work item throws an exception it
26// is rethrown at GetResult(), rather then firing an UnhandledException event.
27// Note that PostExecute exception are always ignored.
28// 25 Mar 2005 - Changes:
29// - Fixed lost of work items bug
30// 3 Jul 2005: Changes.
31// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed.
32// 16 Aug 2005: Changes.
33// - Fixed bug where the InUseThreads becomes negative when canceling work items.
34//
35// 31 Jan 2006 - Changes:
36// - Added work items priority
37// - Removed support of chained delegates in callbacks and post executes (nobody really use this)
38// - Added work items groups
39// - Added work items groups idle event
40// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array
41// it returns true rather then throwing an exception.
42// - Added option to start the STP and the WIG as suspended
43// - Exception behavior changed, the real exception is returned by an
44// inner exception
45// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.)
46// - Added performance counters
47// - Added priority to the threads in the pool
48//
49// 13 Feb 2006 - Changes:
50// - Added a call to the dispose of the Performance Counter so
51// their won't be a Performance Counter leak.
52// - Added exception catch in case the Performance Counters cannot
53// be created.
54
55using System;
56using System.Security;
57using System.Threading;
58using System.Collections;
59using System.Diagnostics;
60using System.Runtime.CompilerServices;
61
62using Amib.Threading.Internal;
63
64namespace Amib.Threading
65{
66 #region SmartThreadPool class
67 /// <summary>
68 /// Smart thread pool class.
69 /// </summary>
70 public class SmartThreadPool : IWorkItemsGroup, IDisposable
71 {
72 #region Default Constants
73
74 /// <summary>
75 /// Default minimum number of threads the thread pool contains. (0)
76 /// </summary>
77 public const int DefaultMinWorkerThreads = 0;
78
79 /// <summary>
80 /// Default maximum number of threads the thread pool contains. (25)
81 /// </summary>
82 public const int DefaultMaxWorkerThreads = 25;
83
84 /// <summary>
85 /// Default idle timeout in milliseconds. (One minute)
86 /// </summary>
87 public const int DefaultIdleTimeout = 60*1000; // One minute
88
89 /// <summary>
90 /// Indicate to copy the security context of the caller and then use it in the call. (false)
91 /// </summary>
92 public const bool DefaultUseCallerCallContext = false;
93
94 /// <summary>
95 /// Indicate to copy the HTTP context of the caller and then use it in the call. (false)
96 /// </summary>
97 public const bool DefaultUseCallerHttpContext = false;
98
99 /// <summary>
100 /// Indicate to dispose of the state objects if they support the IDispose interface. (false)
101 /// </summary>
102 public const bool DefaultDisposeOfStateObjects = false;
103
104 /// <summary>
105 /// The default option to run the post execute
106 /// </summary>
107 public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always;
108
109 /// <summary>
110 /// The default post execute method to run.
111 /// When null it means not to call it.
112 /// </summary>
113 public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null;
114
115 /// <summary>
116 /// The default work item priority
117 /// </summary>
118 public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;
119
120 /// <summary>
121 /// The default is to work on work items as soon as they arrive
122 /// and not to wait for the start.
123 /// </summary>
124 public const bool DefaultStartSuspended = false;
125
126 /// <summary>
127 /// The default is not to use the performance counters
128 /// </summary>
129 public static readonly string DefaultPerformanceCounterInstanceName = null;
130
131 public static readonly int DefaultStackSize = 0;
132
133 /// <summary>
134 /// The default thread priority
135 /// </summary>
136 public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
137
138 #endregion
139
140 #region Member Variables
141
142 /// <summary>
143 /// Contains the name of this instance of SmartThreadPool.
144 /// Can be changed by the user.
145 /// </summary>
146 private string _name = "SmartThreadPool";
147
148 /// <summary>
149 /// Hashtable of all the threads in the thread pool.
150 /// </summary>
151 private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable());
152
153 /// <summary>
154 /// Queue of work items.
155 /// </summary>
156 private WorkItemsQueue _workItemsQueue = new WorkItemsQueue();
157
158 /// <summary>
159 /// Count the work items handled.
160 /// Used by the performance counter.
161 /// </summary>
162 private long _workItemsProcessed = 0;
163
164 /// <summary>
165 /// Number of threads that currently work (not idle).
166 /// </summary>
167 private int _inUseWorkerThreads = 0;
168
169 /// <summary>
170 /// Start information to use.
171 /// It is simpler than providing many constructors.
172 /// </summary>
173 private STPStartInfo _stpStartInfo = new STPStartInfo();
174
175 /// <summary>
176 /// Total number of work items that are stored in the work items queue
177 /// plus the work items that the threads in the pool are working on.
178 /// </summary>
179 private int _currentWorkItemsCount = 0;
180
181 /// <summary>
182 /// Signaled when the thread pool is idle, i.e. no thread is busy
183 /// and the work items queue is empty
184 /// </summary>
185 private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
186
187 /// <summary>
188 /// An event to signal all the threads to quit immediately.
189 /// </summary>
190 private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
191
192 /// <summary>
193 /// A flag to indicate the threads to quit.
194 /// </summary>
195 private bool _shutdown = false;
196
197 /// <summary>
198 /// Counts the threads created in the pool.
199 /// It is used to name the threads.
200 /// </summary>
201 private int _threadCounter = 0;
202
203 /// <summary>
204 /// Indicate that the SmartThreadPool has been disposed
205 /// </summary>
206 private bool _isDisposed = false;
207
208 /// <summary>
209 /// Event to send that the thread pool is idle
210 /// </summary>
211 private event EventHandler _stpIdle;
212
213 /// <summary>
214 /// On idle event
215 /// </summary>
216 //private event WorkItemsGroupIdleHandler _onIdle;
217
218 /// <summary>
219 /// Holds all the WorkItemsGroup instaces that have at least one
220 /// work item int the SmartThreadPool
221 /// This variable is used in case of Shutdown
222 /// </summary>
223 private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable());
224
225 /// <summary>
226 /// A reference from each thread in the thread pool to its SmartThreadPool
227 /// object container.
228 /// With this variable a thread can know whatever it belongs to a
229 /// SmartThreadPool.
230 /// </summary>
231 [ThreadStatic]
232 private static SmartThreadPool _smartThreadPool;
233
234 /// <summary>
235 /// A reference to the current work item a thread from the thread pool
236 /// is executing.
237 /// </summary>
238 [ThreadStatic]
239 private static WorkItem _currentWorkItem;
240
241 /// <summary>
242 /// STP performance counters
243 /// </summary>
244 private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance;
245
246 #endregion
247
248 #region Construction and Finalization
249
250 /// <summary>
251 /// Constructor
252 /// </summary>
253 public SmartThreadPool()
254 {
255 Initialize();
256 }
257
258 /// <summary>
259 /// Constructor
260 /// </summary>
261 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
262 public SmartThreadPool(int idleTimeout)
263 {
264 _stpStartInfo.IdleTimeout = idleTimeout;
265 Initialize();
266 }
267
268 /// <summary>
269 /// Constructor
270 /// </summary>
271 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
272 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
273 public SmartThreadPool(
274 int idleTimeout,
275 int maxWorkerThreads)
276 {
277 _stpStartInfo.IdleTimeout = idleTimeout;
278 _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
279 Initialize();
280 }
281
282 /// <summary>
283 /// Constructor
284 /// </summary>
285 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
286 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
287 /// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
288 public SmartThreadPool(
289 int idleTimeout,
290 int maxWorkerThreads,
291 int minWorkerThreads)
292 {
293 _stpStartInfo.IdleTimeout = idleTimeout;
294 _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
295 _stpStartInfo.MinWorkerThreads = minWorkerThreads;
296 Initialize();
297 }
298
299 /// <summary>
300 /// Constructor
301 /// </summary>
302 public SmartThreadPool(STPStartInfo stpStartInfo)
303 {
304 _stpStartInfo = new STPStartInfo(stpStartInfo);
305 Initialize();
306 }
307
308 private void Initialize()
309 {
310 ValidateSTPStartInfo();
311
312 if (null != _stpStartInfo.PerformanceCounterInstanceName)
313 {
314 try
315 {
316 _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
317 }
318 catch(Exception e)
319 {
320 Debug.WriteLine("Unable to create Performance Counters: " + e.ToString());
321 _pcs = NullSTPInstancePerformanceCounters.Instance;
322 }
323 }
324
325 StartOptimalNumberOfThreads();
326 }
327
328 private void StartOptimalNumberOfThreads()
329 {
330 int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads);
331 threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads);
332 StartThreads(threadsCount);
333 }
334
335 private void ValidateSTPStartInfo()
336 {
337 if (_stpStartInfo.MinWorkerThreads < 0)
338 {
339 throw new ArgumentOutOfRangeException(
340 "MinWorkerThreads", "MinWorkerThreads cannot be negative");
341 }
342
343 if (_stpStartInfo.MaxWorkerThreads <= 0)
344 {
345 throw new ArgumentOutOfRangeException(
346 "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero");
347 }
348
349 if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads)
350 {
351 throw new ArgumentOutOfRangeException(
352 "MinWorkerThreads, maxWorkerThreads",
353 "MaxWorkerThreads must be greater or equal to MinWorkerThreads");
354 }
355 }
356
357 private void ValidateCallback(Delegate callback)
358 {
359 if(callback.GetInvocationList().Length > 1)
360 {
361 throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
362 }
363 }
364
365 #endregion
366
367 #region Thread Processing
368
369 /// <summary>
370 /// Waits on the queue for a work item, shutdown, or timeout.
371 /// </summary>
372 /// <returns>
373 /// Returns the WaitingCallback or null in case of timeout or shutdown.
374 /// </returns>
375 private WorkItem Dequeue()
376 {
377 WorkItem workItem =
378 _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent);
379
380 return workItem;
381 }
382
383 /// <summary>
384 /// Put a new work item in the queue
385 /// </summary>
386 /// <param name="workItem">A work item to queue</param>
387 private void Enqueue(WorkItem workItem)
388 {
389 Enqueue(workItem, true);
390 }
391
392 /// <summary>
393 /// Put a new work item in the queue
394 /// </summary>
395 /// <param name="workItem">A work item to queue</param>
396 internal void Enqueue(WorkItem workItem, bool incrementWorkItems)
397 {
398 // Make sure the workItem is not null
399 Debug.Assert(null != workItem);
400
401 if (incrementWorkItems)
402 {
403 IncrementWorkItemsCount();
404 }
405
406 _workItemsQueue.EnqueueWorkItem(workItem);
407 workItem.WorkItemIsQueued();
408
409 // If all the threads are busy then try to create a new one
410 if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count)
411 {
412 StartThreads(1);
413 }
414 }
415
416 private void IncrementWorkItemsCount()
417 {
418 _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
419
420 int count = Interlocked.Increment(ref _currentWorkItemsCount);
421 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
422 if (count == 1)
423 {
424 //Trace.WriteLine("STP is NOT idle");
425 _isIdleWaitHandle.Reset();
426 }
427 }
428
429 private void DecrementWorkItemsCount()
430 {
431 ++_workItemsProcessed;
432
433 // The counter counts even if the work item was cancelled
434 _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
435
436 int count = Interlocked.Decrement(ref _currentWorkItemsCount);
437 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
438 if (count == 0)
439 {
440 //Trace.WriteLine("STP is idle");
441 _isIdleWaitHandle.Set();
442 }
443 }
444
445 internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
446 {
447 _workItemsGroups[workItemsGroup] = workItemsGroup;
448 }
449
450 internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
451 {
452 if (_workItemsGroups.Contains(workItemsGroup))
453 {
454 _workItemsGroups.Remove(workItemsGroup);
455 }
456 }
457
458 /// <summary>
459 /// Inform that the current thread is about to quit or quiting.
460 /// The same thread may call this method more than once.
461 /// </summary>
462 private void InformCompleted()
463 {
464 // There is no need to lock the two methods together
465 // since only the current thread removes itself
466 // and the _workerThreads is a synchronized hashtable
467 if (_workerThreads.Contains(Thread.CurrentThread))
468 {
469 _workerThreads.Remove(Thread.CurrentThread);
470 _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
471 }
472 }
473
474 /// <summary>
475 /// Starts new threads
476 /// </summary>
477 /// <param name="threadsCount">The number of threads to start</param>
478 private void StartThreads(int threadsCount)
479 {
480 if (_stpStartInfo.StartSuspended)
481 {
482 return;
483 }
484
485 lock(_workerThreads.SyncRoot)
486 {
487 // Don't start threads on shut down
488 if (_shutdown)
489 {
490 return;
491 }
492
493 for(int i = 0; i < threadsCount; ++i)
494 {
495 // Don't create more threads then the upper limit
496 if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads)
497 {
498 return;
499 }
500
501 // Create a new thread
502 Thread workerThread = new Thread(new ThreadStart(ProcessQueuedItems), _stpStartInfo.StackSize);
503
504 // Configure the new thread and start it
505 workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
506 workerThread.IsBackground = true;
507 workerThread.Priority = _stpStartInfo.ThreadPriority;
508 workerThread.Start();
509 ++_threadCounter;
510
511 // Add the new thread to the hashtable and update its creation
512 // time.
513 _workerThreads[workerThread] = DateTime.Now;
514 _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
515 }
516 }
517 }
518
519 /// <summary>
520 /// A worker thread method that processes work items from the work items queue.
521 /// </summary>
522 private void ProcessQueuedItems()
523 {
524 // Initialize the _smartThreadPool variable
525 _smartThreadPool = this;
526
527 try
528 {
529 bool bInUseWorkerThreadsWasIncremented = false;
530
531 // Process until shutdown.
532 while(!_shutdown)
533 {
534 // Update the last time this thread was seen alive.
535 // It's good for debugging.
536 _workerThreads[Thread.CurrentThread] = DateTime.Now;
537
538 // Wait for a work item, shutdown, or timeout
539 WorkItem workItem = Dequeue();
540
541 // Update the last time this thread was seen alive.
542 // It's good for debugging.
543 _workerThreads[Thread.CurrentThread] = DateTime.Now;
544
545 // On timeout or shut down.
546 if (null == workItem)
547 {
548 // Double lock for quit.
549 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
550 {
551 lock(_workerThreads.SyncRoot)
552 {
553 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
554 {
555 // Inform that the thread is quiting and then quit.
556 // This method must be called within this lock or else
557 // more threads will quit and the thread pool will go
558 // below the lower limit.
559 InformCompleted();
560 break;
561 }
562 }
563 }
564 }
565
566 // If we didn't quit then skip to the next iteration.
567 if (null == workItem)
568 {
569 continue;
570 }
571
572 try
573 {
574 // Initialize the value to false
575 bInUseWorkerThreadsWasIncremented = false;
576
577 // Change the state of the work item to 'in progress' if possible.
578 // We do it here so if the work item has been canceled we won't
579 // increment the _inUseWorkerThreads.
580 // The cancel mechanism doesn't delete items from the queue,
581 // it marks the work item as canceled, and when the work item
582 // is dequeued, we just skip it.
583 // If the post execute of work item is set to always or to
584 // call when the work item is canceled then the StartingWorkItem()
585 // will return true, so the post execute can run.
586 if (!workItem.StartingWorkItem())
587 {
588 continue;
589 }
590
591 // Execute the callback. Make sure to accurately
592 // record how many callbacks are currently executing.
593 int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads);
594 _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
595
596 // Mark that the _inUseWorkerThreads incremented, so in the finally{}
597 // statement we will decrement it correctly.
598 bInUseWorkerThreadsWasIncremented = true;
599
600 // Set the _currentWorkItem to the current work item
601 _currentWorkItem = workItem;
602
603 lock(workItem)
604 {
605 workItem.currentThread = Thread.CurrentThread;
606 }
607
608 ExecuteWorkItem(workItem);
609
610 lock(workItem)
611 {
612 workItem.currentThread = null;
613 }
614
615 }
616 catch(ThreadAbortException ex)
617 {
618 lock(workItem)
619 {
620 workItem.currentThread = null;
621 }
622 ex.GetHashCode();
623 Thread.ResetAbort();
624 }
625 catch(Exception ex)
626 {
627 ex.GetHashCode();
628 // Do nothing
629 }
630 finally
631 {
632 lock(workItem)
633 {
634 workItem.currentThread = null;
635 }
636
637 if (null != workItem)
638 {
639 workItem.DisposeOfState();
640 }
641
642 // Set the _currentWorkItem to null, since we
643 // no longer run user's code.
644 _currentWorkItem = null;
645
646 // Decrement the _inUseWorkerThreads only if we had
647 // incremented it. Note the cancelled work items don't
648 // increment _inUseWorkerThreads.
649 if (bInUseWorkerThreadsWasIncremented)
650 {
651 int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads);
652 _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
653 }
654
655 // Notify that the work item has been completed.
656 // WorkItemsGroup may enqueue their next work item.
657 workItem.FireWorkItemCompleted();
658
659 // Decrement the number of work items here so the idle
660 // ManualResetEvent won't fluctuate.
661 DecrementWorkItemsCount();
662 }
663 }
664 }
665 catch(ThreadAbortException tae)
666 {
667 tae.GetHashCode();
668 // Handle the abort exception gracfully.
669 Thread.ResetAbort();
670 }
671 catch(Exception e)
672 {
673 Debug.Assert(null != e);
674 }
675 finally
676 {
677 InformCompleted();
678 }
679 }
680
681 private void ExecuteWorkItem(WorkItem workItem)
682 {
683 _pcs.SampleWorkItemsWaitTime(workItem.WaitingTime);
684 try
685 {
686 workItem.Execute();
687 }
688 catch
689 {
690 throw;
691 }
692 finally
693 {
694 _pcs.SampleWorkItemsProcessTime(workItem.ProcessTime);
695 }
696 }
697
698
699 #endregion
700
701 #region Public Methods
702
703 /// <summary>
704 /// Queue a work item
705 /// </summary>
706 /// <param name="callback">A callback to execute</param>
707 /// <returns>Returns a work item result</returns>
708 public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
709 {
710 ValidateNotDisposed();
711 ValidateCallback(callback);
712 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback);
713 Enqueue(workItem);
714 return workItem.GetWorkItemResult();
715 }
716
717 /// <summary>
718 /// Queue a work item
719 /// </summary>
720 /// <param name="callback">A callback to execute</param>
721 /// <param name="workItemPriority">The priority of the work item</param>
722 /// <returns>Returns a work item result</returns>
723 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
724 {
725 ValidateNotDisposed();
726 ValidateCallback(callback);
727 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority);
728 Enqueue(workItem);
729 return workItem.GetWorkItemResult();
730 }
731
732 /// <summary>
733 /// Queue a work item
734 /// </summary>
735 /// <param name="workItemInfo">Work item info</param>
736 /// <param name="callback">A callback to execute</param>
737 /// <returns>Returns a work item result</returns>
738 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
739 {
740 ValidateNotDisposed();
741 ValidateCallback(callback);
742 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback);
743 Enqueue(workItem);
744 return workItem.GetWorkItemResult();
745 }
746
747 /// <summary>
748 /// Queue a work item
749 /// </summary>
750 /// <param name="callback">A callback to execute</param>
751 /// <param name="state">
752 /// The context object of the work item. Used for passing arguments to the work item.
753 /// </param>
754 /// <returns>Returns a work item result</returns>
755 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
756 {
757 ValidateNotDisposed();
758 ValidateCallback(callback);
759 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state);
760 Enqueue(workItem);
761 return workItem.GetWorkItemResult();
762 }
763
764 /// <summary>
765 /// Queue a work item
766 /// </summary>
767 /// <param name="callback">A callback to execute</param>
768 /// <param name="state">
769 /// The context object of the work item. Used for passing arguments to the work item.
770 /// </param>
771 /// <param name="workItemPriority">The work item priority</param>
772 /// <returns>Returns a work item result</returns>
773 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
774 {
775 ValidateNotDisposed();
776 ValidateCallback(callback);
777 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority);
778 Enqueue(workItem);
779 return workItem.GetWorkItemResult();
780 }
781
782 /// <summary>
783 /// Queue a work item
784 /// </summary>
785 /// <param name="workItemInfo">Work item information</param>
786 /// <param name="callback">A callback to execute</param>
787 /// <param name="state">
788 /// The context object of the work item. Used for passing arguments to the work item.
789 /// </param>
790 /// <returns>Returns a work item result</returns>
791 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
792 {
793 ValidateNotDisposed();
794 ValidateCallback(callback);
795 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state);
796 Enqueue(workItem);
797 return workItem.GetWorkItemResult();
798 }
799
800 /// <summary>
801 /// Queue a work item
802 /// </summary>
803 /// <param name="callback">A callback to execute</param>
804 /// <param name="state">
805 /// The context object of the work item. Used for passing arguments to the work item.
806 /// </param>
807 /// <param name="postExecuteWorkItemCallback">
808 /// A delegate to call after the callback completion
809 /// </param>
810 /// <returns>Returns a work item result</returns>
811 public IWorkItemResult QueueWorkItem(
812 WorkItemCallback callback,
813 object state,
814 PostExecuteWorkItemCallback postExecuteWorkItemCallback)
815 {
816 ValidateNotDisposed();
817 ValidateCallback(callback);
818 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback);
819 Enqueue(workItem);
820 return workItem.GetWorkItemResult();
821 }
822
823 /// <summary>
824 /// Queue a work item
825 /// </summary>
826 /// <param name="callback">A callback to execute</param>
827 /// <param name="state">
828 /// The context object of the work item. Used for passing arguments to the work item.
829 /// </param>
830 /// <param name="postExecuteWorkItemCallback">
831 /// A delegate to call after the callback completion
832 /// </param>
833 /// <param name="workItemPriority">The work item priority</param>
834 /// <returns>Returns a work item result</returns>
835 public IWorkItemResult QueueWorkItem(
836 WorkItemCallback callback,
837 object state,
838 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
839 WorkItemPriority workItemPriority)
840 {
841 ValidateNotDisposed();
842 ValidateCallback(callback);
843 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
844 Enqueue(workItem);
845 return workItem.GetWorkItemResult();
846 }
847
848 /// <summary>
849 /// Queue a work item
850 /// </summary>
851 /// <param name="callback">A callback to execute</param>
852 /// <param name="state">
853 /// The context object of the work item. Used for passing arguments to the work item.
854 /// </param>
855 /// <param name="postExecuteWorkItemCallback">
856 /// A delegate to call after the callback completion
857 /// </param>
858 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
859 /// <returns>Returns a work item result</returns>
860 public IWorkItemResult QueueWorkItem(
861 WorkItemCallback callback,
862 object state,
863 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
864 CallToPostExecute callToPostExecute)
865 {
866 ValidateNotDisposed();
867 ValidateCallback(callback);
868 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
869 Enqueue(workItem);
870 return workItem.GetWorkItemResult();
871 }
872
873 /// <summary>
874 /// Queue a work item
875 /// </summary>
876 /// <param name="callback">A callback to execute</param>
877 /// <param name="state">
878 /// The context object of the work item. Used for passing arguments to the work item.
879 /// </param>
880 /// <param name="postExecuteWorkItemCallback">
881 /// A delegate to call after the callback completion
882 /// </param>
883 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
884 /// <param name="workItemPriority">The work item priority</param>
885 /// <returns>Returns a work item result</returns>
886 public IWorkItemResult QueueWorkItem(
887 WorkItemCallback callback,
888 object state,
889 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
890 CallToPostExecute callToPostExecute,
891 WorkItemPriority workItemPriority)
892 {
893 ValidateNotDisposed();
894 ValidateCallback(callback);
895 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
896 Enqueue(workItem);
897 return workItem.GetWorkItemResult();
898 }
899
900 /// <summary>
901 /// Wait for the thread pool to be idle
902 /// </summary>
903 public void WaitForIdle()
904 {
905 WaitForIdle(Timeout.Infinite);
906 }
907
908 /// <summary>
909 /// Wait for the thread pool to be idle
910 /// </summary>
911 public bool WaitForIdle(TimeSpan timeout)
912 {
913 return WaitForIdle((int)timeout.TotalMilliseconds);
914 }
915
916 /// <summary>
917 /// Wait for the thread pool to be idle
918 /// </summary>
919 public bool WaitForIdle(int millisecondsTimeout)
920 {
921 ValidateWaitForIdle();
922 return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
923 }
924
925 private void ValidateWaitForIdle()
926 {
927 if(_smartThreadPool == this)
928 {
929 throw new NotSupportedException(
930 "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
931 }
932 }
933
934 internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup)
935 {
936 ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem);
937 if ((null != workItemsGroup) &&
938 (null != SmartThreadPool._currentWorkItem) &&
939 SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup))
940 {
941 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
942 }
943 }
944
945 [MethodImpl(MethodImplOptions.NoInlining)]
946 private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem)
947 {
948 if ((null != workItemsGroup) &&
949 (null != workItem) &&
950 workItem.WasQueuedBy(workItemsGroup))
951 {
952 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
953 }
954 }
955
956
957
958 /// <summary>
959 /// Force the SmartThreadPool to shutdown
960 /// </summary>
961 public void Shutdown()
962 {
963 Shutdown(true, 0);
964 }
965
966 public void Shutdown(bool forceAbort, TimeSpan timeout)
967 {
968 Shutdown(forceAbort, (int)timeout.TotalMilliseconds);
969 }
970
971 /// <summary>
972 /// Empties the queue of work items and abort the threads in the pool.
973 /// </summary>
974 public void Shutdown(bool forceAbort, int millisecondsTimeout)
975 {
976 ValidateNotDisposed();
977
978 ISTPInstancePerformanceCounters pcs = _pcs;
979
980 if (NullSTPInstancePerformanceCounters.Instance != _pcs)
981 {
982 _pcs.Dispose();
983 // Set the _pcs to "null" to stop updating the performance
984 // counters
985 _pcs = NullSTPInstancePerformanceCounters.Instance;
986 }
987
988 Thread [] threads = null;
989 lock(_workerThreads.SyncRoot)
990 {
991 // Shutdown the work items queue
992 _workItemsQueue.Dispose();
993
994 // Signal the threads to exit
995 _shutdown = true;
996 _shuttingDownEvent.Set();
997
998 // Make a copy of the threads' references in the pool
999 threads = new Thread [_workerThreads.Count];
1000 _workerThreads.Keys.CopyTo(threads, 0);
1001 }
1002
1003 int millisecondsLeft = millisecondsTimeout;
1004 DateTime start = DateTime.Now;
1005 bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
1006 bool timeout = false;
1007
1008 // Each iteration we update the time left for the timeout.
1009 foreach(Thread thread in threads)
1010 {
1011 // Join don't work with negative numbers
1012 if (!waitInfinitely && (millisecondsLeft < 0))
1013 {
1014 timeout = true;
1015 break;
1016 }
1017
1018 // Wait for the thread to terminate
1019 bool success = thread.Join(millisecondsLeft);
1020 if(!success)
1021 {
1022 timeout = true;
1023 break;
1024 }
1025
1026 if(!waitInfinitely)
1027 {
1028 // Update the time left to wait
1029 TimeSpan ts = DateTime.Now - start;
1030 millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
1031 }
1032 }
1033
1034 if (timeout && forceAbort)
1035 {
1036 // Abort the threads in the pool
1037 foreach(Thread thread in threads)
1038 {
1039 if ((thread != null) && thread.IsAlive)
1040 {
1041 try
1042 {
1043 thread.Abort("Shutdown");
1044 }
1045 catch(SecurityException e)
1046 {
1047 e.GetHashCode();
1048 }
1049 catch(ThreadStateException ex)
1050 {
1051 ex.GetHashCode();
1052 // In case the thread has been terminated
1053 // after the check if it is alive.
1054 }
1055 }
1056 }
1057 }
1058
1059 // Dispose of the performance counters
1060 pcs.Dispose();
1061 }
1062
1063 /// <summary>
1064 /// Wait for all work items to complete
1065 /// </summary>
1066 /// <param name="workItemResults">Array of work item result objects</param>
1067 /// <returns>
1068 /// true when every work item in workItemResults has completed; otherwise false.
1069 /// </returns>
1070 public static bool WaitAll(
1071 IWorkItemResult [] workItemResults)
1072 {
1073 return WaitAll(workItemResults, Timeout.Infinite, true);
1074 }
1075
1076 /// <summary>
1077 /// Wait for all work items to complete
1078 /// </summary>
1079 /// <param name="workItemResults">Array of work item result objects</param>
1080 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1081 /// <param name="exitContext">
1082 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1083 /// </param>
1084 /// <returns>
1085 /// true when every work item in workItemResults has completed; otherwise false.
1086 /// </returns>
1087 public static bool WaitAll(
1088 IWorkItemResult [] workItemResults,
1089 TimeSpan timeout,
1090 bool exitContext)
1091 {
1092 return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
1093 }
1094
1095 /// <summary>
1096 /// Wait for all work items to complete
1097 /// </summary>
1098 /// <param name="workItemResults">Array of work item result objects</param>
1099 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1100 /// <param name="exitContext">
1101 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1102 /// </param>
1103 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1104 /// <returns>
1105 /// true when every work item in workItemResults has completed; otherwise false.
1106 /// </returns>
1107 public static bool WaitAll(
1108 IWorkItemResult [] workItemResults,
1109 TimeSpan timeout,
1110 bool exitContext,
1111 WaitHandle cancelWaitHandle)
1112 {
1113 return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
1114 }
1115
1116 /// <summary>
1117 /// Wait for all work items to complete
1118 /// </summary>
1119 /// <param name="workItemResults">Array of work item result objects</param>
1120 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1121 /// <param name="exitContext">
1122 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1123 /// </param>
1124 /// <returns>
1125 /// true when every work item in workItemResults has completed; otherwise false.
1126 /// </returns>
1127 public static bool WaitAll(
1128 IWorkItemResult [] workItemResults,
1129 int millisecondsTimeout,
1130 bool exitContext)
1131 {
1132 return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null);
1133 }
1134
1135 /// <summary>
1136 /// Wait for all work items to complete
1137 /// </summary>
1138 /// <param name="workItemResults">Array of work item result objects</param>
1139 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1140 /// <param name="exitContext">
1141 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1142 /// </param>
1143 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1144 /// <returns>
1145 /// true when every work item in workItemResults has completed; otherwise false.
1146 /// </returns>
1147 public static bool WaitAll(
1148 IWorkItemResult [] workItemResults,
1149 int millisecondsTimeout,
1150 bool exitContext,
1151 WaitHandle cancelWaitHandle)
1152 {
1153 return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
1154 }
1155
1156
1157 /// <summary>
1158 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1159 /// </summary>
1160 /// <param name="workItemResults">Array of work item result objects</param>
1161 /// <returns>
1162 /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
1163 /// </returns>
1164 public static int WaitAny(
1165 IWorkItemResult [] workItemResults)
1166 {
1167 return WaitAny(workItemResults, Timeout.Infinite, true);
1168 }
1169
1170 /// <summary>
1171 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1172 /// </summary>
1173 /// <param name="workItemResults">Array of work item result objects</param>
1174 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1175 /// <param name="exitContext">
1176 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1177 /// </param>
1178 /// <returns>
1179 /// 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.
1180 /// </returns>
1181 public static int WaitAny(
1182 IWorkItemResult [] workItemResults,
1183 TimeSpan timeout,
1184 bool exitContext)
1185 {
1186 return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
1187 }
1188
1189 /// <summary>
1190 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1191 /// </summary>
1192 /// <param name="workItemResults">Array of work item result objects</param>
1193 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1194 /// <param name="exitContext">
1195 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1196 /// </param>
1197 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1198 /// <returns>
1199 /// 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.
1200 /// </returns>
1201 public static int WaitAny(
1202 IWorkItemResult [] workItemResults,
1203 TimeSpan timeout,
1204 bool exitContext,
1205 WaitHandle cancelWaitHandle)
1206 {
1207 return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
1208 }
1209
1210 /// <summary>
1211 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1212 /// </summary>
1213 /// <param name="workItemResults">Array of work item result objects</param>
1214 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1215 /// <param name="exitContext">
1216 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1217 /// </param>
1218 /// <returns>
1219 /// 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.
1220 /// </returns>
1221 public static int WaitAny(
1222 IWorkItemResult [] workItemResults,
1223 int millisecondsTimeout,
1224 bool exitContext)
1225 {
1226 return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null);
1227 }
1228
1229 /// <summary>
1230 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1231 /// </summary>
1232 /// <param name="workItemResults">Array of work item result objects</param>
1233 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1234 /// <param name="exitContext">
1235 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1236 /// </param>
1237 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1238 /// <returns>
1239 /// 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.
1240 /// </returns>
1241 public static int WaitAny(
1242 IWorkItemResult [] workItemResults,
1243 int millisecondsTimeout,
1244 bool exitContext,
1245 WaitHandle cancelWaitHandle)
1246 {
1247 return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
1248 }
1249
1250 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency)
1251 {
1252 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo);
1253 return workItemsGroup;
1254 }
1255
1256 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo)
1257 {
1258 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo);
1259 return workItemsGroup;
1260 }
1261
1262 public event WorkItemsGroupIdleHandler OnIdle
1263 {
1264 add
1265 {
1266 throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
1267 //_onIdle += value;
1268 }
1269 remove
1270 {
1271 throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
1272 //_onIdle -= value;
1273 }
1274 }
1275
1276 public void Cancel()
1277 {
1278 ICollection workItemsGroups = _workItemsGroups.Values;
1279 foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
1280 {
1281 workItemsGroup.Cancel();
1282 }
1283 }
1284
1285 public void Start()
1286 {
1287 lock (this)
1288 {
1289 if (!this._stpStartInfo.StartSuspended)
1290 {
1291 return;
1292 }
1293 _stpStartInfo.StartSuspended = false;
1294 }
1295
1296 ICollection workItemsGroups = _workItemsGroups.Values;
1297 foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
1298 {
1299 workItemsGroup.OnSTPIsStarting();
1300 }
1301
1302 StartOptimalNumberOfThreads();
1303 }
1304
1305 #endregion
1306
1307 #region Properties
1308
1309 /// <summary>
1310 /// Get/Set the name of the SmartThreadPool instance
1311 /// </summary>
1312 public string Name
1313 {
1314 get
1315 {
1316 return _name;
1317 }
1318
1319 set
1320 {
1321 _name = value;
1322 }
1323 }
1324
1325 /// <summary>
1326 /// Get the lower limit of threads in the pool.
1327 /// </summary>
1328 public int MinThreads
1329 {
1330 get
1331 {
1332 ValidateNotDisposed();
1333 return _stpStartInfo.MinWorkerThreads;
1334 }
1335 }
1336
1337 /// <summary>
1338 /// Get the upper limit of threads in the pool.
1339 /// </summary>
1340 public int MaxThreads
1341 {
1342 get
1343 {
1344 ValidateNotDisposed();
1345 return _stpStartInfo.MaxWorkerThreads;
1346 }
1347 }
1348 /// <summary>
1349 /// Get the number of threads in the thread pool.
1350 /// Should be between the lower and the upper limits.
1351 /// </summary>
1352 public int ActiveThreads
1353 {
1354 get
1355 {
1356 ValidateNotDisposed();
1357 return _workerThreads.Count;
1358 }
1359 }
1360
1361 /// <summary>
1362 /// Get the number of busy (not idle) threads in the thread pool.
1363 /// </summary>
1364 public int InUseThreads
1365 {
1366 get
1367 {
1368 ValidateNotDisposed();
1369 return _inUseWorkerThreads;
1370 }
1371 }
1372
1373 /// <summary>
1374 /// Get the number of work items in the queue.
1375 /// </summary>
1376 public int WaitingCallbacks
1377 {
1378 get
1379 {
1380 ValidateNotDisposed();
1381 return _workItemsQueue.Count;
1382 }
1383 }
1384
1385
1386 public event EventHandler Idle
1387 {
1388 add
1389 {
1390 _stpIdle += value;
1391 }
1392
1393 remove
1394 {
1395 _stpIdle -= value;
1396 }
1397 }
1398
1399 #endregion
1400
1401 #region IDisposable Members
1402
1403// ~SmartThreadPool()
1404// {
1405// Dispose();
1406// }
1407
1408 public void Dispose()
1409 {
1410 if (!_isDisposed)
1411 {
1412 if (!_shutdown)
1413 {
1414 Shutdown();
1415 }
1416
1417 if (null != _shuttingDownEvent)
1418 {
1419 _shuttingDownEvent.Close();
1420 _shuttingDownEvent = null;
1421 }
1422 _workerThreads.Clear();
1423 _isDisposed = true;
1424 GC.SuppressFinalize(this);
1425 }
1426 }
1427
1428 private void ValidateNotDisposed()
1429 {
1430 if(_isDisposed)
1431 {
1432 throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
1433 }
1434 }
1435 #endregion
1436 }
1437 #endregion
1438}