aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ThirdParty/SmartThreadPool
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
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 '')
-rw-r--r--ThirdParty/SmartThreadPool/AssemblyInfo.cs61
-rw-r--r--ThirdParty/SmartThreadPool/CallerThreadContext.cs223
-rw-r--r--ThirdParty/SmartThreadPool/Exceptions.cs81
-rw-r--r--ThirdParty/SmartThreadPool/Interfaces.cs271
-rw-r--r--ThirdParty/SmartThreadPool/PriorityQueue.cs240
-rw-r--r--ThirdParty/SmartThreadPool/STPPerformanceCounter.cs352
-rw-r--r--ThirdParty/SmartThreadPool/STPStartInfo.cs99
-rw-r--r--ThirdParty/SmartThreadPool/SmartThreadPool.cs1438
-rw-r--r--ThirdParty/SmartThreadPool/WIGStartInfo.cs99
-rw-r--r--ThirdParty/SmartThreadPool/WorkItem.cs1035
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemFactory.cs333
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemInfo.cs102
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemsGroup.cs512
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemsQueue.cs600
14 files changed, 5446 insertions, 0 deletions
diff --git a/ThirdParty/SmartThreadPool/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/AssemblyInfo.cs
new file mode 100644
index 0000000..765bd30
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/AssemblyInfo.cs
@@ -0,0 +1,61 @@
1using System;
2using System.Reflection;
3using System.Runtime.InteropServices;
4
5//
6// General Information about an assembly is controlled through the following
7// set of attributes. Change these attribute values to modify the information
8// associated with an assembly.
9//
10[assembly: AssemblyTitle("")]
11[assembly: AssemblyDescription("")]
12[assembly: AssemblyConfiguration("")]
13[assembly: AssemblyCompany("")]
14[assembly: AssemblyProduct("")]
15[assembly: AssemblyCopyright("")]
16[assembly: AssemblyTrademark("")]
17[assembly: AssemblyCulture("")]
18[assembly: ComVisible(false)]
19[assembly: CLSCompliant(true)]
20
21//
22// Version information for an assembly consists of the following four values:
23//
24// Major Version
25// Minor Version
26// Build Number
27// Revision
28//
29// You can specify all the values or you can default the Revision and Build Numbers
30// by using the '*' as shown below:
31
32[assembly: AssemblyVersion("1.0.*")]
33
34//
35// In order to sign your assembly you must specify a key to use. Refer to the
36// Microsoft .NET Framework documentation for more information on assembly signing.
37//
38// Use the attributes below to control which key is used for signing.
39//
40// Notes:
41// (*) If no key is specified, the assembly is not signed.
42// (*) KeyName refers to a key that has been installed in the Crypto Service
43// Provider (CSP) on your machine. KeyFile refers to a file which contains
44// a key.
45// (*) If the KeyFile and the KeyName values are both specified, the
46// following processing occurs:
47// (1) If the KeyName can be found in the CSP, that key is used.
48// (2) If the KeyName does not exist and the KeyFile does exist, the key
49// in the KeyFile is installed into the CSP and used.
50// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
51// When specifying the KeyFile, the location of the KeyFile should be
52// relative to the project output directory which is
53// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
54// located in the project directory, you would specify the AssemblyKeyFile
55// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
56// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
57// documentation for more information on this.
58//
59[assembly: AssemblyDelaySign(false)]
60[assembly: AssemblyKeyFile("")]
61[assembly: AssemblyKeyName("")]
diff --git a/ThirdParty/SmartThreadPool/CallerThreadContext.cs b/ThirdParty/SmartThreadPool/CallerThreadContext.cs
new file mode 100644
index 0000000..6ea53f6
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/CallerThreadContext.cs
@@ -0,0 +1,223 @@
1using System;
2using System.Diagnostics;
3using System.Threading;
4using System.Reflection;
5using System.Web;
6using System.Runtime.Remoting.Messaging;
7
8
9namespace Amib.Threading
10{
11 #region CallerThreadContext class
12
13 /// <summary>
14 /// This class stores the caller call context in order to restore
15 /// it when the work item is executed in the thread pool environment.
16 /// </summary>
17 internal class CallerThreadContext
18 {
19 #region Prepare reflection information
20
21 // Cached type information.
22 private static MethodInfo getLogicalCallContextMethodInfo =
23 typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
24
25 private static MethodInfo setLogicalCallContextMethodInfo =
26 typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
27
28 private static string HttpContextSlotName = GetHttpContextSlotName();
29
30 private static string GetHttpContextSlotName()
31 {
32 FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
33
34 if( fi != null )
35 return (string)fi.GetValue(null);
36 else // Use the default "HttpContext" slot name
37 return "HttpContext";
38 }
39
40 #endregion
41
42 #region Private fields
43
44 private HttpContext _httpContext = null;
45 private LogicalCallContext _callContext = null;
46
47 #endregion
48
49 /// <summary>
50 /// Constructor
51 /// </summary>
52 private CallerThreadContext()
53 {
54 }
55
56 public bool CapturedCallContext
57 {
58 get
59 {
60 return (null != _callContext);
61 }
62 }
63
64 public bool CapturedHttpContext
65 {
66 get
67 {
68 return (null != _httpContext);
69 }
70 }
71
72 /// <summary>
73 /// Captures the current thread context
74 /// </summary>
75 /// <returns></returns>
76 public static CallerThreadContext Capture(
77 bool captureCallContext,
78 bool captureHttpContext)
79 {
80 Debug.Assert(captureCallContext || captureHttpContext);
81
82 CallerThreadContext callerThreadContext = new CallerThreadContext();
83
84 // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture()
85 // Capture Call Context
86 if(captureCallContext && (getLogicalCallContextMethodInfo != null))
87 {
88 callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null);
89 if (callerThreadContext._callContext != null)
90 {
91 callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone();
92 }
93 }
94
95 // Capture httpContext
96 if (captureHttpContext && (null != HttpContext.Current))
97 {
98 callerThreadContext._httpContext = HttpContext.Current;
99 }
100
101 return callerThreadContext;
102 }
103
104 /// <summary>
105 /// Applies the thread context stored earlier
106 /// </summary>
107 /// <param name="callerThreadContext"></param>
108 public static void Apply(CallerThreadContext callerThreadContext)
109 {
110 if (null == callerThreadContext)
111 {
112 throw new ArgumentNullException("callerThreadContext");
113 }
114
115 // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run()
116 // Restore call context
117 if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null))
118 {
119 setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
120 }
121
122 // Restore HttpContext
123 if (callerThreadContext._httpContext != null)
124 {
125 CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
126 }
127 }
128 }
129
130 #endregion
131
132}
133
134
135/*
136// Ami Bar
137// amibar@gmail.com
138
139using System;
140using System.Threading;
141using System.Globalization;
142using System.Security.Principal;
143using System.Reflection;
144using System.Runtime.Remoting.Contexts;
145
146namespace Amib.Threading.Internal
147{
148 #region CallerThreadContext class
149
150 /// <summary>
151 /// This class stores the caller thread context in order to restore
152 /// it when the work item is executed in the context of the thread
153 /// from the pool.
154 /// Note that we can't store the thread's CompressedStack, because
155 /// it throws a security exception
156 /// </summary>
157 public class CallerThreadContext
158 {
159 private CultureInfo _culture = null;
160 private CultureInfo _cultureUI = null;
161 private IPrincipal _principal;
162 private System.Runtime.Remoting.Contexts.Context _context;
163
164 private static FieldInfo _fieldInfo = GetFieldInfo();
165
166 private static FieldInfo GetFieldInfo()
167 {
168 Type threadType = typeof(Thread);
169 return threadType.GetField(
170 "m_Context",
171 BindingFlags.Instance | BindingFlags.NonPublic);
172 }
173
174 /// <summary>
175 /// Constructor
176 /// </summary>
177 private CallerThreadContext()
178 {
179 }
180
181 /// <summary>
182 /// Captures the current thread context
183 /// </summary>
184 /// <returns></returns>
185 public static CallerThreadContext Capture()
186 {
187 CallerThreadContext callerThreadContext = new CallerThreadContext();
188
189 Thread thread = Thread.CurrentThread;
190 callerThreadContext._culture = thread.CurrentCulture;
191 callerThreadContext._cultureUI = thread.CurrentUICulture;
192 callerThreadContext._principal = Thread.CurrentPrincipal;
193 callerThreadContext._context = Thread.CurrentContext;
194 return callerThreadContext;
195 }
196
197 /// <summary>
198 /// Applies the thread context stored earlier
199 /// </summary>
200 /// <param name="callerThreadContext"></param>
201 public static void Apply(CallerThreadContext callerThreadContext)
202 {
203 Thread thread = Thread.CurrentThread;
204 thread.CurrentCulture = callerThreadContext._culture;
205 thread.CurrentUICulture = callerThreadContext._cultureUI;
206 Thread.CurrentPrincipal = callerThreadContext._principal;
207
208 // Uncomment the following block to enable the Thread.CurrentThread
209/*
210 if (null != _fieldInfo)
211 {
212 _fieldInfo.SetValue(
213 Thread.CurrentThread,
214 callerThreadContext._context);
215 }
216* /
217 }
218 }
219
220 #endregion
221}
222*/
223
diff --git a/ThirdParty/SmartThreadPool/Exceptions.cs b/ThirdParty/SmartThreadPool/Exceptions.cs
new file mode 100644
index 0000000..c454709
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/Exceptions.cs
@@ -0,0 +1,81 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System;
5using System.Runtime.Serialization;
6
7namespace Amib.Threading
8{
9 #region Exceptions
10
11 /// <summary>
12 /// Represents an exception in case IWorkItemResult.GetResult has been canceled
13 /// </summary>
14 [Serializable]
15 public sealed class WorkItemCancelException : ApplicationException
16 {
17 public WorkItemCancelException() : base()
18 {
19 }
20
21 public WorkItemCancelException(string message) : base(message)
22 {
23 }
24
25 public WorkItemCancelException(string message, Exception e) : base(message, e)
26 {
27 }
28
29 public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc)
30 {
31 }
32 }
33
34 /// <summary>
35 /// Represents an exception in case IWorkItemResult.GetResult has been timed out
36 /// </summary>
37 [Serializable]
38 public sealed class WorkItemTimeoutException : ApplicationException
39 {
40 public WorkItemTimeoutException() : base()
41 {
42 }
43
44 public WorkItemTimeoutException(string message) : base(message)
45 {
46 }
47
48 public WorkItemTimeoutException(string message, Exception e) : base(message, e)
49 {
50 }
51
52 public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc)
53 {
54 }
55 }
56
57 /// <summary>
58 /// Represents an exception in case IWorkItemResult.GetResult has been timed out
59 /// </summary>
60 [Serializable]
61 public sealed class WorkItemResultException : ApplicationException
62 {
63 public WorkItemResultException() : base()
64 {
65 }
66
67 public WorkItemResultException(string message) : base(message)
68 {
69 }
70
71 public WorkItemResultException(string message, Exception e) : base(message, e)
72 {
73 }
74
75 public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc)
76 {
77 }
78 }
79
80 #endregion
81}
diff --git a/ThirdParty/SmartThreadPool/Interfaces.cs b/ThirdParty/SmartThreadPool/Interfaces.cs
new file mode 100644
index 0000000..f1c1fcf
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/Interfaces.cs
@@ -0,0 +1,271 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System;
5using System.Threading;
6
7namespace Amib.Threading
8{
9 #region Delegates
10
11 /// <summary>
12 /// A delegate that represents the method to run as the work item
13 /// </summary>
14 /// <param name="state">A state object for the method to run</param>
15 public delegate object WorkItemCallback(object state);
16
17 /// <summary>
18 /// A delegate to call after the WorkItemCallback completed
19 /// </summary>
20 /// <param name="wir">The work item result object</param>
21 public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
22
23 /// <summary>
24 /// A delegate to call when a WorkItemsGroup becomes idle
25 /// </summary>
26 /// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
27 public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
28
29 #endregion
30
31 #region WorkItem Priority
32
33 public enum WorkItemPriority
34 {
35 Lowest,
36 BelowNormal,
37 Normal,
38 AboveNormal,
39 Highest,
40 }
41
42 #endregion
43
44 #region IHasWorkItemPriority interface
45
46 public interface IHasWorkItemPriority
47 {
48 WorkItemPriority WorkItemPriority { get; }
49 }
50
51 #endregion
52
53 #region IWorkItemsGroup interface
54
55 /// <summary>
56 /// IWorkItemsGroup interface
57 /// </summary>
58 public interface IWorkItemsGroup
59 {
60 /// <summary>
61 /// Get/Set the name of the WorkItemsGroup
62 /// </summary>
63 string Name { get; set; }
64
65 IWorkItemResult QueueWorkItem(WorkItemCallback callback);
66 IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
67 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
68 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
69 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
70 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
71 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
72 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
73
74 IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
75 IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
76
77 void WaitForIdle();
78 bool WaitForIdle(TimeSpan timeout);
79 bool WaitForIdle(int millisecondsTimeout);
80
81 int WaitingCallbacks { get; }
82 event WorkItemsGroupIdleHandler OnIdle;
83
84 void Cancel();
85 void Start();
86 }
87
88 #endregion
89
90 #region CallToPostExecute enumerator
91
92 [Flags]
93 public enum CallToPostExecute
94 {
95 Never = 0x00,
96 WhenWorkItemCanceled = 0x01,
97 WhenWorkItemNotCanceled = 0x02,
98 Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
99 }
100
101 #endregion
102
103 #region IWorkItemResult interface
104
105 /// <summary>
106 /// IWorkItemResult interface
107 /// </summary>
108 public interface IWorkItemResult
109 {
110 /// <summary>
111 /// Get the result of the work item.
112 /// If the work item didn't run yet then the caller waits.
113 /// </summary>
114 /// <returns>The result of the work item</returns>
115 object GetResult();
116
117 /// <summary>
118 /// Get the result of the work item.
119 /// If the work item didn't run yet then the caller waits until timeout.
120 /// </summary>
121 /// <returns>The result of the work item</returns>
122 /// On timeout throws WorkItemTimeoutException
123 object GetResult(
124 int millisecondsTimeout,
125 bool exitContext);
126
127 /// <summary>
128 /// Get the result of the work item.
129 /// If the work item didn't run yet then the caller waits until timeout.
130 /// </summary>
131 /// <returns>The result of the work item</returns>
132 /// On timeout throws WorkItemTimeoutException
133 object GetResult(
134 TimeSpan timeout,
135 bool exitContext);
136
137 void Abort();
138
139 /// <summary>
140 /// Get the result of the work item.
141 /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
142 /// </summary>
143 /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
144 /// <param name="exitContext">
145 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
146 /// </param>
147 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
148 /// <returns>The result of the work item</returns>
149 /// On timeout throws WorkItemTimeoutException
150 /// On cancel throws WorkItemCancelException
151 object GetResult(
152 int millisecondsTimeout,
153 bool exitContext,
154 WaitHandle cancelWaitHandle);
155
156 /// <summary>
157 /// Get the result of the work item.
158 /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
159 /// </summary>
160 /// <returns>The result of the work item</returns>
161 /// On timeout throws WorkItemTimeoutException
162 /// On cancel throws WorkItemCancelException
163 object GetResult(
164 TimeSpan timeout,
165 bool exitContext,
166 WaitHandle cancelWaitHandle);
167
168 /// <summary>
169 /// Get the result of the work item.
170 /// If the work item didn't run yet then the caller waits.
171 /// </summary>
172 /// <param name="e">Filled with the exception if one was thrown</param>
173 /// <returns>The result of the work item</returns>
174 object GetResult(out Exception e);
175
176 /// <summary>
177 /// Get the result of the work item.
178 /// If the work item didn't run yet then the caller waits until timeout.
179 /// </summary>
180 /// <param name="e">Filled with the exception if one was thrown</param>
181 /// <returns>The result of the work item</returns>
182 /// On timeout throws WorkItemTimeoutException
183 object GetResult(
184 int millisecondsTimeout,
185 bool exitContext,
186 out Exception e);
187
188 /// <summary>
189 /// Get the result of the work item.
190 /// If the work item didn't run yet then the caller waits until timeout.
191 /// </summary>
192 /// <param name="e">Filled with the exception if one was thrown</param>
193 /// <returns>The result of the work item</returns>
194 /// On timeout throws WorkItemTimeoutException
195 object GetResult(
196 TimeSpan timeout,
197 bool exitContext,
198 out Exception e);
199
200 /// <summary>
201 /// Get the result of the work item.
202 /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
203 /// </summary>
204 /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
205 /// <param name="exitContext">
206 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
207 /// </param>
208 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
209 /// <param name="e">Filled with the exception if one was thrown</param>
210 /// <returns>The result of the work item</returns>
211 /// On timeout throws WorkItemTimeoutException
212 /// On cancel throws WorkItemCancelException
213 object GetResult(
214 int millisecondsTimeout,
215 bool exitContext,
216 WaitHandle cancelWaitHandle,
217 out Exception e);
218
219 /// <summary>
220 /// Get the result of the work item.
221 /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
222 /// </summary>
223 /// <returns>The result of the work item</returns>
224 /// <param name="e">Filled with the exception if one was thrown</param>
225 /// On timeout throws WorkItemTimeoutException
226 /// On cancel throws WorkItemCancelException
227 object GetResult(
228 TimeSpan timeout,
229 bool exitContext,
230 WaitHandle cancelWaitHandle,
231 out Exception e);
232
233 /// <summary>
234 /// Gets an indication whether the asynchronous operation has completed.
235 /// </summary>
236 bool IsCompleted { get; }
237
238 /// <summary>
239 /// Gets an indication whether the asynchronous operation has been canceled.
240 /// </summary>
241 bool IsCanceled { get; }
242
243 /// <summary>
244 /// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
245 /// </summary>
246 object State { get; }
247
248 /// <summary>
249 /// Cancel the work item if it didn't start running yet.
250 /// </summary>
251 /// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
252 bool Cancel();
253
254 /// <summary>
255 /// Get the work item's priority
256 /// </summary>
257 WorkItemPriority WorkItemPriority { get; }
258
259 /// <summary>
260 /// Return the result, same as GetResult()
261 /// </summary>
262 object Result { get; }
263
264 /// <summary>
265 /// Returns the exception if occured otherwise returns null.
266 /// </summary>
267 object Exception { get; }
268 }
269
270 #endregion
271}
diff --git a/ThirdParty/SmartThreadPool/PriorityQueue.cs b/ThirdParty/SmartThreadPool/PriorityQueue.cs
new file mode 100644
index 0000000..63d5e84
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/PriorityQueue.cs
@@ -0,0 +1,240 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System;
5using System.Collections;
6using System.Diagnostics;
7
8namespace Amib.Threading.Internal
9{
10 #region PriorityQueue class
11
12 /// <summary>
13 /// PriorityQueue class
14 /// This class is not thread safe because we use external lock
15 /// </summary>
16 public sealed class PriorityQueue : IEnumerable
17 {
18 #region Private members
19
20 /// <summary>
21 /// The number of queues, there is one for each type of priority
22 /// </summary>
23 private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1;
24
25 /// <summary>
26 /// Work items queues. There is one for each type of priority
27 /// </summary>
28 private Queue [] _queues = new Queue[_queuesCount];
29
30 /// <summary>
31 /// The total number of work items within the queues
32 /// </summary>
33 private int _workItemsCount = 0;
34
35 /// <summary>
36 /// Use with IEnumerable interface
37 /// </summary>
38 private int _version = 0;
39
40 #endregion
41
42 #region Contructor
43
44 public PriorityQueue()
45 {
46 for(int i = 0; i < _queues.Length; ++i)
47 {
48 _queues[i] = new Queue();
49 }
50 }
51
52 #endregion
53
54 #region Methods
55
56 /// <summary>
57 /// Enqueue a work item.
58 /// </summary>
59 /// <param name="workItem">A work item</param>
60 public void Enqueue(IHasWorkItemPriority workItem)
61 {
62 Debug.Assert(null != workItem);
63
64 int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1;
65 Debug.Assert(queueIndex >= 0);
66 Debug.Assert(queueIndex < _queuesCount);
67
68 _queues[queueIndex].Enqueue(workItem);
69 ++_workItemsCount;
70 ++_version;
71 }
72
73 /// <summary>
74 /// Dequeque a work item.
75 /// </summary>
76 /// <returns>Returns the next work item</returns>
77 public IHasWorkItemPriority Dequeue()
78 {
79 IHasWorkItemPriority workItem = null;
80
81 if(_workItemsCount > 0)
82 {
83 int queueIndex = GetNextNonEmptyQueue(-1);
84 Debug.Assert(queueIndex >= 0);
85 workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority;
86 Debug.Assert(null != workItem);
87 --_workItemsCount;
88 ++_version;
89 }
90
91 return workItem;
92 }
93
94 /// <summary>
95 /// Find the next non empty queue starting at queue queueIndex+1
96 /// </summary>
97 /// <param name="queueIndex">The index-1 to start from</param>
98 /// <returns>
99 /// The index of the next non empty queue or -1 if all the queues are empty
100 /// </returns>
101 private int GetNextNonEmptyQueue(int queueIndex)
102 {
103 for(int i = queueIndex+1; i < _queuesCount; ++i)
104 {
105 if(_queues[i].Count > 0)
106 {
107 return i;
108 }
109 }
110 return -1;
111 }
112
113 /// <summary>
114 /// The number of work items
115 /// </summary>
116 public int Count
117 {
118 get
119 {
120 return _workItemsCount;
121 }
122 }
123
124 /// <summary>
125 /// Clear all the work items
126 /// </summary>
127 public void Clear()
128 {
129 if (_workItemsCount > 0)
130 {
131 foreach(Queue queue in _queues)
132 {
133 queue.Clear();
134 }
135 _workItemsCount = 0;
136 ++_version;
137 }
138 }
139
140 #endregion
141
142 #region IEnumerable Members
143
144 /// <summary>
145 /// Returns an enumerator to iterate over the work items
146 /// </summary>
147 /// <returns>Returns an enumerator</returns>
148 public IEnumerator GetEnumerator()
149 {
150 return new PriorityQueueEnumerator(this);
151 }
152
153 #endregion
154
155 #region PriorityQueueEnumerator
156
157 /// <summary>
158 /// The class the implements the enumerator
159 /// </summary>
160 private class PriorityQueueEnumerator : IEnumerator
161 {
162 private PriorityQueue _priorityQueue;
163 private int _version;
164 private int _queueIndex;
165 private IEnumerator _enumerator;
166
167 public PriorityQueueEnumerator(PriorityQueue priorityQueue)
168 {
169 _priorityQueue = priorityQueue;
170 _version = _priorityQueue._version;
171 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
172 if (_queueIndex >= 0)
173 {
174 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
175 }
176 else
177 {
178 _enumerator = null;
179 }
180 }
181
182 #region IEnumerator Members
183
184 public void Reset()
185 {
186 _version = _priorityQueue._version;
187 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
188 if (_queueIndex >= 0)
189 {
190 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
191 }
192 else
193 {
194 _enumerator = null;
195 }
196 }
197
198 public object Current
199 {
200 get
201 {
202 Debug.Assert(null != _enumerator);
203 return _enumerator.Current;
204 }
205 }
206
207 public bool MoveNext()
208 {
209 if (null == _enumerator)
210 {
211 return false;
212 }
213
214 if(_version != _priorityQueue._version)
215 {
216 throw new InvalidOperationException("The collection has been modified");
217
218 }
219 if (!_enumerator.MoveNext())
220 {
221 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex);
222 if(-1 == _queueIndex)
223 {
224 return false;
225 }
226 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
227 _enumerator.MoveNext();
228 return true;
229 }
230 return true;
231 }
232
233 #endregion
234 }
235
236 #endregion
237 }
238
239 #endregion
240}
diff --git a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs
new file mode 100644
index 0000000..be70aea
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs
@@ -0,0 +1,352 @@
1using System;
2using System.Diagnostics;
3
4namespace Amib.Threading.Internal
5{
6 internal enum STPPerformanceCounterType
7 {
8 // Fields
9 ActiveThreads = 0,
10 InUseThreads = 1,
11 OverheadThreads = 2,
12 OverheadThreadsPercent = 3,
13 OverheadThreadsPercentBase = 4,
14
15 WorkItems = 5,
16 WorkItemsInQueue = 6,
17 WorkItemsProcessed = 7,
18
19 WorkItemsQueuedPerSecond = 8,
20 WorkItemsProcessedPerSecond = 9,
21
22 AvgWorkItemWaitTime = 10,
23 AvgWorkItemWaitTimeBase = 11,
24
25 AvgWorkItemProcessTime = 12,
26 AvgWorkItemProcessTimeBase = 13,
27
28 WorkItemsGroups = 14,
29
30 LastCounter = 14,
31 }
32
33
34 /// <summary>
35 /// Summary description for STPPerformanceCounter.
36 /// </summary>
37 internal class STPPerformanceCounter
38 {
39 // Fields
40 private PerformanceCounterType _pcType;
41 protected string _counterHelp;
42 protected string _counterName;
43
44 // Methods
45 public STPPerformanceCounter(
46 string counterName,
47 string counterHelp,
48 PerformanceCounterType pcType)
49 {
50 this._counterName = counterName;
51 this._counterHelp = counterHelp;
52 this._pcType = pcType;
53 }
54
55 public void AddCounterToCollection(CounterCreationDataCollection counterData)
56 {
57 CounterCreationData counterCreationData = new CounterCreationData(
58 _counterName,
59 _counterHelp,
60 _pcType);
61
62 counterData.Add(counterCreationData);
63 }
64
65 // Properties
66 public string Name
67 {
68 get
69 {
70 return _counterName;
71 }
72 }
73 }
74
75 internal class STPPerformanceCounters
76 {
77 // Fields
78 internal STPPerformanceCounter[] _stpPerformanceCounters;
79 private static STPPerformanceCounters _instance;
80 internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
81 internal const string _stpCategoryName = "SmartThreadPool";
82
83 // Methods
84 static STPPerformanceCounters()
85 {
86 _instance = new STPPerformanceCounters();
87 }
88
89 private STPPerformanceCounters()
90 {
91 STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[]
92 {
93 new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32),
94 new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32),
95 new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32),
96 new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction),
97 new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase),
98
99 new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32),
100 new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32),
101 new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32),
102
103 new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32),
104 new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32),
105
106 new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64),
107 new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase),
108
109 new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64),
110 new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase),
111
112 new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32),
113 };
114
115 _stpPerformanceCounters = stpPerformanceCounters;
116 SetupCategory();
117 }
118
119 private void SetupCategory()
120 {
121 if (!PerformanceCounterCategory.Exists(_stpCategoryName))
122 {
123 CounterCreationDataCollection counters = new CounterCreationDataCollection();
124
125 for (int i = 0; i < _stpPerformanceCounters.Length; i++)
126 {
127 _stpPerformanceCounters[i].AddCounterToCollection(counters);
128 }
129
130
131 // *********** Remark for .NET 2.0 ***********
132 // If you are here, it means you got the warning that this overload
133 // of the method is deprecated in .NET 2.0. To use the correct
134 // method overload, uncomment the third argument of the method.
135 PerformanceCounterCategory.Create(
136 _stpCategoryName,
137 _stpCategoryHelp,
138 //PerformanceCounterCategoryType.MultiInstance,
139 counters);
140
141 }
142 }
143
144 // Properties
145 public static STPPerformanceCounters Instance
146 {
147 get
148 {
149 return _instance;
150 }
151 }
152 }
153
154 internal class STPInstancePerformanceCounter : IDisposable
155 {
156 // Fields
157 private PerformanceCounter _pcs;
158
159 // Methods
160 protected STPInstancePerformanceCounter()
161 {
162 }
163
164 public STPInstancePerformanceCounter(
165 string instance,
166 STPPerformanceCounterType spcType)
167 {
168 STPPerformanceCounters counters = STPPerformanceCounters.Instance;
169 _pcs = new PerformanceCounter(
170 STPPerformanceCounters._stpCategoryName,
171 counters._stpPerformanceCounters[(int) spcType].Name,
172 instance,
173 false);
174 _pcs.RawValue = _pcs.RawValue;
175 }
176
177 ~STPInstancePerformanceCounter()
178 {
179 Close();
180 }
181
182 public void Close()
183 {
184 if (_pcs != null)
185 {
186 _pcs.RemoveInstance();
187 _pcs.Close();
188 _pcs = null;
189 }
190 }
191
192 public void Dispose()
193 {
194 Close();
195 GC.SuppressFinalize(this);
196 }
197
198 public virtual void Increment()
199 {
200 _pcs.Increment();
201 }
202
203 public virtual void IncrementBy(long val)
204 {
205 _pcs.IncrementBy(val);
206 }
207
208 public virtual void Set(long val)
209 {
210 _pcs.RawValue = val;
211 }
212 }
213
214 internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
215 {
216 // Methods
217 public STPInstanceNullPerformanceCounter() {}
218 public override void Increment() {}
219 public override void IncrementBy(long value) {}
220 public override void Set(long val) {}
221 }
222
223 internal interface ISTPInstancePerformanceCounters : IDisposable
224 {
225 void Close();
226 void SampleThreads(long activeThreads, long inUseThreads);
227 void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
228 void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
229 void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
230 }
231
232
233 internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
234 {
235 // Fields
236 private STPInstancePerformanceCounter[] _pcs;
237 private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
238
239 // Methods
240 static STPInstancePerformanceCounters()
241 {
242 _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter();
243 }
244
245 public STPInstancePerformanceCounters(string instance)
246 {
247 _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
248 STPPerformanceCounters counters = STPPerformanceCounters.Instance;
249 for (int i = 0; i < _pcs.Length; i++)
250 {
251 if (instance != null)
252 {
253 _pcs[i] = new STPInstancePerformanceCounter(
254 instance,
255 (STPPerformanceCounterType) i);
256 }
257 else
258 {
259 _pcs[i] = _stpInstanceNullPerformanceCounter;
260 }
261 }
262 }
263
264
265 public void Close()
266 {
267 if (null != _pcs)
268 {
269 for (int i = 0; i < _pcs.Length; i++)
270 {
271 if (null != _pcs[i])
272 {
273 _pcs[i].Close();
274 }
275 }
276 _pcs = null;
277 }
278 }
279
280 ~STPInstancePerformanceCounters()
281 {
282 Close();
283 }
284
285 public void Dispose()
286 {
287 Close();
288 GC.SuppressFinalize(this);
289 }
290
291 private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
292 {
293 return _pcs[(int) spcType];
294 }
295
296 public void SampleThreads(long activeThreads, long inUseThreads)
297 {
298 GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads);
299 GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads);
300 GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads);
301
302 GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads);
303 GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads);
304 }
305
306 public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
307 {
308 GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed);
309 GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued);
310 GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed);
311
312 GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued);
313 GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed);
314 }
315
316 public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
317 {
318 GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds);
319 GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment();
320 }
321
322 public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
323 {
324 GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds);
325 GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
326 }
327 }
328
329 internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
330 {
331 static NullSTPInstancePerformanceCounters()
332 {
333 }
334
335 private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null);
336
337 public static NullSTPInstancePerformanceCounters Instance
338 {
339 get { return _instance; }
340 }
341
342 public NullSTPInstancePerformanceCounters(string instance) {}
343 public void Close() {}
344 public void Dispose() {}
345
346 public void SampleThreads(long activeThreads, long inUseThreads) {}
347 public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
348 public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
349 public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
350 }
351
352}
diff --git a/ThirdParty/SmartThreadPool/STPStartInfo.cs b/ThirdParty/SmartThreadPool/STPStartInfo.cs
new file mode 100644
index 0000000..d181563
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/STPStartInfo.cs
@@ -0,0 +1,99 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System.Threading;
5
6namespace Amib.Threading
7{
8 /// <summary>
9 /// Summary description for STPStartInfo.
10 /// </summary>
11 public class STPStartInfo : WIGStartInfo
12 {
13 /// <summary>
14 /// Idle timeout in milliseconds.
15 /// If a thread is idle for _idleTimeout milliseconds then
16 /// it may quit.
17 /// </summary>
18 private int _idleTimeout;
19
20 /// <summary>
21 /// The lower limit of threads in the pool.
22 /// </summary>
23 private int _minWorkerThreads;
24
25 /// <summary>
26 /// The upper limit of threads in the pool.
27 /// </summary>
28 private int _maxWorkerThreads;
29
30 /// <summary>
31 /// The priority of the threads in the pool
32 /// </summary>
33 private ThreadPriority _threadPriority;
34
35 /// <summary>
36 /// If this field is not null then the performance counters are enabled
37 /// and use the string as the name of the instance.
38 /// </summary>
39 private string _pcInstanceName;
40
41 private int _stackSize;
42
43 public STPStartInfo() : base()
44 {
45 _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
46 _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
47 _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
48 _threadPriority = SmartThreadPool.DefaultThreadPriority;
49 _pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
50 _stackSize = SmartThreadPool.DefaultStackSize;
51 }
52
53 public STPStartInfo(STPStartInfo stpStartInfo) : base(stpStartInfo)
54 {
55 _idleTimeout = stpStartInfo._idleTimeout;
56 _minWorkerThreads = stpStartInfo._minWorkerThreads;
57 _maxWorkerThreads = stpStartInfo._maxWorkerThreads;
58 _threadPriority = stpStartInfo._threadPriority;
59 _pcInstanceName = stpStartInfo._pcInstanceName;
60 _stackSize = stpStartInfo._stackSize;
61 }
62
63 public int IdleTimeout
64 {
65 get { return _idleTimeout; }
66 set { _idleTimeout = value; }
67 }
68
69 public int MinWorkerThreads
70 {
71 get { return _minWorkerThreads; }
72 set { _minWorkerThreads = value; }
73 }
74
75 public int MaxWorkerThreads
76 {
77 get { return _maxWorkerThreads; }
78 set { _maxWorkerThreads = value; }
79 }
80
81 public ThreadPriority ThreadPriority
82 {
83 get { return _threadPriority; }
84 set { _threadPriority = value; }
85 }
86
87 public string PerformanceCounterInstanceName
88 {
89 get { return _pcInstanceName; }
90 set { _pcInstanceName = value; }
91 }
92
93 public int StackSize
94 {
95 get { return _stackSize; }
96 set { _stackSize = value; }
97 }
98 }
99}
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}
diff --git a/ThirdParty/SmartThreadPool/WIGStartInfo.cs b/ThirdParty/SmartThreadPool/WIGStartInfo.cs
new file mode 100644
index 0000000..150317f
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WIGStartInfo.cs
@@ -0,0 +1,99 @@
1// Ami Bar
2// amibar@gmail.com
3
4namespace Amib.Threading
5{
6 /// <summary>
7 /// Summary description for WIGStartInfo.
8 /// </summary>
9 public class WIGStartInfo
10 {
11 /// <summary>
12 /// Use the caller's security context
13 /// </summary>
14 private bool _useCallerCallContext;
15
16 /// <summary>
17 /// Use the caller's HTTP context
18 /// </summary>
19 private bool _useCallerHttpContext;
20
21 /// <summary>
22 /// Dispose of the state object of a work item
23 /// </summary>
24 private bool _disposeOfStateObjects;
25
26 /// <summary>
27 /// The option to run the post execute
28 /// </summary>
29 private CallToPostExecute _callToPostExecute;
30
31 /// <summary>
32 /// A post execute callback to call when none is provided in
33 /// the QueueWorkItem method.
34 /// </summary>
35 private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
36
37 /// <summary>
38 /// Indicate the WorkItemsGroup to suspend the handling of the work items
39 /// until the Start() method is called.
40 /// </summary>
41 private bool _startSuspended;
42
43 public WIGStartInfo()
44 {
45 _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
46 _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
47 _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
48 _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
49 _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
50 _startSuspended = SmartThreadPool.DefaultStartSuspended;
51 }
52
53 public WIGStartInfo(WIGStartInfo wigStartInfo)
54 {
55 _useCallerCallContext = wigStartInfo._useCallerCallContext;
56 _useCallerHttpContext = wigStartInfo._useCallerHttpContext;
57 _disposeOfStateObjects = wigStartInfo._disposeOfStateObjects;
58 _callToPostExecute = wigStartInfo._callToPostExecute;
59 _postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback;
60 _startSuspended = wigStartInfo._startSuspended;
61 }
62
63 public bool UseCallerCallContext
64 {
65 get { return _useCallerCallContext; }
66 set { _useCallerCallContext = value; }
67 }
68
69 public bool UseCallerHttpContext
70 {
71 get { return _useCallerHttpContext; }
72 set { _useCallerHttpContext = value; }
73 }
74
75 public bool DisposeOfStateObjects
76 {
77 get { return _disposeOfStateObjects; }
78 set { _disposeOfStateObjects = value; }
79 }
80
81 public CallToPostExecute CallToPostExecute
82 {
83 get { return _callToPostExecute; }
84 set { _callToPostExecute = value; }
85 }
86
87 public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
88 {
89 get { return _postExecuteWorkItemCallback; }
90 set { _postExecuteWorkItemCallback = value; }
91 }
92
93 public bool StartSuspended
94 {
95 get { return _startSuspended; }
96 set { _startSuspended = value; }
97 }
98 }
99}
diff --git a/ThirdParty/SmartThreadPool/WorkItem.cs b/ThirdParty/SmartThreadPool/WorkItem.cs
new file mode 100644
index 0000000..d0c0524
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItem.cs
@@ -0,0 +1,1035 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System;
5using System.Threading;
6using System.Diagnostics;
7
8namespace Amib.Threading.Internal
9{
10 #region WorkItem Delegate
11
12 /// <summary>
13 /// An internal delegate to call when the WorkItem starts or completes
14 /// </summary>
15 internal delegate void WorkItemStateCallback(WorkItem workItem);
16
17 #endregion
18
19 #region IInternalWorkItemResult interface
20
21 public class CanceledWorkItemsGroup
22 {
23 public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup();
24
25 private bool _isCanceled = false;
26 public bool IsCanceled
27 {
28 get { return _isCanceled; }
29 set { _isCanceled = value; }
30 }
31 }
32
33 internal interface IInternalWorkItemResult
34 {
35 event WorkItemStateCallback OnWorkItemStarted;
36 event WorkItemStateCallback OnWorkItemCompleted;
37 }
38
39 #endregion
40
41 #region IWorkItem interface
42
43 public interface IWorkItem
44 {
45
46 }
47
48 #endregion
49
50 #region WorkItem class
51
52 /// <summary>
53 /// Holds a callback delegate and the state for that delegate.
54 /// </summary>
55 public class WorkItem : IHasWorkItemPriority, IWorkItem
56 {
57 #region WorkItemState enum
58
59 /// <summary>
60 /// Indicates the state of the work item in the thread pool
61 /// </summary>
62 private enum WorkItemState
63 {
64 InQueue,
65 InProgress,
66 Completed,
67 Canceled,
68 }
69
70 #endregion
71
72 #region Member Variables
73
74 public Thread currentThread;
75
76 /// <summary>
77 /// Callback delegate for the callback.
78 /// </summary>
79 private WorkItemCallback _callback;
80
81 /// <summary>
82 /// State with which to call the callback delegate.
83 /// </summary>
84 private object _state;
85
86 /// <summary>
87 /// Stores the caller's context
88 /// </summary>
89 private CallerThreadContext _callerContext;
90
91 /// <summary>
92 /// Holds the result of the mehtod
93 /// </summary>
94 private object _result;
95
96 /// <summary>
97 /// Hold the exception if the method threw it
98 /// </summary>
99 private Exception _exception;
100
101 /// <summary>
102 /// Hold the state of the work item
103 /// </summary>
104 private WorkItemState _workItemState;
105
106 /// <summary>
107 /// A ManualResetEvent to indicate that the result is ready
108 /// </summary>
109 private ManualResetEvent _workItemCompleted;
110
111 /// <summary>
112 /// A reference count to the _workItemCompleted.
113 /// When it reaches to zero _workItemCompleted is Closed
114 /// </summary>
115 private int _workItemCompletedRefCount;
116
117 /// <summary>
118 /// Represents the result state of the work item
119 /// </summary>
120 private WorkItemResult _workItemResult;
121
122 /// <summary>
123 /// Work item info
124 /// </summary>
125 private WorkItemInfo _workItemInfo;
126
127 /// <summary>
128 /// Called when the WorkItem starts
129 /// </summary>
130 private event WorkItemStateCallback _workItemStartedEvent;
131
132 /// <summary>
133 /// Called when the WorkItem completes
134 /// </summary>
135 private event WorkItemStateCallback _workItemCompletedEvent;
136
137 /// <summary>
138 /// A reference to an object that indicates whatever the
139 /// WorkItemsGroup has been canceled
140 /// </summary>
141 private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
142
143 /// <summary>
144 /// The work item group this work item belong to.
145 ///
146 /// </summary>
147 private IWorkItemsGroup _workItemsGroup;
148
149 #region Performance Counter fields
150
151 /// <summary>
152 /// The time when the work items is queued.
153 /// Used with the performance counter.
154 /// </summary>
155 private DateTime _queuedTime;
156
157 /// <summary>
158 /// The time when the work items starts its execution.
159 /// Used with the performance counter.
160 /// </summary>
161 private DateTime _beginProcessTime;
162
163 /// <summary>
164 /// The time when the work items ends its execution.
165 /// Used with the performance counter.
166 /// </summary>
167 private DateTime _endProcessTime;
168
169 #endregion
170
171 #endregion
172
173 #region Properties
174
175 public TimeSpan WaitingTime
176 {
177 get
178 {
179 return (_beginProcessTime - _queuedTime);
180 }
181 }
182
183 public TimeSpan ProcessTime
184 {
185 get
186 {
187 return (_endProcessTime - _beginProcessTime);
188 }
189 }
190
191 #endregion
192
193 #region Construction
194
195 /// <summary>
196 /// Initialize the callback holding object.
197 /// </summary>
198 /// <param name="callback">Callback delegate for the callback.</param>
199 /// <param name="state">State with which to call the callback delegate.</param>
200 ///
201 /// We assume that the WorkItem object is created within the thread
202 /// that meant to run the callback
203 public WorkItem(
204 IWorkItemsGroup workItemsGroup,
205 WorkItemInfo workItemInfo,
206 WorkItemCallback callback,
207 object state)
208 {
209 _workItemsGroup = workItemsGroup;
210 _workItemInfo = workItemInfo;
211
212 if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
213 {
214 _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
215 }
216
217 _callback = callback;
218 _state = state;
219 _workItemResult = new WorkItemResult(this);
220 Initialize();
221 }
222
223 internal void Initialize()
224 {
225 _workItemState = WorkItemState.InQueue;
226 _workItemCompleted = null;
227 _workItemCompletedRefCount = 0;
228 }
229
230 internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup)
231 {
232 return (workItemsGroup == _workItemsGroup);
233 }
234
235
236 #endregion
237
238 #region Methods
239
240 public CanceledWorkItemsGroup CanceledWorkItemsGroup
241 {
242 get
243 {
244 return _canceledWorkItemsGroup;
245 }
246
247 set
248 {
249 _canceledWorkItemsGroup = value;
250 }
251 }
252
253 /// <summary>
254 /// Change the state of the work item to in progress if it wasn't canceled.
255 /// </summary>
256 /// <returns>
257 /// Return true on success or false in case the work item was canceled.
258 /// If the work item needs to run a post execute then the method will return true.
259 /// </returns>
260 public bool StartingWorkItem()
261 {
262 _beginProcessTime = DateTime.Now;
263
264 lock(this)
265 {
266 if (IsCanceled)
267 {
268 bool result = false;
269 if ((_workItemInfo.PostExecuteWorkItemCallback != null) &&
270 ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled))
271 {
272 result = true;
273 }
274
275 return result;
276 }
277
278 Debug.Assert(WorkItemState.InQueue == GetWorkItemState());
279
280 SetWorkItemState(WorkItemState.InProgress);
281 }
282
283 return true;
284 }
285
286 /// <summary>
287 /// Execute the work item and the post execute
288 /// </summary>
289 public void Execute()
290 {
291 CallToPostExecute currentCallToPostExecute = 0;
292
293 // Execute the work item if we are in the correct state
294 switch(GetWorkItemState())
295 {
296 case WorkItemState.InProgress:
297 currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled;
298 ExecuteWorkItem();
299 break;
300 case WorkItemState.Canceled:
301 currentCallToPostExecute |= CallToPostExecute.WhenWorkItemCanceled;
302 break;
303 default:
304 Debug.Assert(false);
305 throw new NotSupportedException();
306 }
307
308 // Run the post execute as needed
309 if ((currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0)
310 {
311 PostExecute();
312 }
313
314 _endProcessTime = DateTime.Now;
315 }
316
317 internal void FireWorkItemCompleted()
318 {
319 try
320 {
321 if (null != _workItemCompletedEvent)
322 {
323 _workItemCompletedEvent(this);
324 }
325 }
326 catch // Ignore exceptions
327 {}
328 }
329
330 /// <summary>
331 /// Execute the work item
332 /// </summary>
333 private void ExecuteWorkItem()
334 {
335 CallerThreadContext ctc = null;
336 if (null != _callerContext)
337 {
338 ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
339 CallerThreadContext.Apply(_callerContext);
340 }
341
342 Exception exception = null;
343 object result = null;
344
345 try
346 {
347 result = _callback(_state);
348 }
349 catch (Exception e)
350 {
351 // Save the exception so we can rethrow it later
352 exception = e;
353 }
354
355 if (null != _callerContext)
356 {
357 CallerThreadContext.Apply(ctc);
358 }
359
360 SetResult(result, exception);
361 }
362
363 /// <summary>
364 /// Runs the post execute callback
365 /// </summary>
366 private void PostExecute()
367 {
368 if (null != _workItemInfo.PostExecuteWorkItemCallback)
369 {
370 try
371 {
372 _workItemInfo.PostExecuteWorkItemCallback(this._workItemResult);
373 }
374 catch (Exception e)
375 {
376 Debug.Assert(null != e);
377 }
378 }
379 }
380
381 /// <summary>
382 /// Set the result of the work item to return
383 /// </summary>
384 /// <param name="result">The result of the work item</param>
385 internal void SetResult(object result, Exception exception)
386 {
387 _result = result;
388 _exception = exception;
389 SignalComplete(false);
390 }
391
392 /// <summary>
393 /// Returns the work item result
394 /// </summary>
395 /// <returns>The work item result</returns>
396 internal IWorkItemResult GetWorkItemResult()
397 {
398 return _workItemResult;
399 }
400
401 /// <summary>
402 /// Wait for all work items to complete
403 /// </summary>
404 /// <param name="workItemResults">Array of work item result objects</param>
405 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
406 /// <param name="exitContext">
407 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
408 /// </param>
409 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
410 /// <returns>
411 /// true when every work item in workItemResults has completed; otherwise false.
412 /// </returns>
413 internal static bool WaitAll(
414 IWorkItemResult [] workItemResults,
415 int millisecondsTimeout,
416 bool exitContext,
417 WaitHandle cancelWaitHandle)
418 {
419 if (0 == workItemResults.Length)
420 {
421 return true;
422 }
423
424 bool success;
425 WaitHandle [] waitHandles = new WaitHandle[workItemResults.Length];;
426 GetWaitHandles(workItemResults, waitHandles);
427
428 if ((null == cancelWaitHandle) && (waitHandles.Length <= 64))
429 {
430 success = WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
431 }
432 else
433 {
434 success = true;
435 int millisecondsLeft = millisecondsTimeout;
436 DateTime start = DateTime.Now;
437
438 WaitHandle [] whs;
439 if (null != cancelWaitHandle)
440 {
441 whs = new WaitHandle [] { null, cancelWaitHandle };
442 }
443 else
444 {
445 whs = new WaitHandle [] { null };
446 }
447
448 bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
449 // Iterate over the wait handles and wait for each one to complete.
450 // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle
451 // won't affect it.
452 // Each iteration we update the time left for the timeout.
453 for(int i = 0; i < workItemResults.Length; ++i)
454 {
455 // WaitAny don't work with negative numbers
456 if (!waitInfinitely && (millisecondsLeft < 0))
457 {
458 success = false;
459 break;
460 }
461
462 whs[0] = waitHandles[i];
463 int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
464 if((result > 0) || (WaitHandle.WaitTimeout == result))
465 {
466 success = false;
467 break;
468 }
469
470 if(!waitInfinitely)
471 {
472 // Update the time left to wait
473 TimeSpan ts = DateTime.Now - start;
474 millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
475 }
476 }
477 }
478 // Release the wait handles
479 ReleaseWaitHandles(workItemResults);
480
481 return success;
482 }
483
484 /// <summary>
485 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
486 /// </summary>
487 /// <param name="workItemResults">Array of work item result objects</param>
488 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
489 /// <param name="exitContext">
490 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
491 /// </param>
492 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
493 /// <returns>
494 /// 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.
495 /// </returns>
496 internal static int WaitAny(
497 IWorkItemResult [] workItemResults,
498 int millisecondsTimeout,
499 bool exitContext,
500 WaitHandle cancelWaitHandle)
501 {
502 WaitHandle [] waitHandles = null;
503
504 if (null != cancelWaitHandle)
505 {
506 waitHandles = new WaitHandle[workItemResults.Length+1];
507 GetWaitHandles(workItemResults, waitHandles);
508 waitHandles[workItemResults.Length] = cancelWaitHandle;
509 }
510 else
511 {
512 waitHandles = new WaitHandle[workItemResults.Length];
513 GetWaitHandles(workItemResults, waitHandles);
514 }
515
516 int result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
517
518 // Treat cancel as timeout
519 if (null != cancelWaitHandle)
520 {
521 if (result == workItemResults.Length)
522 {
523 result = WaitHandle.WaitTimeout;
524 }
525 }
526
527 ReleaseWaitHandles(workItemResults);
528
529 return result;
530 }
531
532 /// <summary>
533 /// Fill an array of wait handles with the work items wait handles.
534 /// </summary>
535 /// <param name="workItemResults">An array of work item results</param>
536 /// <param name="waitHandles">An array of wait handles to fill</param>
537 private static void GetWaitHandles(
538 IWorkItemResult [] workItemResults,
539 WaitHandle [] waitHandles)
540 {
541 for(int i = 0; i < workItemResults.Length; ++i)
542 {
543 WorkItemResult wir = workItemResults[i] as WorkItemResult;
544 Debug.Assert(null != wir, "All workItemResults must be WorkItemResult objects");
545
546 waitHandles[i] = wir.GetWorkItem().GetWaitHandle();
547 }
548 }
549
550 /// <summary>
551 /// Release the work items' wait handles
552 /// </summary>
553 /// <param name="workItemResults">An array of work item results</param>
554 private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults)
555 {
556 for(int i = 0; i < workItemResults.Length; ++i)
557 {
558 WorkItemResult wir = workItemResults[i] as WorkItemResult;
559
560 wir.GetWorkItem().ReleaseWaitHandle();
561 }
562 }
563
564
565 #endregion
566
567 #region Private Members
568
569 private WorkItemState GetWorkItemState()
570 {
571 if (_canceledWorkItemsGroup.IsCanceled)
572 {
573 return WorkItemState.Canceled;
574 }
575 return _workItemState;
576
577 }
578 /// <summary>
579 /// Sets the work item's state
580 /// </summary>
581 /// <param name="workItemState">The state to set the work item to</param>
582 private void SetWorkItemState(WorkItemState workItemState)
583 {
584 lock(this)
585 {
586 _workItemState = workItemState;
587 }
588 }
589
590 /// <summary>
591 /// Signals that work item has been completed or canceled
592 /// </summary>
593 /// <param name="canceled">Indicates that the work item has been canceled</param>
594 private void SignalComplete(bool canceled)
595 {
596 SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed);
597 lock(this)
598 {
599 // If someone is waiting then signal.
600 if (null != _workItemCompleted)
601 {
602 _workItemCompleted.Set();
603 }
604 }
605 }
606
607 internal void WorkItemIsQueued()
608 {
609 _queuedTime = DateTime.Now;
610 }
611
612 #endregion
613
614 #region Members exposed by WorkItemResult
615
616 /// <summary>
617 /// Cancel the work item if it didn't start running yet.
618 /// </summary>
619 /// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
620 private bool Cancel()
621 {
622 lock(this)
623 {
624 switch(GetWorkItemState())
625 {
626 case WorkItemState.Canceled:
627 //Debug.WriteLine("Work item already canceled");
628 return true;
629 case WorkItemState.Completed:
630 case WorkItemState.InProgress:
631 //Debug.WriteLine("Work item cannot be canceled");
632 return false;
633 case WorkItemState.InQueue:
634 // Signal to the wait for completion that the work
635 // item has been completed (canceled). There is no
636 // reason to wait for it to get out of the queue
637 SignalComplete(true);
638 //Debug.WriteLine("Work item canceled");
639 return true;
640 }
641 }
642 return false;
643 }
644
645 /// <summary>
646 /// Get the result of the work item.
647 /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
648 /// In case of error the method throws and exception
649 /// </summary>
650 /// <returns>The result of the work item</returns>
651 private object GetResult(
652 int millisecondsTimeout,
653 bool exitContext,
654 WaitHandle cancelWaitHandle)
655 {
656 Exception e = null;
657 object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
658 if (null != e)
659 {
660 throw new WorkItemResultException("The work item caused an excpetion, see the inner exception for details", e);
661 }
662 return result;
663 }
664
665 /// <summary>
666 /// Get the result of the work item.
667 /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
668 /// In case of error the e argument is filled with the exception
669 /// </summary>
670 /// <returns>The result of the work item</returns>
671 private object GetResult(
672 int millisecondsTimeout,
673 bool exitContext,
674 WaitHandle cancelWaitHandle,
675 out Exception e)
676 {
677 e = null;
678
679 // Check for cancel
680 if (WorkItemState.Canceled == GetWorkItemState())
681 {
682 throw new WorkItemCancelException("Work item canceled");
683 }
684
685 // Check for completion
686 if (IsCompleted)
687 {
688 e = _exception;
689 return _result;
690 }
691
692 // If no cancelWaitHandle is provided
693 if (null == cancelWaitHandle)
694 {
695 WaitHandle wh = GetWaitHandle();
696
697 bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext);
698
699 ReleaseWaitHandle();
700
701 if (timeout)
702 {
703 throw new WorkItemTimeoutException("Work item timeout");
704 }
705 }
706 else
707 {
708 WaitHandle wh = GetWaitHandle();
709 int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
710 ReleaseWaitHandle();
711
712 switch(result)
713 {
714 case 0:
715 // The work item signaled
716 // Note that the signal could be also as a result of canceling the
717 // work item (not the get result)
718 break;
719 case 1:
720 case WaitHandle.WaitTimeout:
721 throw new WorkItemTimeoutException("Work item timeout");
722 default:
723 Debug.Assert(false);
724 break;
725
726 }
727 }
728
729 // Check for cancel
730 if (WorkItemState.Canceled == GetWorkItemState())
731 {
732 throw new WorkItemCancelException("Work item canceled");
733 }
734
735 Debug.Assert(IsCompleted);
736
737 e = _exception;
738
739 // Return the result
740 return _result;
741 }
742
743 /// <summary>
744 /// A wait handle to wait for completion, cancel, or timeout
745 /// </summary>
746 private WaitHandle GetWaitHandle()
747 {
748 lock(this)
749 {
750 if (null == _workItemCompleted)
751 {
752 _workItemCompleted = new ManualResetEvent(IsCompleted);
753 }
754 ++_workItemCompletedRefCount;
755 }
756 return _workItemCompleted;
757 }
758
759 private void ReleaseWaitHandle()
760 {
761 lock(this)
762 {
763 if (null != _workItemCompleted)
764 {
765 --_workItemCompletedRefCount;
766 if (0 == _workItemCompletedRefCount)
767 {
768 _workItemCompleted.Close();
769 _workItemCompleted = null;
770 }
771 }
772 }
773 }
774
775 /// <summary>
776 /// Returns true when the work item has completed or canceled
777 /// </summary>
778 private bool IsCompleted
779 {
780 get
781 {
782 lock(this)
783 {
784 WorkItemState workItemState = GetWorkItemState();
785 return ((workItemState == WorkItemState.Completed) ||
786 (workItemState == WorkItemState.Canceled));
787 }
788 }
789 }
790
791 /// <summary>
792 /// Returns true when the work item has canceled
793 /// </summary>
794 public bool IsCanceled
795 {
796 get
797 {
798 lock(this)
799 {
800 return (GetWorkItemState() == WorkItemState.Canceled);
801 }
802 }
803 }
804
805 #endregion
806
807 #region IHasWorkItemPriority Members
808
809 /// <summary>
810 /// Returns the priority of the work item
811 /// </summary>
812 public WorkItemPriority WorkItemPriority
813 {
814 get
815 {
816 return _workItemInfo.WorkItemPriority;
817 }
818 }
819
820 #endregion
821
822 internal event WorkItemStateCallback OnWorkItemStarted
823 {
824 add
825 {
826 _workItemStartedEvent += value;
827 }
828 remove
829 {
830 _workItemStartedEvent -= value;
831 }
832 }
833
834 internal event WorkItemStateCallback OnWorkItemCompleted
835 {
836 add
837 {
838 _workItemCompletedEvent += value;
839 }
840 remove
841 {
842 _workItemCompletedEvent -= value;
843 }
844 }
845
846
847 #region WorkItemResult class
848
849 private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult
850 {
851 /// <summary>
852 /// A back reference to the work item
853 /// </summary>
854 private WorkItem _workItem;
855
856 public WorkItemResult(WorkItem workItem)
857 {
858 _workItem = workItem;
859 }
860
861 internal WorkItem GetWorkItem()
862 {
863 return _workItem;
864 }
865
866 #region IWorkItemResult Members
867
868 public bool IsCompleted
869 {
870 get
871 {
872 return _workItem.IsCompleted;
873 }
874 }
875
876 public void Abort()
877 {
878 _workItem.Abort();
879 }
880
881 public bool IsCanceled
882 {
883 get
884 {
885 return _workItem.IsCanceled;
886 }
887 }
888
889 public object GetResult()
890 {
891 return _workItem.GetResult(Timeout.Infinite, true, null);
892 }
893
894 public object GetResult(int millisecondsTimeout, bool exitContext)
895 {
896 return _workItem.GetResult(millisecondsTimeout, exitContext, null);
897 }
898
899 public object GetResult(TimeSpan timeout, bool exitContext)
900 {
901 return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null);
902 }
903
904 public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
905 {
906 return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
907 }
908
909 public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
910 {
911 return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
912 }
913
914 public object GetResult(out Exception e)
915 {
916 return _workItem.GetResult(Timeout.Infinite, true, null, out e);
917 }
918
919 public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
920 {
921 return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e);
922 }
923
924 public object GetResult(TimeSpan timeout, bool exitContext, out Exception e)
925 {
926 return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e);
927 }
928
929 public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
930 {
931 return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
932 }
933
934 public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
935 {
936 return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e);
937 }
938
939 public bool Cancel()
940 {
941 return _workItem.Cancel();
942 }
943
944 public object State
945 {
946 get
947 {
948 return _workItem._state;
949 }
950 }
951
952 public WorkItemPriority WorkItemPriority
953 {
954 get
955 {
956 return _workItem._workItemInfo.WorkItemPriority;
957 }
958 }
959
960 /// <summary>
961 /// Return the result, same as GetResult()
962 /// </summary>
963 public object Result
964 {
965 get { return GetResult(); }
966 }
967
968 /// <summary>
969 /// Returns the exception if occured otherwise returns null.
970 /// This value is valid only after the work item completed,
971 /// before that it is always null.
972 /// </summary>
973 public object Exception
974 {
975 get { return _workItem._exception; }
976 }
977
978 #endregion
979
980 #region IInternalWorkItemResult Members
981
982 public event WorkItemStateCallback OnWorkItemStarted
983 {
984 add
985 {
986 _workItem.OnWorkItemStarted += value;
987 }
988 remove
989 {
990 _workItem.OnWorkItemStarted -= value;
991 }
992 }
993
994
995 public event WorkItemStateCallback OnWorkItemCompleted
996 {
997 add
998 {
999 _workItem.OnWorkItemCompleted += value;
1000 }
1001 remove
1002 {
1003 _workItem.OnWorkItemCompleted -= value;
1004 }
1005 }
1006
1007 #endregion
1008 }
1009
1010 #endregion
1011
1012 public void DisposeOfState()
1013 {
1014 if (_workItemInfo.DisposeOfStateObjects)
1015 {
1016 IDisposable disp = _state as IDisposable;
1017 if (null != disp)
1018 {
1019 disp.Dispose();
1020 _state = null;
1021 }
1022 }
1023 }
1024
1025 public void Abort()
1026 {
1027 lock (this)
1028 {
1029 if(currentThread != null)
1030 currentThread.Abort();
1031 }
1032 }
1033 }
1034 #endregion
1035}
diff --git a/ThirdParty/SmartThreadPool/WorkItemFactory.cs b/ThirdParty/SmartThreadPool/WorkItemFactory.cs
new file mode 100644
index 0000000..dfcb54f
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItemFactory.cs
@@ -0,0 +1,333 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System;
5
6namespace Amib.Threading.Internal
7{
8 #region WorkItemFactory class
9
10 public class WorkItemFactory
11 {
12 /// <summary>
13 /// Create a new work item
14 /// </summary>
15 /// <param name="wigStartInfo">Work item group start information</param>
16 /// <param name="callback">A callback to execute</param>
17 /// <returns>Returns a work item</returns>
18 public static WorkItem CreateWorkItem(
19 IWorkItemsGroup workItemsGroup,
20 WIGStartInfo wigStartInfo,
21 WorkItemCallback callback)
22 {
23 return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null);
24 }
25
26 /// <summary>
27 /// Create a new work item
28 /// </summary>
29 /// <param name="wigStartInfo">Work item group start information</param>
30 /// <param name="callback">A callback to execute</param>
31 /// <param name="workItemPriority">The priority of the work item</param>
32 /// <returns>Returns a work item</returns>
33 public static WorkItem CreateWorkItem(
34 IWorkItemsGroup workItemsGroup,
35 WIGStartInfo wigStartInfo,
36 WorkItemCallback callback,
37 WorkItemPriority workItemPriority)
38 {
39 return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority);
40 }
41
42 /// <summary>
43 /// Create a new work item
44 /// </summary>
45 /// <param name="wigStartInfo">Work item group start information</param>
46 /// <param name="workItemInfo">Work item info</param>
47 /// <param name="callback">A callback to execute</param>
48 /// <returns>Returns a work item</returns>
49 public static WorkItem CreateWorkItem(
50 IWorkItemsGroup workItemsGroup,
51 WIGStartInfo wigStartInfo,
52 WorkItemInfo workItemInfo,
53 WorkItemCallback callback)
54 {
55 return CreateWorkItem(
56 workItemsGroup,
57 wigStartInfo,
58 workItemInfo,
59 callback,
60 null);
61 }
62
63 /// <summary>
64 /// Create a new work item
65 /// </summary>
66 /// <param name="wigStartInfo">Work item group start information</param>
67 /// <param name="callback">A callback to execute</param>
68 /// <param name="state">
69 /// The context object of the work item. Used for passing arguments to the work item.
70 /// </param>
71 /// <returns>Returns a work item</returns>
72 public static WorkItem CreateWorkItem(
73 IWorkItemsGroup workItemsGroup,
74 WIGStartInfo wigStartInfo,
75 WorkItemCallback callback,
76 object state)
77 {
78 ValidateCallback(callback);
79
80 WorkItemInfo workItemInfo = new WorkItemInfo();
81 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
82 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
83 workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
84 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
85 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
86
87 WorkItem workItem = new WorkItem(
88 workItemsGroup,
89 workItemInfo,
90 callback,
91 state);
92 return workItem;
93 }
94
95 /// <summary>
96 /// Create a new work item
97 /// </summary>
98 /// <param name="wigStartInfo">Work item group start information</param>
99 /// <param name="callback">A callback to execute</param>
100 /// <param name="state">
101 /// The context object of the work item. Used for passing arguments to the work item.
102 /// </param>
103 /// <param name="workItemPriority">The work item priority</param>
104 /// <returns>Returns a work item</returns>
105 public static WorkItem CreateWorkItem(
106 IWorkItemsGroup workItemsGroup,
107 WIGStartInfo wigStartInfo,
108 WorkItemCallback callback,
109 object state,
110 WorkItemPriority workItemPriority)
111 {
112 ValidateCallback(callback);
113
114 WorkItemInfo workItemInfo = new WorkItemInfo();
115 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
116 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
117 workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
118 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
119 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
120 workItemInfo.WorkItemPriority = workItemPriority;
121
122 WorkItem workItem = new WorkItem(
123 workItemsGroup,
124 workItemInfo,
125 callback,
126 state);
127
128 return workItem;
129 }
130
131 /// <summary>
132 /// Create a new work item
133 /// </summary>
134 /// <param name="wigStartInfo">Work item group start information</param>
135 /// <param name="workItemInfo">Work item information</param>
136 /// <param name="callback">A callback to execute</param>
137 /// <param name="state">
138 /// The context object of the work item. Used for passing arguments to the work item.
139 /// </param>
140 /// <returns>Returns a work item</returns>
141 public static WorkItem CreateWorkItem(
142 IWorkItemsGroup workItemsGroup,
143 WIGStartInfo wigStartInfo,
144 WorkItemInfo workItemInfo,
145 WorkItemCallback callback,
146 object state)
147 {
148 ValidateCallback(callback);
149 ValidateCallback(workItemInfo.PostExecuteWorkItemCallback);
150
151 WorkItem workItem = new WorkItem(
152 workItemsGroup,
153 new WorkItemInfo(workItemInfo),
154 callback,
155 state);
156
157 return workItem;
158 }
159
160 /// <summary>
161 /// Create a new work item
162 /// </summary>
163 /// <param name="wigStartInfo">Work item group start information</param>
164 /// <param name="callback">A callback to execute</param>
165 /// <param name="state">
166 /// The context object of the work item. Used for passing arguments to the work item.
167 /// </param>
168 /// <param name="postExecuteWorkItemCallback">
169 /// A delegate to call after the callback completion
170 /// </param>
171 /// <returns>Returns a work item</returns>
172 public static WorkItem CreateWorkItem(
173 IWorkItemsGroup workItemsGroup,
174 WIGStartInfo wigStartInfo,
175 WorkItemCallback callback,
176 object state,
177 PostExecuteWorkItemCallback postExecuteWorkItemCallback)
178 {
179 ValidateCallback(callback);
180 ValidateCallback(postExecuteWorkItemCallback);
181
182 WorkItemInfo workItemInfo = new WorkItemInfo();
183 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
184 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
185 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
186 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
187 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
188
189 WorkItem workItem = new WorkItem(
190 workItemsGroup,
191 workItemInfo,
192 callback,
193 state);
194
195 return workItem;
196 }
197
198 /// <summary>
199 /// Create a new work item
200 /// </summary>
201 /// <param name="wigStartInfo">Work item group start information</param>
202 /// <param name="callback">A callback to execute</param>
203 /// <param name="state">
204 /// The context object of the work item. Used for passing arguments to the work item.
205 /// </param>
206 /// <param name="postExecuteWorkItemCallback">
207 /// A delegate to call after the callback completion
208 /// </param>
209 /// <param name="workItemPriority">The work item priority</param>
210 /// <returns>Returns a work item</returns>
211 public static WorkItem CreateWorkItem(
212 IWorkItemsGroup workItemsGroup,
213 WIGStartInfo wigStartInfo,
214 WorkItemCallback callback,
215 object state,
216 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
217 WorkItemPriority workItemPriority)
218 {
219 ValidateCallback(callback);
220 ValidateCallback(postExecuteWorkItemCallback);
221
222 WorkItemInfo workItemInfo = new WorkItemInfo();
223 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
224 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
225 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
226 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
227 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
228 workItemInfo.WorkItemPriority = workItemPriority;
229
230 WorkItem workItem = new WorkItem(
231 workItemsGroup,
232 workItemInfo,
233 callback,
234 state);
235
236 return workItem;
237 }
238
239 /// <summary>
240 /// Create a new work item
241 /// </summary>
242 /// <param name="wigStartInfo">Work item group start information</param>
243 /// <param name="callback">A callback to execute</param>
244 /// <param name="state">
245 /// The context object of the work item. Used for passing arguments to the work item.
246 /// </param>
247 /// <param name="postExecuteWorkItemCallback">
248 /// A delegate to call after the callback completion
249 /// </param>
250 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
251 /// <returns>Returns a work item</returns>
252 public static WorkItem CreateWorkItem(
253 IWorkItemsGroup workItemsGroup,
254 WIGStartInfo wigStartInfo,
255 WorkItemCallback callback,
256 object state,
257 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
258 CallToPostExecute callToPostExecute)
259 {
260 ValidateCallback(callback);
261 ValidateCallback(postExecuteWorkItemCallback);
262
263 WorkItemInfo workItemInfo = new WorkItemInfo();
264 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
265 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
266 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
267 workItemInfo.CallToPostExecute = callToPostExecute;
268 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
269
270 WorkItem workItem = new WorkItem(
271 workItemsGroup,
272 workItemInfo,
273 callback,
274 state);
275
276 return workItem;
277 }
278
279 /// <summary>
280 /// Create a new work item
281 /// </summary>
282 /// <param name="wigStartInfo">Work item group start information</param>
283 /// <param name="callback">A callback to execute</param>
284 /// <param name="state">
285 /// The context object of the work item. Used for passing arguments to the work item.
286 /// </param>
287 /// <param name="postExecuteWorkItemCallback">
288 /// A delegate to call after the callback completion
289 /// </param>
290 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
291 /// <param name="workItemPriority">The work item priority</param>
292 /// <returns>Returns a work item</returns>
293 public static WorkItem CreateWorkItem(
294 IWorkItemsGroup workItemsGroup,
295 WIGStartInfo wigStartInfo,
296 WorkItemCallback callback,
297 object state,
298 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
299 CallToPostExecute callToPostExecute,
300 WorkItemPriority workItemPriority)
301 {
302
303 ValidateCallback(callback);
304 ValidateCallback(postExecuteWorkItemCallback);
305
306 WorkItemInfo workItemInfo = new WorkItemInfo();
307 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
308 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
309 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
310 workItemInfo.CallToPostExecute = callToPostExecute;
311 workItemInfo.WorkItemPriority = workItemPriority;
312 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
313
314 WorkItem workItem = new WorkItem(
315 workItemsGroup,
316 workItemInfo,
317 callback,
318 state);
319
320 return workItem;
321 }
322
323 private static void ValidateCallback(Delegate callback)
324 {
325 if(callback.GetInvocationList().Length > 1)
326 {
327 throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
328 }
329 }
330 }
331
332 #endregion
333}
diff --git a/ThirdParty/SmartThreadPool/WorkItemInfo.cs b/ThirdParty/SmartThreadPool/WorkItemInfo.cs
new file mode 100644
index 0000000..c259339
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItemInfo.cs
@@ -0,0 +1,102 @@
1// Ami Bar
2// amibar@gmail.com
3
4namespace Amib.Threading
5{
6 #region WorkItemInfo class
7
8 /// <summary>
9 /// Summary description for WorkItemInfo.
10 /// </summary>
11 public class WorkItemInfo
12 {
13 /// <summary>
14 /// Use the caller's security context
15 /// </summary>
16 private bool _useCallerCallContext;
17
18 /// <summary>
19 /// Use the caller's security context
20 /// </summary>
21 private bool _useCallerHttpContext;
22
23 /// <summary>
24 /// Dispose of the state object of a work item
25 /// </summary>
26 private bool _disposeOfStateObjects;
27
28 /// <summary>
29 /// The option to run the post execute
30 /// </summary>
31 private CallToPostExecute _callToPostExecute;
32
33 /// <summary>
34 /// A post execute callback to call when none is provided in
35 /// the QueueWorkItem method.
36 /// </summary>
37 private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
38
39 /// <summary>
40 /// The priority of the work item
41 /// </summary>
42 private WorkItemPriority _workItemPriority;
43
44 public WorkItemInfo()
45 {
46 _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
47 _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
48 _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
49 _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
50 _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
51 _workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
52 }
53
54 public WorkItemInfo(WorkItemInfo workItemInfo)
55 {
56 _useCallerCallContext = workItemInfo._useCallerCallContext;
57 _useCallerHttpContext = workItemInfo._useCallerHttpContext;
58 _disposeOfStateObjects = workItemInfo._disposeOfStateObjects;
59 _callToPostExecute = workItemInfo._callToPostExecute;
60 _postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback;
61 _workItemPriority = workItemInfo._workItemPriority;
62 }
63
64 public bool UseCallerCallContext
65 {
66 get { return _useCallerCallContext; }
67 set { _useCallerCallContext = value; }
68 }
69
70 public bool UseCallerHttpContext
71 {
72 get { return _useCallerHttpContext; }
73 set { _useCallerHttpContext = value; }
74 }
75
76 public bool DisposeOfStateObjects
77 {
78 get { return _disposeOfStateObjects; }
79 set { _disposeOfStateObjects = value; }
80 }
81
82 public CallToPostExecute CallToPostExecute
83 {
84 get { return _callToPostExecute; }
85 set { _callToPostExecute = value; }
86 }
87
88 public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
89 {
90 get { return _postExecuteWorkItemCallback; }
91 set { _postExecuteWorkItemCallback = value; }
92 }
93
94 public WorkItemPriority WorkItemPriority
95 {
96 get { return _workItemPriority; }
97 set { _workItemPriority = value; }
98 }
99 }
100
101 #endregion
102}
diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs
new file mode 100644
index 0000000..01ac8dd
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs
@@ -0,0 +1,512 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System;
5using System.Threading;
6using System.Runtime.CompilerServices;
7using System.Diagnostics;
8
9namespace Amib.Threading.Internal
10{
11 #region WorkItemsGroup class
12
13 /// <summary>
14 /// Summary description for WorkItemsGroup.
15 /// </summary>
16 public class WorkItemsGroup : IWorkItemsGroup
17 {
18 #region Private members
19
20 private object _lock = new object();
21 /// <summary>
22 /// Contains the name of this instance of SmartThreadPool.
23 /// Can be changed by the user.
24 /// </summary>
25 private string _name = "WorkItemsGroup";
26
27 /// <summary>
28 /// A reference to the SmartThreadPool instance that created this
29 /// WorkItemsGroup.
30 /// </summary>
31 private SmartThreadPool _stp;
32
33 /// <summary>
34 /// The OnIdle event
35 /// </summary>
36 private event WorkItemsGroupIdleHandler _onIdle;
37
38 /// <summary>
39 /// Defines how many work items of this WorkItemsGroup can run at once.
40 /// </summary>
41 private int _concurrency;
42
43 /// <summary>
44 /// Priority queue to hold work items before they are passed
45 /// to the SmartThreadPool.
46 /// </summary>
47 private PriorityQueue _workItemsQueue;
48
49 /// <summary>
50 /// Indicate how many work items are waiting in the SmartThreadPool
51 /// queue.
52 /// This value is used to apply the concurrency.
53 /// </summary>
54 private int _workItemsInStpQueue;
55
56 /// <summary>
57 /// Indicate how many work items are currently running in the SmartThreadPool.
58 /// This value is used with the Cancel, to calculate if we can send new
59 /// work items to the STP.
60 /// </summary>
61 private int _workItemsExecutingInStp = 0;
62
63 /// <summary>
64 /// WorkItemsGroup start information
65 /// </summary>
66 private WIGStartInfo _workItemsGroupStartInfo;
67
68 /// <summary>
69 /// Signaled when all of the WorkItemsGroup's work item completed.
70 /// </summary>
71 private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
72
73 /// <summary>
74 /// A common object for all the work items that this work items group
75 /// generate so we can mark them to cancel in O(1)
76 /// </summary>
77 private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
78
79 #endregion
80
81 #region Construction
82
83 public WorkItemsGroup(
84 SmartThreadPool stp,
85 int concurrency,
86 WIGStartInfo wigStartInfo)
87 {
88 if (concurrency <= 0)
89 {
90 throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero");
91 }
92 _stp = stp;
93 _concurrency = concurrency;
94 _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo);
95 _workItemsQueue = new PriorityQueue();
96
97 // The _workItemsInStpQueue gets the number of currently executing work items,
98 // because once a work item is executing, it cannot be cancelled.
99 _workItemsInStpQueue = _workItemsExecutingInStp;
100 }
101
102 #endregion
103
104 #region IWorkItemsGroup implementation
105
106 /// <summary>
107 /// Get/Set the name of the SmartThreadPool instance
108 /// </summary>
109 public string Name
110 {
111 get
112 {
113 return _name;
114 }
115
116 set
117 {
118 _name = value;
119 }
120 }
121
122 /// <summary>
123 /// Queue a work item
124 /// </summary>
125 /// <param name="callback">A callback to execute</param>
126 /// <returns>Returns a work item result</returns>
127 public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
128 {
129 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback);
130 EnqueueToSTPNextWorkItem(workItem);
131 return workItem.GetWorkItemResult();
132 }
133
134 /// <summary>
135 /// Queue a work item
136 /// </summary>
137 /// <param name="callback">A callback to execute</param>
138 /// <param name="workItemPriority">The priority of the work item</param>
139 /// <returns>Returns a work item result</returns>
140 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
141 {
142 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority);
143 EnqueueToSTPNextWorkItem(workItem);
144 return workItem.GetWorkItemResult();
145 }
146
147 /// <summary>
148 /// Queue a work item
149 /// </summary>
150 /// <param name="workItemInfo">Work item info</param>
151 /// <param name="callback">A callback to execute</param>
152 /// <returns>Returns a work item result</returns>
153 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
154 {
155 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback);
156 EnqueueToSTPNextWorkItem(workItem);
157 return workItem.GetWorkItemResult();
158 }
159
160 /// <summary>
161 /// Queue a work item
162 /// </summary>
163 /// <param name="callback">A callback to execute</param>
164 /// <param name="state">
165 /// The context object of the work item. Used for passing arguments to the work item.
166 /// </param>
167 /// <returns>Returns a work item result</returns>
168 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
169 {
170 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state);
171 EnqueueToSTPNextWorkItem(workItem);
172 return workItem.GetWorkItemResult();
173 }
174
175 /// <summary>
176 /// Queue a work item
177 /// </summary>
178 /// <param name="callback">A callback to execute</param>
179 /// <param name="state">
180 /// The context object of the work item. Used for passing arguments to the work item.
181 /// </param>
182 /// <param name="workItemPriority">The work item priority</param>
183 /// <returns>Returns a work item result</returns>
184 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
185 {
186 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority);
187 EnqueueToSTPNextWorkItem(workItem);
188 return workItem.GetWorkItemResult();
189 }
190
191 /// <summary>
192 /// Queue a work item
193 /// </summary>
194 /// <param name="workItemInfo">Work item information</param>
195 /// <param name="callback">A callback to execute</param>
196 /// <param name="state">
197 /// The context object of the work item. Used for passing arguments to the work item.
198 /// </param>
199 /// <returns>Returns a work item result</returns>
200 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
201 {
202 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state);
203 EnqueueToSTPNextWorkItem(workItem);
204 return workItem.GetWorkItemResult();
205 }
206
207 /// <summary>
208 /// Queue a work item
209 /// </summary>
210 /// <param name="callback">A callback to execute</param>
211 /// <param name="state">
212 /// The context object of the work item. Used for passing arguments to the work item.
213 /// </param>
214 /// <param name="postExecuteWorkItemCallback">
215 /// A delegate to call after the callback completion
216 /// </param>
217 /// <returns>Returns a work item result</returns>
218 public IWorkItemResult QueueWorkItem(
219 WorkItemCallback callback,
220 object state,
221 PostExecuteWorkItemCallback postExecuteWorkItemCallback)
222 {
223 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback);
224 EnqueueToSTPNextWorkItem(workItem);
225 return workItem.GetWorkItemResult();
226 }
227
228 /// <summary>
229 /// Queue a work item
230 /// </summary>
231 /// <param name="callback">A callback to execute</param>
232 /// <param name="state">
233 /// The context object of the work item. Used for passing arguments to the work item.
234 /// </param>
235 /// <param name="postExecuteWorkItemCallback">
236 /// A delegate to call after the callback completion
237 /// </param>
238 /// <param name="workItemPriority">The work item priority</param>
239 /// <returns>Returns a work item result</returns>
240 public IWorkItemResult QueueWorkItem(
241 WorkItemCallback callback,
242 object state,
243 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
244 WorkItemPriority workItemPriority)
245 {
246 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
247 EnqueueToSTPNextWorkItem(workItem);
248 return workItem.GetWorkItemResult();
249 }
250
251 /// <summary>
252 /// Queue a work item
253 /// </summary>
254 /// <param name="callback">A callback to execute</param>
255 /// <param name="state">
256 /// The context object of the work item. Used for passing arguments to the work item.
257 /// </param>
258 /// <param name="postExecuteWorkItemCallback">
259 /// A delegate to call after the callback completion
260 /// </param>
261 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
262 /// <returns>Returns a work item result</returns>
263 public IWorkItemResult QueueWorkItem(
264 WorkItemCallback callback,
265 object state,
266 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
267 CallToPostExecute callToPostExecute)
268 {
269 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
270 EnqueueToSTPNextWorkItem(workItem);
271 return workItem.GetWorkItemResult();
272 }
273
274 /// <summary>
275 /// Queue a work item
276 /// </summary>
277 /// <param name="callback">A callback to execute</param>
278 /// <param name="state">
279 /// The context object of the work item. Used for passing arguments to the work item.
280 /// </param>
281 /// <param name="postExecuteWorkItemCallback">
282 /// A delegate to call after the callback completion
283 /// </param>
284 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
285 /// <param name="workItemPriority">The work item priority</param>
286 /// <returns>Returns a work item result</returns>
287 public IWorkItemResult QueueWorkItem(
288 WorkItemCallback callback,
289 object state,
290 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
291 CallToPostExecute callToPostExecute,
292 WorkItemPriority workItemPriority)
293 {
294 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
295 EnqueueToSTPNextWorkItem(workItem);
296 return workItem.GetWorkItemResult();
297 }
298
299 /// <summary>
300 /// Wait for the thread pool to be idle
301 /// </summary>
302 public void WaitForIdle()
303 {
304 WaitForIdle(Timeout.Infinite);
305 }
306
307 /// <summary>
308 /// Wait for the thread pool to be idle
309 /// </summary>
310 public bool WaitForIdle(TimeSpan timeout)
311 {
312 return WaitForIdle((int)timeout.TotalMilliseconds);
313 }
314
315 /// <summary>
316 /// Wait for the thread pool to be idle
317 /// </summary>
318 public bool WaitForIdle(int millisecondsTimeout)
319 {
320 _stp.ValidateWorkItemsGroupWaitForIdle(this);
321 return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
322 }
323
324 public int WaitingCallbacks
325 {
326 get
327 {
328 return _workItemsQueue.Count;
329 }
330 }
331
332 public event WorkItemsGroupIdleHandler OnIdle
333 {
334 add
335 {
336 _onIdle += value;
337 }
338 remove
339 {
340 _onIdle -= value;
341 }
342 }
343
344 public void Cancel()
345 {
346 lock(_lock)
347 {
348 _canceledWorkItemsGroup.IsCanceled = true;
349 _workItemsQueue.Clear();
350 _workItemsInStpQueue = 0;
351 _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
352 }
353 }
354
355 public void Start()
356 {
357 lock (this)
358 {
359 if (!_workItemsGroupStartInfo.StartSuspended)
360 {
361 return;
362 }
363 _workItemsGroupStartInfo.StartSuspended = false;
364 }
365
366 for(int i = 0; i < _concurrency; ++i)
367 {
368 EnqueueToSTPNextWorkItem(null, false);
369 }
370 }
371
372 #endregion
373
374 #region Private methods
375
376 private void RegisterToWorkItemCompletion(IWorkItemResult wir)
377 {
378 IInternalWorkItemResult iwir = wir as IInternalWorkItemResult;
379 iwir.OnWorkItemStarted += new WorkItemStateCallback(OnWorkItemStartedCallback);
380 iwir.OnWorkItemCompleted += new WorkItemStateCallback(OnWorkItemCompletedCallback);
381 }
382
383 public void OnSTPIsStarting()
384 {
385 lock (this)
386 {
387 if (_workItemsGroupStartInfo.StartSuspended)
388 {
389 return;
390 }
391 }
392
393 for(int i = 0; i < _concurrency; ++i)
394 {
395 EnqueueToSTPNextWorkItem(null, false);
396 }
397 }
398
399 private object FireOnIdle(object state)
400 {
401 FireOnIdleImpl(_onIdle);
402 return null;
403 }
404
405 [MethodImpl(MethodImplOptions.NoInlining)]
406 private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
407 {
408 if(null == onIdle)
409 {
410 return;
411 }
412
413 Delegate[] delegates = onIdle.GetInvocationList();
414 foreach(WorkItemsGroupIdleHandler eh in delegates)
415 {
416 try
417 {
418 eh(this);
419 }
420 // Ignore exceptions
421 catch{}
422 }
423 }
424
425 private void OnWorkItemStartedCallback(WorkItem workItem)
426 {
427 lock(_lock)
428 {
429 ++_workItemsExecutingInStp;
430 }
431 }
432
433 private void OnWorkItemCompletedCallback(WorkItem workItem)
434 {
435 EnqueueToSTPNextWorkItem(null, true);
436 }
437
438 private void EnqueueToSTPNextWorkItem(WorkItem workItem)
439 {
440 EnqueueToSTPNextWorkItem(workItem, false);
441 }
442
443 private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
444 {
445 lock(_lock)
446 {
447 // Got here from OnWorkItemCompletedCallback()
448 if (decrementWorkItemsInStpQueue)
449 {
450 --_workItemsInStpQueue;
451
452 if(_workItemsInStpQueue < 0)
453 {
454 _workItemsInStpQueue = 0;
455 }
456
457 --_workItemsExecutingInStp;
458
459 if(_workItemsExecutingInStp < 0)
460 {
461 _workItemsExecutingInStp = 0;
462 }
463 }
464
465 // If the work item is not null then enqueue it
466 if (null != workItem)
467 {
468 workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;
469
470 RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
471 _workItemsQueue.Enqueue(workItem);
472 //_stp.IncrementWorkItemsCount();
473
474 if ((1 == _workItemsQueue.Count) &&
475 (0 == _workItemsInStpQueue))
476 {
477 _stp.RegisterWorkItemsGroup(this);
478 Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle");
479 _isIdleWaitHandle.Reset();
480 }
481 }
482
483 // If the work items queue of the group is empty than quit
484 if (0 == _workItemsQueue.Count)
485 {
486 if (0 == _workItemsInStpQueue)
487 {
488 _stp.UnregisterWorkItemsGroup(this);
489 Trace.WriteLine("WorkItemsGroup " + Name + " is idle");
490 _isIdleWaitHandle.Set();
491 _stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle));
492 }
493 return;
494 }
495
496 if (!_workItemsGroupStartInfo.StartSuspended)
497 {
498 if (_workItemsInStpQueue < _concurrency)
499 {
500 WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
501 _stp.Enqueue(nextWorkItem, true);
502 ++_workItemsInStpQueue;
503 }
504 }
505 }
506 }
507
508 #endregion
509 }
510
511 #endregion
512}
diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
new file mode 100644
index 0000000..af5af07
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
@@ -0,0 +1,600 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System;
5using System.Threading;
6
7namespace Amib.Threading.Internal
8{
9 #region WorkItemsQueue class
10
11 /// <summary>
12 /// WorkItemsQueue class.
13 /// </summary>
14 public class WorkItemsQueue : IDisposable
15 {
16 #region Member variables
17
18 /// <summary>
19 /// Waiters queue (implemented as stack).
20 /// </summary>
21 private WaiterEntry _headWaiterEntry = new WaiterEntry();
22
23 /// <summary>
24 /// Waiters count
25 /// </summary>
26 private int _waitersCount = 0;
27
28 /// <summary>
29 /// Work items queue
30 /// </summary>
31 private PriorityQueue _workItems = new PriorityQueue();
32
33 /// <summary>
34 /// Indicate that work items are allowed to be queued
35 /// </summary>
36 private bool _isWorkItemsQueueActive = true;
37
38 /// <summary>
39 /// Each thread in the thread pool keeps its own waiter entry.
40 /// </summary>
41 [ThreadStatic]
42 private static WaiterEntry _waiterEntry;
43
44 /// <summary>
45 /// A flag that indicates if the WorkItemsQueue has been disposed.
46 /// </summary>
47 private bool _isDisposed = false;
48
49 #endregion
50
51 #region Public properties
52
53 /// <summary>
54 /// Returns the current number of work items in the queue
55 /// </summary>
56 public int Count
57 {
58 get
59 {
60 lock(this)
61 {
62 ValidateNotDisposed();
63 return _workItems.Count;
64 }
65 }
66 }
67
68 /// <summary>
69 /// Returns the current number of waiters
70 /// </summary>
71 public int WaitersCount
72 {
73 get
74 {
75 lock(this)
76 {
77 ValidateNotDisposed();
78 return _waitersCount;
79 }
80 }
81 }
82
83
84 #endregion
85
86 #region Public methods
87
88 /// <summary>
89 /// Enqueue a work item to the queue.
90 /// </summary>
91 public bool EnqueueWorkItem(WorkItem workItem)
92 {
93 // A work item cannot be null, since null is used in the
94 // WaitForWorkItem() method to indicate timeout or cancel
95 if (null == workItem)
96 {
97 throw new ArgumentNullException("workItem" , "workItem cannot be null");
98 }
99
100 bool enqueue = true;
101
102 // First check if there is a waiter waiting for work item. During
103 // the check, timed out waiters are ignored. If there is no
104 // waiter then the work item is queued.
105 lock(this)
106 {
107 ValidateNotDisposed();
108
109 if (!_isWorkItemsQueueActive)
110 {
111 return false;
112 }
113
114 while(_waitersCount > 0)
115 {
116 // Dequeue a waiter.
117 WaiterEntry waiterEntry = PopWaiter();
118
119 // Signal the waiter. On success break the loop
120 if (waiterEntry.Signal(workItem))
121 {
122 enqueue = false;
123 break;
124 }
125 }
126
127 if (enqueue)
128 {
129 // Enqueue the work item
130 _workItems.Enqueue(workItem);
131 }
132 }
133 return true;
134 }
135
136
137 /// <summary>
138 /// Waits for a work item or exits on timeout or cancel
139 /// </summary>
140 /// <param name="millisecondsTimeout">Timeout in milliseconds</param>
141 /// <param name="cancelEvent">Cancel wait handle</param>
142 /// <returns>Returns true if the resource was granted</returns>
143 public WorkItem DequeueWorkItem(
144 int millisecondsTimeout,
145 WaitHandle cancelEvent)
146 {
147 /// This method cause the caller to wait for a work item.
148 /// If there is at least one waiting work item then the
149 /// method returns immidiately with true.
150 ///
151 /// If there are no waiting work items then the caller
152 /// is queued between other waiters for a work item to arrive.
153 ///
154 /// If a work item didn't come within millisecondsTimeout or
155 /// the user canceled the wait by signaling the cancelEvent
156 /// then the method returns false to indicate that the caller
157 /// didn't get a work item.
158
159 WaiterEntry waiterEntry = null;
160 WorkItem workItem = null;
161
162 lock(this)
163 {
164 ValidateNotDisposed();
165
166 // If there are waiting work items then take one and return.
167 if (_workItems.Count > 0)
168 {
169 workItem = _workItems.Dequeue() as WorkItem;
170 return workItem;
171 }
172 // No waiting work items ...
173 else
174 {
175 // Get the wait entry for the waiters queue
176 waiterEntry = GetThreadWaiterEntry();
177
178 // Put the waiter with the other waiters
179 PushWaiter(waiterEntry);
180 }
181 }
182
183 // Prepare array of wait handle for the WaitHandle.WaitAny()
184 WaitHandle [] waitHandles = new WaitHandle [] {
185 waiterEntry.WaitHandle,
186 cancelEvent };
187
188 // Wait for an available resource, cancel event, or timeout.
189
190 // During the wait we are supposes to exit the synchronization
191 // domain. (Placing true as the third argument of the WaitAny())
192 // It just doesn't work, I don't know why, so I have lock(this)
193 // statments insted of one.
194
195 int index = WaitHandle.WaitAny(
196 waitHandles,
197 millisecondsTimeout,
198 true);
199
200 lock(this)
201 {
202 // success is true if it got a work item.
203 bool success = (0 == index);
204
205 // The timeout variable is used only for readability.
206 // (We treat cancel as timeout)
207 bool timeout = !success;
208
209 // On timeout update the waiterEntry that it is timed out
210 if (timeout)
211 {
212 // The Timeout() fails if the waiter has already been signaled
213 timeout = waiterEntry.Timeout();
214
215 // On timeout remove the waiter from the queue.
216 // Note that the complexity is O(1).
217 if(timeout)
218 {
219 RemoveWaiter(waiterEntry, false);
220 }
221
222 // Again readability
223 success = !timeout;
224 }
225
226 // On success return the work item
227 if (success)
228 {
229 workItem = waiterEntry.WorkItem;
230
231 if (null == workItem)
232 {
233 workItem = _workItems.Dequeue() as WorkItem;
234 }
235 }
236 }
237 // On failure return null.
238 return workItem;
239 }
240
241 /// <summary>
242 /// Cleanup the work items queue, hence no more work
243 /// items are allowed to be queue
244 /// </summary>
245 protected virtual void Cleanup()
246 {
247 lock(this)
248 {
249 // Deactivate only once
250 if (!_isWorkItemsQueueActive)
251 {
252 return;
253 }
254
255 // Don't queue more work items
256 _isWorkItemsQueueActive = false;
257
258 foreach(WorkItem workItem in _workItems)
259 {
260 workItem.DisposeOfState();
261 }
262
263 // Clear the work items that are already queued
264 _workItems.Clear();
265
266 // Note:
267 // I don't iterate over the queue and dispose of work items's states,
268 // since if a work item has a state object that is still in use in the
269 // application then I must not dispose it.
270
271 // Tell the waiters that they were timed out.
272 // It won't signal them to exit, but to ignore their
273 // next work item.
274 while(_waitersCount > 0)
275 {
276 WaiterEntry waiterEntry = PopWaiter();
277 waiterEntry.Timeout();
278 }
279 }
280 }
281
282 #endregion
283
284 #region Private methods
285
286 /// <summary>
287 /// Returns the WaiterEntry of the current thread
288 /// </summary>
289 /// <returns></returns>
290 /// In order to avoid creation and destuction of WaiterEntry
291 /// objects each thread has its own WaiterEntry object.
292 private WaiterEntry GetThreadWaiterEntry()
293 {
294 if (null == _waiterEntry)
295 {
296 _waiterEntry = new WaiterEntry();
297 }
298 _waiterEntry.Reset();
299 return _waiterEntry;
300 }
301
302 #region Waiters stack methods
303
304 /// <summary>
305 /// Push a new waiter into the waiter's stack
306 /// </summary>
307 /// <param name="newWaiterEntry">A waiter to put in the stack</param>
308 public void PushWaiter(WaiterEntry newWaiterEntry)
309 {
310 // Remove the waiter if it is already in the stack and
311 // update waiter's count as needed
312 RemoveWaiter(newWaiterEntry, false);
313
314 // If the stack is empty then newWaiterEntry is the new head of the stack
315 if (null == _headWaiterEntry._nextWaiterEntry)
316 {
317 _headWaiterEntry._nextWaiterEntry = newWaiterEntry;
318 newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
319
320 }
321 // If the stack is not empty then put newWaiterEntry as the new head
322 // of the stack.
323 else
324 {
325 // Save the old first waiter entry
326 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
327
328 // Update the links
329 _headWaiterEntry._nextWaiterEntry = newWaiterEntry;
330 newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry;
331 newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
332 oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry;
333 }
334
335 // Increment the number of waiters
336 ++_waitersCount;
337 }
338
339 /// <summary>
340 /// Pop a waiter from the waiter's stack
341 /// </summary>
342 /// <returns>Returns the first waiter in the stack</returns>
343 private WaiterEntry PopWaiter()
344 {
345 // Store the current stack head
346 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
347
348 // Store the new stack head
349 WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry;
350
351 // Update the old stack head list links and decrement the number
352 // waiters.
353 RemoveWaiter(oldFirstWaiterEntry, true);
354
355 // Update the new stack head
356 _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry;
357 if (null != newHeadWaiterEntry)
358 {
359 newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry;
360 }
361
362 // Return the old stack head
363 return oldFirstWaiterEntry;
364 }
365
366 /// <summary>
367 /// Remove a waiter from the stack
368 /// </summary>
369 /// <param name="waiterEntry">A waiter entry to remove</param>
370 /// <param name="popDecrement">If true the waiter count is always decremented</param>
371 private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement)
372 {
373 // Store the prev entry in the list
374 WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry;
375
376 // Store the next entry in the list
377 WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry;
378
379 // A flag to indicate if we need to decrement the waiters count.
380 // If we got here from PopWaiter then we must decrement.
381 // If we got here from PushWaiter then we decrement only if
382 // the waiter was already in the stack.
383 bool decrementCounter = popDecrement;
384
385 // Null the waiter's entry links
386 waiterEntry._prevWaiterEntry = null;
387 waiterEntry._nextWaiterEntry = null;
388
389 // If the waiter entry had a prev link then update it.
390 // It also means that the waiter is already in the list and we
391 // need to decrement the waiters count.
392 if (null != prevWaiterEntry)
393 {
394 prevWaiterEntry._nextWaiterEntry = nextWaiterEntry;
395 decrementCounter = true;
396 }
397
398 // If the waiter entry had a next link then update it.
399 // It also means that the waiter is already in the list and we
400 // need to decrement the waiters count.
401 if (null != nextWaiterEntry)
402 {
403 nextWaiterEntry._prevWaiterEntry = prevWaiterEntry;
404 decrementCounter = true;
405 }
406
407 // Decrement the waiters count if needed
408 if (decrementCounter)
409 {
410 --_waitersCount;
411 }
412 }
413
414 #endregion
415
416 #endregion
417
418 #region WaiterEntry class
419
420 // A waiter entry in the _waiters queue.
421 public class WaiterEntry : IDisposable
422 {
423 #region Member variables
424
425 /// <summary>
426 /// Event to signal the waiter that it got the work item.
427 /// </summary>
428 private AutoResetEvent _waitHandle = new AutoResetEvent(false);
429
430 /// <summary>
431 /// Flag to know if this waiter already quited from the queue
432 /// because of a timeout.
433 /// </summary>
434 private bool _isTimedout = false;
435
436 /// <summary>
437 /// Flag to know if the waiter was signaled and got a work item.
438 /// </summary>
439 private bool _isSignaled = false;
440
441 /// <summary>
442 /// A work item that passed directly to the waiter withou going
443 /// through the queue
444 /// </summary>
445 private WorkItem _workItem = null;
446
447 private bool _isDisposed = false;
448
449 // Linked list members
450 internal WaiterEntry _nextWaiterEntry = null;
451 internal WaiterEntry _prevWaiterEntry = null;
452
453 #endregion
454
455 #region Construction
456
457 public WaiterEntry()
458 {
459 Reset();
460 }
461
462 #endregion
463
464 #region Public methods
465
466 public WaitHandle WaitHandle
467 {
468 get { return _waitHandle; }
469 }
470
471 public WorkItem WorkItem
472 {
473 get
474 {
475 lock(this)
476 {
477 return _workItem;
478 }
479 }
480 }
481
482 /// <summary>
483 /// Signal the waiter that it got a work item.
484 /// </summary>
485 /// <returns>Return true on success</returns>
486 /// The method fails if Timeout() preceded its call
487 public bool Signal(WorkItem workItem)
488 {
489 lock(this)
490 {
491 if (!_isTimedout)
492 {
493 _workItem = workItem;
494 _isSignaled = true;
495 _waitHandle.Set();
496 return true;
497 }
498 }
499 return false;
500 }
501
502 /// <summary>
503 /// Mark the wait entry that it has been timed out
504 /// </summary>
505 /// <returns>Return true on success</returns>
506 /// The method fails if Signal() preceded its call
507 public bool Timeout()
508 {
509 lock(this)
510 {
511 // Time out can happen only if the waiter wasn't marked as
512 // signaled
513 if (!_isSignaled)
514 {
515 // We don't remove the waiter from the queue, the DequeueWorkItem
516 // method skips _waiters that were timed out.
517 _isTimedout = true;
518 return true;
519 }
520 }
521 return false;
522 }
523
524 /// <summary>
525 /// Reset the wait entry so it can be used again
526 /// </summary>
527 public void Reset()
528 {
529 _workItem = null;
530 _isTimedout = false;
531 _isSignaled = false;
532 _waitHandle.Reset();
533 }
534
535 /// <summary>
536 /// Free resources
537 /// </summary>
538 public void Close()
539 {
540 if (null != _waitHandle)
541 {
542 _waitHandle.Close();
543 _waitHandle = null;
544 }
545 }
546
547 #endregion
548
549 #region IDisposable Members
550
551 public void Dispose()
552 {
553 if (!_isDisposed)
554 {
555 Close();
556 _isDisposed = true;
557 }
558 }
559
560 ~WaiterEntry()
561 {
562 Dispose();
563 }
564
565 #endregion
566 }
567
568 #endregion
569
570 #region IDisposable Members
571
572 public void Dispose()
573 {
574 if (!_isDisposed)
575 {
576 Cleanup();
577 _isDisposed = true;
578 GC.SuppressFinalize(this);
579 }
580 }
581
582 ~WorkItemsQueue()
583 {
584 Cleanup();
585 }
586
587 private void ValidateNotDisposed()
588 {
589 if(_isDisposed)
590 {
591 throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
592 }
593 }
594
595 #endregion
596 }
597
598 #endregion
599}
600