diff options
author | Justin Clark-Casey (justincc) | 2013-05-01 19:01:43 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2013-05-01 19:01:43 +0100 |
commit | 206fb306a7820cf593570e35ddfa8e7c5a10e449 (patch) | |
tree | 0ef0fdf42ddc0b63224af52b62b0bad42f62e352 /ThirdParty/SmartThreadPool | |
parent | Fix CAPS to work like they should - do not send caps to the viewer if they're... (diff) | |
download | opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.zip opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.gz opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.bz2 opensim-SC_OLD-206fb306a7820cf593570e35ddfa8e7c5a10e449.tar.xz |
Update SmartThreadPool to latest version 2.2.3 with a major and minor change.
SmartThreadPool code comes from http://www.codeproject.com/Articles/7933/Smart-Thread-Pool
This version implements thread abort (via WorkItem.Cancel(true)), threadpool naming, max thread stack, etc. so we no longer need to manually patch those.
However, two changes have been made to stock 2.2.3.
Major change: WorkItem.Cancel(bool abortExecution) in our version does not succeed if the work item was in progress and thread abort was not specified.
This is to match previous behaviour where we handle co-operative termination via another mechanism rather than checking WorkItem.IsCanceled.
Minor change: Did not add STP's StopWatch implementation as this is only used WinCE and Silverlight and causes a build clash with System.Diagnostics.StopWatch
The reason for updating is to see if this improves http://opensimulator.org/mantis/view.php?id=6557 and http://opensimulator.org/mantis/view.php?id=6586
Diffstat (limited to 'ThirdParty/SmartThreadPool')
26 files changed, 6666 insertions, 4783 deletions
diff --git a/ThirdParty/SmartThreadPool/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/AssemblyInfo.cs deleted file mode 100644 index e2465b0..0000000 --- a/ThirdParty/SmartThreadPool/AssemblyInfo.cs +++ /dev/null | |||
@@ -1,61 +0,0 @@ | |||
1 | using System; | ||
2 | using System.Reflection; | ||
3 | using 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("0.7.6.*")] | ||
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 index 6ea53f6..2177241 100644 --- a/ThirdParty/SmartThreadPool/CallerThreadContext.cs +++ b/ThirdParty/SmartThreadPool/CallerThreadContext.cs | |||
@@ -1,223 +1,138 @@ | |||
1 | using System; | 1 | |
2 | using System.Diagnostics; | 2 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
3 | using System.Threading; | 3 | |
4 | using System.Reflection; | 4 | using System; |
5 | using System.Web; | 5 | using System.Diagnostics; |
6 | using System.Runtime.Remoting.Messaging; | 6 | using System.Threading; |
7 | 7 | using System.Reflection; | |
8 | 8 | using System.Web; | |
9 | namespace Amib.Threading | 9 | using System.Runtime.Remoting.Messaging; |
10 | { | 10 | |
11 | #region CallerThreadContext class | 11 | |
12 | 12 | namespace Amib.Threading.Internal | |
13 | /// <summary> | 13 | { |
14 | /// This class stores the caller call context in order to restore | 14 | #region CallerThreadContext class |
15 | /// it when the work item is executed in the thread pool environment. | 15 | |
16 | /// </summary> | 16 | /// <summary> |
17 | internal class CallerThreadContext | 17 | /// This class stores the caller call context in order to restore |
18 | { | 18 | /// it when the work item is executed in the thread pool environment. |
19 | #region Prepare reflection information | 19 | /// </summary> |
20 | 20 | internal class CallerThreadContext | |
21 | // Cached type information. | 21 | { |
22 | private static MethodInfo getLogicalCallContextMethodInfo = | 22 | #region Prepare reflection information |
23 | typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); | 23 | |
24 | 24 | // Cached type information. | |
25 | private static MethodInfo setLogicalCallContextMethodInfo = | 25 | private static readonly MethodInfo getLogicalCallContextMethodInfo = |
26 | typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); | 26 | typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); |
27 | 27 | ||
28 | private static string HttpContextSlotName = GetHttpContextSlotName(); | 28 | private static readonly MethodInfo setLogicalCallContextMethodInfo = |
29 | 29 | typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); | |
30 | private static string GetHttpContextSlotName() | 30 | |
31 | { | 31 | private static string HttpContextSlotName = GetHttpContextSlotName(); |
32 | FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); | 32 | |
33 | 33 | private static string GetHttpContextSlotName() | |
34 | if( fi != null ) | 34 | { |
35 | return (string)fi.GetValue(null); | 35 | FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); |
36 | else // Use the default "HttpContext" slot name | 36 | |
37 | return "HttpContext"; | 37 | if (fi != null) |
38 | } | 38 | { |
39 | 39 | return (string) fi.GetValue(null); | |
40 | #endregion | 40 | } |
41 | 41 | ||
42 | #region Private fields | 42 | return "HttpContext"; |
43 | 43 | } | |
44 | private HttpContext _httpContext = null; | 44 | |
45 | private LogicalCallContext _callContext = null; | 45 | #endregion |
46 | 46 | ||
47 | #endregion | 47 | #region Private fields |
48 | 48 | ||
49 | /// <summary> | 49 | private HttpContext _httpContext; |
50 | /// Constructor | 50 | private LogicalCallContext _callContext; |
51 | /// </summary> | 51 | |
52 | private CallerThreadContext() | 52 | #endregion |
53 | { | 53 | |
54 | } | 54 | /// <summary> |
55 | 55 | /// Constructor | |
56 | public bool CapturedCallContext | 56 | /// </summary> |
57 | { | 57 | private CallerThreadContext() |
58 | get | 58 | { |
59 | { | 59 | } |
60 | return (null != _callContext); | 60 | |
61 | } | 61 | public bool CapturedCallContext |
62 | } | 62 | { |
63 | 63 | get | |
64 | public bool CapturedHttpContext | 64 | { |
65 | { | 65 | return (null != _callContext); |
66 | get | 66 | } |
67 | { | 67 | } |
68 | return (null != _httpContext); | 68 | |
69 | } | 69 | public bool CapturedHttpContext |
70 | } | 70 | { |
71 | 71 | get | |
72 | /// <summary> | 72 | { |
73 | /// Captures the current thread context | 73 | return (null != _httpContext); |
74 | /// </summary> | 74 | } |
75 | /// <returns></returns> | 75 | } |
76 | public static CallerThreadContext Capture( | 76 | |
77 | bool captureCallContext, | 77 | /// <summary> |
78 | bool captureHttpContext) | 78 | /// Captures the current thread context |
79 | { | 79 | /// </summary> |
80 | Debug.Assert(captureCallContext || captureHttpContext); | 80 | /// <returns></returns> |
81 | 81 | public static CallerThreadContext Capture( | |
82 | CallerThreadContext callerThreadContext = new CallerThreadContext(); | 82 | bool captureCallContext, |
83 | 83 | bool captureHttpContext) | |
84 | // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() | 84 | { |
85 | // Capture Call Context | 85 | Debug.Assert(captureCallContext || captureHttpContext); |
86 | if(captureCallContext && (getLogicalCallContextMethodInfo != null)) | 86 | |
87 | { | 87 | CallerThreadContext callerThreadContext = new CallerThreadContext(); |
88 | callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); | 88 | |
89 | if (callerThreadContext._callContext != null) | 89 | // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() |
90 | { | 90 | // Capture Call Context |
91 | callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); | 91 | if(captureCallContext && (getLogicalCallContextMethodInfo != null)) |
92 | } | 92 | { |
93 | } | 93 | callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); |
94 | 94 | if (callerThreadContext._callContext != null) | |
95 | // Capture httpContext | 95 | { |
96 | if (captureHttpContext && (null != HttpContext.Current)) | 96 | callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); |
97 | { | 97 | } |
98 | callerThreadContext._httpContext = HttpContext.Current; | 98 | } |
99 | } | 99 | |
100 | 100 | // Capture httpContext | |
101 | return callerThreadContext; | 101 | if (captureHttpContext && (null != HttpContext.Current)) |
102 | } | 102 | { |
103 | 103 | callerThreadContext._httpContext = HttpContext.Current; | |
104 | /// <summary> | 104 | } |
105 | /// Applies the thread context stored earlier | 105 | |
106 | /// </summary> | 106 | return callerThreadContext; |
107 | /// <param name="callerThreadContext"></param> | 107 | } |
108 | public static void Apply(CallerThreadContext callerThreadContext) | 108 | |
109 | { | 109 | /// <summary> |
110 | if (null == callerThreadContext) | 110 | /// Applies the thread context stored earlier |
111 | { | 111 | /// </summary> |
112 | throw new ArgumentNullException("callerThreadContext"); | 112 | /// <param name="callerThreadContext"></param> |
113 | } | 113 | public static void Apply(CallerThreadContext callerThreadContext) |
114 | 114 | { | |
115 | // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() | 115 | if (null == callerThreadContext) |
116 | // Restore call context | 116 | { |
117 | if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) | 117 | throw new ArgumentNullException("callerThreadContext"); |
118 | { | 118 | } |
119 | setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); | 119 | |
120 | } | 120 | // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() |
121 | 121 | // Restore call context | |
122 | // Restore HttpContext | 122 | if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) |
123 | if (callerThreadContext._httpContext != null) | 123 | { |
124 | { | 124 | setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); |
125 | CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); | 125 | } |
126 | } | 126 | |
127 | } | 127 | // Restore HttpContext |
128 | } | 128 | if (callerThreadContext._httpContext != null) |
129 | 129 | { | |
130 | #endregion | 130 | HttpContext.Current = callerThreadContext._httpContext; |
131 | 131 | //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); | |
132 | } | 132 | } |
133 | 133 | } | |
134 | 134 | } | |
135 | /* | 135 | |
136 | // Ami Bar | 136 | #endregion |
137 | // amibar@gmail.com | 137 | } |
138 | 138 | #endif | |
139 | using System; | ||
140 | using System.Threading; | ||
141 | using System.Globalization; | ||
142 | using System.Security.Principal; | ||
143 | using System.Reflection; | ||
144 | using System.Runtime.Remoting.Contexts; | ||
145 | |||
146 | namespace 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/CanceledWorkItemsGroup.cs b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs new file mode 100644 index 0000000..4a2a3e7 --- /dev/null +++ b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs | |||
@@ -0,0 +1,14 @@ | |||
1 | namespace Amib.Threading.Internal | ||
2 | { | ||
3 | internal class CanceledWorkItemsGroup | ||
4 | { | ||
5 | public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup(); | ||
6 | |||
7 | public CanceledWorkItemsGroup() | ||
8 | { | ||
9 | IsCanceled = false; | ||
10 | } | ||
11 | |||
12 | public bool IsCanceled { get; set; } | ||
13 | } | ||
14 | } \ No newline at end of file | ||
diff --git a/ThirdParty/SmartThreadPool/EventWaitHandle.cs b/ThirdParty/SmartThreadPool/EventWaitHandle.cs new file mode 100644 index 0000000..70a1a29 --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandle.cs | |||
@@ -0,0 +1,104 @@ | |||
1 | #if (_WINDOWS_CE) | ||
2 | |||
3 | using System; | ||
4 | using System.Runtime.InteropServices; | ||
5 | using System.Threading; | ||
6 | |||
7 | namespace Amib.Threading.Internal | ||
8 | { | ||
9 | /// <summary> | ||
10 | /// EventWaitHandle class | ||
11 | /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation. | ||
12 | /// So I wrote this class to implement these two methods with some of their overloads. | ||
13 | /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny. | ||
14 | /// Note that this class doesn't even inherit from WaitHandle! | ||
15 | /// </summary> | ||
16 | public class STPEventWaitHandle | ||
17 | { | ||
18 | #region Public Constants | ||
19 | |||
20 | public const int WaitTimeout = Timeout.Infinite; | ||
21 | |||
22 | #endregion | ||
23 | |||
24 | #region Private External Constants | ||
25 | |||
26 | private const Int32 WAIT_FAILED = -1; | ||
27 | private const Int32 WAIT_TIMEOUT = 0x102; | ||
28 | private const UInt32 INFINITE = 0xFFFFFFFF; | ||
29 | |||
30 | #endregion | ||
31 | |||
32 | #region WaitAll and WaitAny | ||
33 | |||
34 | internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) | ||
35 | { | ||
36 | return waitHandle.WaitOne(millisecondsTimeout, exitContext); | ||
37 | } | ||
38 | |||
39 | private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles) | ||
40 | { | ||
41 | IntPtr[] nativeHandles = new IntPtr[waitHandles.Length]; | ||
42 | for (int i = 0; i < waitHandles.Length; i++) | ||
43 | { | ||
44 | nativeHandles[i] = waitHandles[i].Handle; | ||
45 | } | ||
46 | return nativeHandles; | ||
47 | } | ||
48 | |||
49 | public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) | ||
50 | { | ||
51 | uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; | ||
52 | |||
53 | IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); | ||
54 | |||
55 | int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout); | ||
56 | |||
57 | if (result == WAIT_TIMEOUT || result == WAIT_FAILED) | ||
58 | { | ||
59 | return false; | ||
60 | } | ||
61 | |||
62 | return true; | ||
63 | } | ||
64 | |||
65 | |||
66 | public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) | ||
67 | { | ||
68 | uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; | ||
69 | |||
70 | IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); | ||
71 | |||
72 | int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout); | ||
73 | |||
74 | if (result >= 0 && result < waitHandles.Length) | ||
75 | { | ||
76 | return result; | ||
77 | } | ||
78 | |||
79 | return -1; | ||
80 | } | ||
81 | |||
82 | public static int WaitAny(WaitHandle[] waitHandles) | ||
83 | { | ||
84 | return WaitAny(waitHandles, Timeout.Infinite, false); | ||
85 | } | ||
86 | |||
87 | public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) | ||
88 | { | ||
89 | int millisecondsTimeout = (int)timeout.TotalMilliseconds; | ||
90 | |||
91 | return WaitAny(waitHandles, millisecondsTimeout, false); | ||
92 | } | ||
93 | |||
94 | #endregion | ||
95 | |||
96 | #region External methods | ||
97 | |||
98 | [DllImport("coredll.dll", SetLastError = true)] | ||
99 | public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds); | ||
100 | |||
101 | #endregion | ||
102 | } | ||
103 | } | ||
104 | #endif \ No newline at end of file | ||
diff --git a/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs new file mode 100644 index 0000000..2f8c55b --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs | |||
@@ -0,0 +1,82 @@ | |||
1 | using System.Threading; | ||
2 | |||
3 | #if (_WINDOWS_CE) | ||
4 | using System; | ||
5 | using System.Runtime.InteropServices; | ||
6 | #endif | ||
7 | |||
8 | namespace Amib.Threading.Internal | ||
9 | { | ||
10 | /// <summary> | ||
11 | /// EventWaitHandleFactory class. | ||
12 | /// This is a static class that creates AutoResetEvent and ManualResetEvent objects. | ||
13 | /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property | ||
14 | /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API. | ||
15 | /// Consequently this class creates the needed XxxResetEvent and replaces the handle if | ||
16 | /// it's a WindowsCE OS. | ||
17 | /// </summary> | ||
18 | public static class EventWaitHandleFactory | ||
19 | { | ||
20 | /// <summary> | ||
21 | /// Create a new AutoResetEvent object | ||
22 | /// </summary> | ||
23 | /// <returns>Return a new AutoResetEvent object</returns> | ||
24 | public static AutoResetEvent CreateAutoResetEvent() | ||
25 | { | ||
26 | AutoResetEvent waitHandle = new AutoResetEvent(false); | ||
27 | |||
28 | #if (_WINDOWS_CE) | ||
29 | ReplaceEventHandle(waitHandle, false, false); | ||
30 | #endif | ||
31 | |||
32 | return waitHandle; | ||
33 | } | ||
34 | |||
35 | /// <summary> | ||
36 | /// Create a new ManualResetEvent object | ||
37 | /// </summary> | ||
38 | /// <returns>Return a new ManualResetEvent object</returns> | ||
39 | public static ManualResetEvent CreateManualResetEvent(bool initialState) | ||
40 | { | ||
41 | ManualResetEvent waitHandle = new ManualResetEvent(initialState); | ||
42 | |||
43 | #if (_WINDOWS_CE) | ||
44 | ReplaceEventHandle(waitHandle, true, initialState); | ||
45 | #endif | ||
46 | |||
47 | return waitHandle; | ||
48 | } | ||
49 | |||
50 | #if (_WINDOWS_CE) | ||
51 | |||
52 | /// <summary> | ||
53 | /// Replace the event handle | ||
54 | /// </summary> | ||
55 | /// <param name="waitHandle">The WaitHandle object which its handle needs to be replaced.</param> | ||
56 | /// <param name="manualReset">Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false)</param> | ||
57 | /// <param name="initialState">The initial state of the event</param> | ||
58 | private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState) | ||
59 | { | ||
60 | // Store the old handle | ||
61 | IntPtr oldHandle = waitHandle.Handle; | ||
62 | |||
63 | // Create a new event | ||
64 | IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null); | ||
65 | |||
66 | // Replace the old event with the new event | ||
67 | waitHandle.Handle = newHandle; | ||
68 | |||
69 | // Close the old event | ||
70 | CloseHandle (oldHandle); | ||
71 | } | ||
72 | |||
73 | [DllImport("coredll.dll", SetLastError = true)] | ||
74 | public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); | ||
75 | |||
76 | //Handle | ||
77 | [DllImport("coredll.dll", SetLastError = true)] | ||
78 | public static extern bool CloseHandle(IntPtr hObject); | ||
79 | #endif | ||
80 | |||
81 | } | ||
82 | } | ||
diff --git a/ThirdParty/SmartThreadPool/Exceptions.cs b/ThirdParty/SmartThreadPool/Exceptions.cs index c454709..8e66ce9 100644 --- a/ThirdParty/SmartThreadPool/Exceptions.cs +++ b/ThirdParty/SmartThreadPool/Exceptions.cs | |||
@@ -1,81 +1,111 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | #if !(_WINDOWS_CE) |
3 | 3 | using System.Runtime.Serialization; | |
4 | using System; | 4 | #endif |
5 | using System.Runtime.Serialization; | 5 | |
6 | 6 | namespace Amib.Threading | |
7 | namespace Amib.Threading | 7 | { |
8 | { | 8 | #region Exceptions |
9 | #region Exceptions | 9 | |
10 | 10 | /// <summary> | |
11 | /// <summary> | 11 | /// Represents an exception in case IWorkItemResult.GetResult has been canceled |
12 | /// Represents an exception in case IWorkItemResult.GetResult has been canceled | 12 | /// </summary> |
13 | /// </summary> | 13 | public sealed partial class WorkItemCancelException : Exception |
14 | [Serializable] | 14 | { |
15 | public sealed class WorkItemCancelException : ApplicationException | 15 | public WorkItemCancelException() |
16 | { | 16 | { |
17 | public WorkItemCancelException() : base() | 17 | } |
18 | { | 18 | |
19 | } | 19 | public WorkItemCancelException(string message) |
20 | 20 | : base(message) | |
21 | public WorkItemCancelException(string message) : base(message) | 21 | { |
22 | { | 22 | } |
23 | } | 23 | |
24 | 24 | public WorkItemCancelException(string message, Exception e) | |
25 | public WorkItemCancelException(string message, Exception e) : base(message, e) | 25 | : base(message, e) |
26 | { | 26 | { |
27 | } | 27 | } |
28 | 28 | } | |
29 | public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc) | 29 | |
30 | { | 30 | /// <summary> |
31 | } | 31 | /// Represents an exception in case IWorkItemResult.GetResult has been timed out |
32 | } | 32 | /// </summary> |
33 | 33 | public sealed partial class WorkItemTimeoutException : Exception | |
34 | /// <summary> | 34 | { |
35 | /// Represents an exception in case IWorkItemResult.GetResult has been timed out | 35 | public WorkItemTimeoutException() |
36 | /// </summary> | 36 | { |
37 | [Serializable] | 37 | } |
38 | public sealed class WorkItemTimeoutException : ApplicationException | 38 | |
39 | { | 39 | public WorkItemTimeoutException(string message) |
40 | public WorkItemTimeoutException() : base() | 40 | : base(message) |
41 | { | 41 | { |
42 | } | 42 | } |
43 | 43 | ||
44 | public WorkItemTimeoutException(string message) : base(message) | 44 | public WorkItemTimeoutException(string message, Exception e) |
45 | { | 45 | : base(message, e) |
46 | } | 46 | { |
47 | 47 | } | |
48 | public WorkItemTimeoutException(string message, Exception e) : base(message, e) | 48 | } |
49 | { | 49 | |
50 | } | 50 | /// <summary> |
51 | 51 | /// Represents an exception in case IWorkItemResult.GetResult has been timed out | |
52 | public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc) | 52 | /// </summary> |
53 | { | 53 | public sealed partial class WorkItemResultException : Exception |
54 | } | 54 | { |
55 | } | 55 | public WorkItemResultException() |
56 | 56 | { | |
57 | /// <summary> | 57 | } |
58 | /// Represents an exception in case IWorkItemResult.GetResult has been timed out | 58 | |
59 | /// </summary> | 59 | public WorkItemResultException(string message) |
60 | [Serializable] | 60 | : base(message) |
61 | public sealed class WorkItemResultException : ApplicationException | 61 | { |
62 | { | 62 | } |
63 | public WorkItemResultException() : base() | 63 | |
64 | { | 64 | public WorkItemResultException(string message, Exception e) |
65 | } | 65 | : base(message, e) |
66 | 66 | { | |
67 | public WorkItemResultException(string message) : base(message) | 67 | } |
68 | { | 68 | } |
69 | } | 69 | |
70 | 70 | ||
71 | public WorkItemResultException(string message, Exception e) : base(message, e) | 71 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
72 | { | 72 | /// <summary> |
73 | } | 73 | /// Represents an exception in case IWorkItemResult.GetResult has been canceled |
74 | 74 | /// </summary> | |
75 | public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc) | 75 | [Serializable] |
76 | { | 76 | public sealed partial class WorkItemCancelException |
77 | } | 77 | { |
78 | } | 78 | public WorkItemCancelException(SerializationInfo si, StreamingContext sc) |
79 | 79 | : base(si, sc) | |
80 | #endregion | 80 | { |
81 | } | 81 | } |
82 | } | ||
83 | |||
84 | /// <summary> | ||
85 | /// Represents an exception in case IWorkItemResult.GetResult has been timed out | ||
86 | /// </summary> | ||
87 | [Serializable] | ||
88 | public sealed partial class WorkItemTimeoutException | ||
89 | { | ||
90 | public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) | ||
91 | : base(si, sc) | ||
92 | { | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /// <summary> | ||
97 | /// Represents an exception in case IWorkItemResult.GetResult has been timed out | ||
98 | /// </summary> | ||
99 | [Serializable] | ||
100 | public sealed partial class WorkItemResultException | ||
101 | { | ||
102 | public WorkItemResultException(SerializationInfo si, StreamingContext sc) | ||
103 | : base(si, sc) | ||
104 | { | ||
105 | } | ||
106 | } | ||
107 | |||
108 | #endif | ||
109 | |||
110 | #endregion | ||
111 | } | ||
diff --git a/ThirdParty/SmartThreadPool/Interfaces.cs b/ThirdParty/SmartThreadPool/Interfaces.cs index f1c1fcf..29c8a3e 100644 --- a/ThirdParty/SmartThreadPool/Interfaces.cs +++ b/ThirdParty/SmartThreadPool/Interfaces.cs | |||
@@ -1,271 +1,628 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | using System.Threading; |
3 | 3 | ||
4 | using System; | 4 | namespace Amib.Threading |
5 | using System.Threading; | 5 | { |
6 | 6 | #region Delegates | |
7 | namespace Amib.Threading | 7 | |
8 | { | 8 | /// <summary> |
9 | #region Delegates | 9 | /// A delegate that represents the method to run as the work item |
10 | 10 | /// </summary> | |
11 | /// <summary> | 11 | /// <param name="state">A state object for the method to run</param> |
12 | /// A delegate that represents the method to run as the work item | 12 | public delegate object WorkItemCallback(object state); |
13 | /// </summary> | 13 | |
14 | /// <param name="state">A state object for the method to run</param> | 14 | /// <summary> |
15 | public delegate object WorkItemCallback(object state); | 15 | /// A delegate to call after the WorkItemCallback completed |
16 | 16 | /// </summary> | |
17 | /// <summary> | 17 | /// <param name="wir">The work item result object</param> |
18 | /// A delegate to call after the WorkItemCallback completed | 18 | public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); |
19 | /// </summary> | 19 | |
20 | /// <param name="wir">The work item result object</param> | 20 | /// <summary> |
21 | public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); | 21 | /// A delegate to call after the WorkItemCallback completed |
22 | 22 | /// </summary> | |
23 | /// <summary> | 23 | /// <param name="wir">The work item result object</param> |
24 | /// A delegate to call when a WorkItemsGroup becomes idle | 24 | public delegate void PostExecuteWorkItemCallback<TResult>(IWorkItemResult<TResult> wir); |
25 | /// </summary> | 25 | |
26 | /// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param> | 26 | /// <summary> |
27 | public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); | 27 | /// A delegate to call when a WorkItemsGroup becomes idle |
28 | 28 | /// </summary> | |
29 | #endregion | 29 | /// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param> |
30 | 30 | public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); | |
31 | #region WorkItem Priority | 31 | |
32 | 32 | /// <summary> | |
33 | public enum WorkItemPriority | 33 | /// A delegate to call after a thread is created, but before |
34 | { | 34 | /// it's first use. |
35 | Lowest, | 35 | /// </summary> |
36 | BelowNormal, | 36 | public delegate void ThreadInitializationHandler(); |
37 | Normal, | 37 | |
38 | AboveNormal, | 38 | /// <summary> |
39 | Highest, | 39 | /// A delegate to call when a thread is about to exit, after |
40 | } | 40 | /// it is no longer belong to the pool. |
41 | 41 | /// </summary> | |
42 | #endregion | 42 | public delegate void ThreadTerminationHandler(); |
43 | 43 | ||
44 | #region IHasWorkItemPriority interface | 44 | #endregion |
45 | 45 | ||
46 | public interface IHasWorkItemPriority | 46 | #region WorkItem Priority |
47 | { | 47 | |
48 | WorkItemPriority WorkItemPriority { get; } | 48 | /// <summary> |
49 | } | 49 | /// Defines the availeable priorities of a work item. |
50 | 50 | /// The higher the priority a work item has, the sooner | |
51 | #endregion | 51 | /// it will be executed. |
52 | 52 | /// </summary> | |
53 | #region IWorkItemsGroup interface | 53 | public enum WorkItemPriority |
54 | 54 | { | |
55 | /// <summary> | 55 | Lowest, |
56 | /// IWorkItemsGroup interface | 56 | BelowNormal, |
57 | /// </summary> | 57 | Normal, |
58 | public interface IWorkItemsGroup | 58 | AboveNormal, |
59 | { | 59 | Highest, |
60 | /// <summary> | 60 | } |
61 | /// Get/Set the name of the WorkItemsGroup | 61 | |
62 | /// </summary> | 62 | #endregion |
63 | string Name { get; set; } | 63 | |
64 | 64 | #region IWorkItemsGroup interface | |
65 | IWorkItemResult QueueWorkItem(WorkItemCallback callback); | 65 | |
66 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); | 66 | /// <summary> |
67 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); | 67 | /// IWorkItemsGroup interface |
68 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); | 68 | /// Created by SmartThreadPool.CreateWorkItemsGroup() |
69 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); | 69 | /// </summary> |
70 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); | 70 | public interface IWorkItemsGroup |
71 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); | 71 | { |
72 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); | 72 | /// <summary> |
73 | 73 | /// Get/Set the name of the WorkItemsGroup | |
74 | IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); | 74 | /// </summary> |
75 | IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); | 75 | string Name { get; set; } |
76 | 76 | ||
77 | void WaitForIdle(); | 77 | /// <summary> |
78 | bool WaitForIdle(TimeSpan timeout); | 78 | /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool |
79 | bool WaitForIdle(int millisecondsTimeout); | 79 | /// </summary> |
80 | 80 | int Concurrency { get; set; } | |
81 | int WaitingCallbacks { get; } | 81 | |
82 | event WorkItemsGroupIdleHandler OnIdle; | 82 | /// <summary> |
83 | 83 | /// Get the number of work items waiting in the queue. | |
84 | void Cancel(); | 84 | /// </summary> |
85 | void Start(); | 85 | int WaitingCallbacks { get; } |
86 | } | 86 | |
87 | 87 | /// <summary> | |
88 | #endregion | 88 | /// Get an array with all the state objects of the currently running items. |
89 | 89 | /// The array represents a snap shot and impact performance. | |
90 | #region CallToPostExecute enumerator | 90 | /// </summary> |
91 | 91 | object[] GetStates(); | |
92 | [Flags] | 92 | |
93 | public enum CallToPostExecute | 93 | /// <summary> |
94 | { | 94 | /// Get the WorkItemsGroup start information |
95 | Never = 0x00, | 95 | /// </summary> |
96 | WhenWorkItemCanceled = 0x01, | 96 | WIGStartInfo WIGStartInfo { get; } |
97 | WhenWorkItemNotCanceled = 0x02, | 97 | |
98 | Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, | 98 | /// <summary> |
99 | } | 99 | /// Starts to execute work items |
100 | 100 | /// </summary> | |
101 | #endregion | 101 | void Start(); |
102 | 102 | ||
103 | #region IWorkItemResult interface | 103 | /// <summary> |
104 | 104 | /// Cancel all the work items. | |
105 | /// <summary> | 105 | /// Same as Cancel(false) |
106 | /// IWorkItemResult interface | 106 | /// </summary> |
107 | /// </summary> | 107 | void Cancel(); |
108 | public interface IWorkItemResult | 108 | |
109 | { | 109 | /// <summary> |
110 | /// <summary> | 110 | /// Cancel all work items using thread abortion |
111 | /// Get the result of the work item. | 111 | /// </summary> |
112 | /// If the work item didn't run yet then the caller waits. | 112 | /// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param> |
113 | /// </summary> | 113 | void Cancel(bool abortExecution); |
114 | /// <returns>The result of the work item</returns> | 114 | |
115 | object GetResult(); | 115 | /// <summary> |
116 | 116 | /// Wait for all work item to complete. | |
117 | /// <summary> | 117 | /// </summary> |
118 | /// Get the result of the work item. | 118 | void WaitForIdle(); |
119 | /// If the work item didn't run yet then the caller waits until timeout. | 119 | |
120 | /// </summary> | 120 | /// <summary> |
121 | /// <returns>The result of the work item</returns> | 121 | /// Wait for all work item to complete, until timeout expired |
122 | /// On timeout throws WorkItemTimeoutException | 122 | /// </summary> |
123 | object GetResult( | 123 | /// <param name="timeout">How long to wait for the work items to complete</param> |
124 | int millisecondsTimeout, | 124 | /// <returns>Returns true if work items completed within the timeout, otherwise false.</returns> |
125 | bool exitContext); | 125 | bool WaitForIdle(TimeSpan timeout); |
126 | 126 | ||
127 | /// <summary> | 127 | /// <summary> |
128 | /// Get the result of the work item. | 128 | /// Wait for all work item to complete, until timeout expired |
129 | /// If the work item didn't run yet then the caller waits until timeout. | 129 | /// </summary> |
130 | /// </summary> | 130 | /// <param name="millisecondsTimeout">How long to wait for the work items to complete in milliseconds</param> |
131 | /// <returns>The result of the work item</returns> | 131 | /// <returns>Returns true if work items completed within the timeout, otherwise false.</returns> |
132 | /// On timeout throws WorkItemTimeoutException | 132 | bool WaitForIdle(int millisecondsTimeout); |
133 | object GetResult( | 133 | |
134 | TimeSpan timeout, | 134 | /// <summary> |
135 | bool exitContext); | 135 | /// IsIdle is true when there are no work items running or queued. |
136 | 136 | /// </summary> | |
137 | void Abort(); | 137 | bool IsIdle { get; } |
138 | 138 | ||
139 | /// <summary> | 139 | /// <summary> |
140 | /// Get the result of the work item. | 140 | /// This event is fired when all work items are completed. |
141 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | 141 | /// (When IsIdle changes to true) |
142 | /// </summary> | 142 | /// This event only work on WorkItemsGroup. On SmartThreadPool |
143 | /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param> | 143 | /// it throws the NotImplementedException. |
144 | /// <param name="exitContext"> | 144 | /// </summary> |
145 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 145 | event WorkItemsGroupIdleHandler OnIdle; |
146 | /// </param> | 146 | |
147 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param> | 147 | #region QueueWorkItem |
148 | /// <returns>The result of the work item</returns> | 148 | |
149 | /// On timeout throws WorkItemTimeoutException | 149 | /// <summary> |
150 | /// On cancel throws WorkItemCancelException | 150 | /// Queue a work item |
151 | object GetResult( | 151 | /// </summary> |
152 | int millisecondsTimeout, | 152 | /// <param name="callback">A callback to execute</param> |
153 | bool exitContext, | 153 | /// <returns>Returns a work item result</returns> |
154 | WaitHandle cancelWaitHandle); | 154 | IWorkItemResult QueueWorkItem(WorkItemCallback callback); |
155 | 155 | ||
156 | /// <summary> | 156 | /// <summary> |
157 | /// Get the result of the work item. | 157 | /// Queue a work item |
158 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | 158 | /// </summary> |
159 | /// </summary> | 159 | /// <param name="callback">A callback to execute</param> |
160 | /// <returns>The result of the work item</returns> | 160 | /// <param name="workItemPriority">The priority of the work item</param> |
161 | /// On timeout throws WorkItemTimeoutException | 161 | /// <returns>Returns a work item result</returns> |
162 | /// On cancel throws WorkItemCancelException | 162 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); |
163 | object GetResult( | 163 | |
164 | TimeSpan timeout, | 164 | /// <summary> |
165 | bool exitContext, | 165 | /// Queue a work item |
166 | WaitHandle cancelWaitHandle); | 166 | /// </summary> |
167 | 167 | /// <param name="callback">A callback to execute</param> | |
168 | /// <summary> | 168 | /// <param name="state"> |
169 | /// Get the result of the work item. | 169 | /// The context object of the work item. Used for passing arguments to the work item. |
170 | /// If the work item didn't run yet then the caller waits. | 170 | /// </param> |
171 | /// </summary> | 171 | /// <returns>Returns a work item result</returns> |
172 | /// <param name="e">Filled with the exception if one was thrown</param> | 172 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); |
173 | /// <returns>The result of the work item</returns> | 173 | |
174 | object GetResult(out Exception e); | 174 | /// <summary> |
175 | 175 | /// Queue a work item | |
176 | /// <summary> | 176 | /// </summary> |
177 | /// Get the result of the work item. | 177 | /// <param name="callback">A callback to execute</param> |
178 | /// If the work item didn't run yet then the caller waits until timeout. | 178 | /// <param name="state"> |
179 | /// </summary> | 179 | /// The context object of the work item. Used for passing arguments to the work item. |
180 | /// <param name="e">Filled with the exception if one was thrown</param> | 180 | /// </param> |
181 | /// <returns>The result of the work item</returns> | 181 | /// <param name="workItemPriority">The work item priority</param> |
182 | /// On timeout throws WorkItemTimeoutException | 182 | /// <returns>Returns a work item result</returns> |
183 | object GetResult( | 183 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); |
184 | int millisecondsTimeout, | 184 | |
185 | bool exitContext, | 185 | /// <summary> |
186 | out Exception e); | 186 | /// Queue a work item |
187 | 187 | /// </summary> | |
188 | /// <summary> | 188 | /// <param name="callback">A callback to execute</param> |
189 | /// Get the result of the work item. | 189 | /// <param name="state"> |
190 | /// If the work item didn't run yet then the caller waits until timeout. | 190 | /// The context object of the work item. Used for passing arguments to the work item. |
191 | /// </summary> | 191 | /// </param> |
192 | /// <param name="e">Filled with the exception if one was thrown</param> | 192 | /// <param name="postExecuteWorkItemCallback"> |
193 | /// <returns>The result of the work item</returns> | 193 | /// A delegate to call after the callback completion |
194 | /// On timeout throws WorkItemTimeoutException | 194 | /// </param> |
195 | object GetResult( | 195 | /// <returns>Returns a work item result</returns> |
196 | TimeSpan timeout, | 196 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); |
197 | bool exitContext, | 197 | |
198 | out Exception e); | 198 | /// <summary> |
199 | 199 | /// Queue a work item | |
200 | /// <summary> | 200 | /// </summary> |
201 | /// Get the result of the work item. | 201 | /// <param name="callback">A callback to execute</param> |
202 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | 202 | /// <param name="state"> |
203 | /// </summary> | 203 | /// The context object of the work item. Used for passing arguments to the work item. |
204 | /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param> | 204 | /// </param> |
205 | /// <param name="exitContext"> | 205 | /// <param name="postExecuteWorkItemCallback"> |
206 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 206 | /// A delegate to call after the callback completion |
207 | /// </param> | 207 | /// </param> |
208 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param> | 208 | /// <param name="workItemPriority">The work item priority</param> |
209 | /// <param name="e">Filled with the exception if one was thrown</param> | 209 | /// <returns>Returns a work item result</returns> |
210 | /// <returns>The result of the work item</returns> | 210 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); |
211 | /// On timeout throws WorkItemTimeoutException | 211 | |
212 | /// On cancel throws WorkItemCancelException | 212 | /// <summary> |
213 | object GetResult( | 213 | /// Queue a work item |
214 | int millisecondsTimeout, | 214 | /// </summary> |
215 | bool exitContext, | 215 | /// <param name="callback">A callback to execute</param> |
216 | WaitHandle cancelWaitHandle, | 216 | /// <param name="state"> |
217 | out Exception e); | 217 | /// The context object of the work item. Used for passing arguments to the work item. |
218 | 218 | /// </param> | |
219 | /// <summary> | 219 | /// <param name="postExecuteWorkItemCallback"> |
220 | /// Get the result of the work item. | 220 | /// A delegate to call after the callback completion |
221 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | 221 | /// </param> |
222 | /// </summary> | 222 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> |
223 | /// <returns>The result of the work item</returns> | 223 | /// <returns>Returns a work item result</returns> |
224 | /// <param name="e">Filled with the exception if one was thrown</param> | 224 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); |
225 | /// On timeout throws WorkItemTimeoutException | 225 | |
226 | /// On cancel throws WorkItemCancelException | 226 | /// <summary> |
227 | object GetResult( | 227 | /// Queue a work item |
228 | TimeSpan timeout, | 228 | /// </summary> |
229 | bool exitContext, | 229 | /// <param name="callback">A callback to execute</param> |
230 | WaitHandle cancelWaitHandle, | 230 | /// <param name="state"> |
231 | out Exception e); | 231 | /// The context object of the work item. Used for passing arguments to the work item. |
232 | 232 | /// </param> | |
233 | /// <summary> | 233 | /// <param name="postExecuteWorkItemCallback"> |
234 | /// Gets an indication whether the asynchronous operation has completed. | 234 | /// A delegate to call after the callback completion |
235 | /// </summary> | 235 | /// </param> |
236 | bool IsCompleted { get; } | 236 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> |
237 | 237 | /// <param name="workItemPriority">The work item priority</param> | |
238 | /// <summary> | 238 | /// <returns>Returns a work item result</returns> |
239 | /// Gets an indication whether the asynchronous operation has been canceled. | 239 | IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); |
240 | /// </summary> | 240 | |
241 | bool IsCanceled { get; } | 241 | /// <summary> |
242 | 242 | /// Queue a work item | |
243 | /// <summary> | 243 | /// </summary> |
244 | /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. | 244 | /// <param name="workItemInfo">Work item info</param> |
245 | /// </summary> | 245 | /// <param name="callback">A callback to execute</param> |
246 | object State { get; } | 246 | /// <returns>Returns a work item result</returns> |
247 | 247 | IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); | |
248 | /// <summary> | 248 | |
249 | /// Cancel the work item if it didn't start running yet. | 249 | /// <summary> |
250 | /// </summary> | 250 | /// Queue a work item |
251 | /// <returns>Returns true on success or false if the work item is in progress or already completed</returns> | 251 | /// </summary> |
252 | bool Cancel(); | 252 | /// <param name="workItemInfo">Work item information</param> |
253 | 253 | /// <param name="callback">A callback to execute</param> | |
254 | /// <summary> | 254 | /// <param name="state"> |
255 | /// Get the work item's priority | 255 | /// The context object of the work item. Used for passing arguments to the work item. |
256 | /// </summary> | 256 | /// </param> |
257 | WorkItemPriority WorkItemPriority { get; } | 257 | /// <returns>Returns a work item result</returns> |
258 | 258 | IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); | |
259 | /// <summary> | 259 | |
260 | /// Return the result, same as GetResult() | 260 | #endregion |
261 | /// </summary> | 261 | |
262 | object Result { get; } | 262 | #region QueueWorkItem(Action<...>) |
263 | 263 | ||
264 | /// <summary> | 264 | /// <summary> |
265 | /// Returns the exception if occured otherwise returns null. | 265 | /// Queue a work item. |
266 | /// </summary> | 266 | /// </summary> |
267 | object Exception { get; } | 267 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> |
268 | } | 268 | IWorkItemResult QueueWorkItem(Action action); |
269 | 269 | ||
270 | #endregion | 270 | /// <summary> |
271 | } | 271 | /// Queue a work item. |
272 | /// </summary> | ||
273 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
274 | IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority); | ||
275 | |||
276 | /// <summary> | ||
277 | /// Queue a work item. | ||
278 | /// </summary> | ||
279 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
280 | IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority); | ||
281 | |||
282 | /// <summary> | ||
283 | /// Queue a work item. | ||
284 | /// </summary> | ||
285 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
286 | IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg); | ||
287 | |||
288 | /// <summary> | ||
289 | /// Queue a work item. | ||
290 | /// </summary> | ||
291 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
292 | IWorkItemResult QueueWorkItem<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2); | ||
293 | |||
294 | /// <summary> | ||
295 | /// Queue a work item. | ||
296 | /// </summary> | ||
297 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
298 | IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority); | ||
299 | |||
300 | /// <summary> | ||
301 | /// Queue a work item. | ||
302 | /// </summary> | ||
303 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
304 | IWorkItemResult QueueWorkItem<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3); | ||
305 | |||
306 | /// <summary> | ||
307 | /// Queue a work item. | ||
308 | /// </summary> | ||
309 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
310 | IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority); | ||
311 | |||
312 | /// <summary> | ||
313 | /// Queue a work item. | ||
314 | /// </summary> | ||
315 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
316 | IWorkItemResult QueueWorkItem<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4); | ||
317 | |||
318 | /// <summary> | ||
319 | /// Queue a work item. | ||
320 | /// </summary> | ||
321 | /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns> | ||
322 | IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority); | ||
323 | |||
324 | #endregion | ||
325 | |||
326 | #region QueueWorkItem(Func<...>) | ||
327 | |||
328 | /// <summary> | ||
329 | /// Queue a work item. | ||
330 | /// </summary> | ||
331 | /// <returns>Returns a IWorkItemResult<TResult> object. | ||
332 | /// its GetResult() returns a TResult object</returns> | ||
333 | IWorkItemResult<TResult> QueueWorkItem<TResult>(Func<TResult> func); | ||
334 | |||
335 | /// <summary> | ||
336 | /// Queue a work item. | ||
337 | /// </summary> | ||
338 | /// <returns>Returns a IWorkItemResult<TResult> object. | ||
339 | /// its GetResult() returns a TResult object</returns> | ||
340 | IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> func, T arg); | ||
341 | |||
342 | /// <summary> | ||
343 | /// Queue a work item. | ||
344 | /// </summary> | ||
345 | /// <returns>Returns a IWorkItemResult<TResult> object. | ||
346 | /// its GetResult() returns a TResult object</returns> | ||
347 | IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 arg1, T2 arg2); | ||
348 | |||
349 | /// <summary> | ||
350 | /// Queue a work item. | ||
351 | /// </summary> | ||
352 | /// <returns>Returns a IWorkItemResult<TResult> object. | ||
353 | /// its GetResult() returns a TResult object</returns> | ||
354 | IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3); | ||
355 | |||
356 | /// <summary> | ||
357 | /// Queue a work item. | ||
358 | /// </summary> | ||
359 | /// <returns>Returns a IWorkItemResult<TResult> object. | ||
360 | /// its GetResult() returns a TResult object</returns> | ||
361 | IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4); | ||
362 | |||
363 | #endregion | ||
364 | } | ||
365 | |||
366 | #endregion | ||
367 | |||
368 | #region CallToPostExecute enumerator | ||
369 | |||
370 | [Flags] | ||
371 | public enum CallToPostExecute | ||
372 | { | ||
373 | /// <summary> | ||
374 | /// Never call to the PostExecute call back | ||
375 | /// </summary> | ||
376 | Never = 0x00, | ||
377 | |||
378 | /// <summary> | ||
379 | /// Call to the PostExecute only when the work item is cancelled | ||
380 | /// </summary> | ||
381 | WhenWorkItemCanceled = 0x01, | ||
382 | |||
383 | /// <summary> | ||
384 | /// Call to the PostExecute only when the work item is not cancelled | ||
385 | /// </summary> | ||
386 | WhenWorkItemNotCanceled = 0x02, | ||
387 | |||
388 | /// <summary> | ||
389 | /// Always call to the PostExecute | ||
390 | /// </summary> | ||
391 | Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, | ||
392 | } | ||
393 | |||
394 | #endregion | ||
395 | |||
396 | #region IWorkItemResult interface | ||
397 | |||
398 | /// <summary> | ||
399 | /// The common interface of IWorkItemResult and IWorkItemResult<T> | ||
400 | /// </summary> | ||
401 | public interface IWaitableResult | ||
402 | { | ||
403 | /// <summary> | ||
404 | /// This method intent is for internal use. | ||
405 | /// </summary> | ||
406 | /// <returns></returns> | ||
407 | IWorkItemResult GetWorkItemResult(); | ||
408 | |||
409 | /// <summary> | ||
410 | /// This method intent is for internal use. | ||
411 | /// </summary> | ||
412 | /// <returns></returns> | ||
413 | IWorkItemResult<TResult> GetWorkItemResultT<TResult>(); | ||
414 | } | ||
415 | |||
416 | /// <summary> | ||
417 | /// IWorkItemResult interface. | ||
418 | /// Created when a WorkItemCallback work item is queued. | ||
419 | /// </summary> | ||
420 | public interface IWorkItemResult : IWorkItemResult<object> | ||
421 | { | ||
422 | } | ||
423 | |||
424 | /// <summary> | ||
425 | /// IWorkItemResult<TResult> interface. | ||
426 | /// Created when a Func<TResult> work item is queued. | ||
427 | /// </summary> | ||
428 | public interface IWorkItemResult<TResult> : IWaitableResult | ||
429 | { | ||
430 | /// <summary> | ||
431 | /// Get the result of the work item. | ||
432 | /// If the work item didn't run yet then the caller waits. | ||
433 | /// </summary> | ||
434 | /// <returns>The result of the work item</returns> | ||
435 | TResult GetResult(); | ||
436 | |||
437 | /// <summary> | ||
438 | /// Get the result of the work item. | ||
439 | /// If the work item didn't run yet then the caller waits until timeout. | ||
440 | /// </summary> | ||
441 | /// <returns>The result of the work item</returns> | ||
442 | /// On timeout throws WorkItemTimeoutException | ||
443 | TResult GetResult( | ||
444 | int millisecondsTimeout, | ||
445 | bool exitContext); | ||
446 | |||
447 | /// <summary> | ||
448 | /// Get the result of the work item. | ||
449 | /// If the work item didn't run yet then the caller waits until timeout. | ||
450 | /// </summary> | ||
451 | /// <returns>The result of the work item</returns> | ||
452 | /// On timeout throws WorkItemTimeoutException | ||
453 | TResult GetResult( | ||
454 | TimeSpan timeout, | ||
455 | bool exitContext); | ||
456 | |||
457 | /// <summary> | ||
458 | /// Get the result of the work item. | ||
459 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | ||
460 | /// </summary> | ||
461 | /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param> | ||
462 | /// <param name="exitContext"> | ||
463 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | ||
464 | /// </param> | ||
465 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param> | ||
466 | /// <returns>The result of the work item</returns> | ||
467 | /// On timeout throws WorkItemTimeoutException | ||
468 | /// On cancel throws WorkItemCancelException | ||
469 | TResult GetResult( | ||
470 | int millisecondsTimeout, | ||
471 | bool exitContext, | ||
472 | WaitHandle cancelWaitHandle); | ||
473 | |||
474 | /// <summary> | ||
475 | /// Get the result of the work item. | ||
476 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | ||
477 | /// </summary> | ||
478 | /// <returns>The result of the work item</returns> | ||
479 | /// On timeout throws WorkItemTimeoutException | ||
480 | /// On cancel throws WorkItemCancelException | ||
481 | TResult GetResult( | ||
482 | TimeSpan timeout, | ||
483 | bool exitContext, | ||
484 | WaitHandle cancelWaitHandle); | ||
485 | |||
486 | /// <summary> | ||
487 | /// Get the result of the work item. | ||
488 | /// If the work item didn't run yet then the caller waits. | ||
489 | /// </summary> | ||
490 | /// <param name="e">Filled with the exception if one was thrown</param> | ||
491 | /// <returns>The result of the work item</returns> | ||
492 | TResult GetResult(out Exception e); | ||
493 | |||
494 | /// <summary> | ||
495 | /// Get the result of the work item. | ||
496 | /// If the work item didn't run yet then the caller waits until timeout. | ||
497 | /// </summary> | ||
498 | /// <param name="millisecondsTimeout"></param> | ||
499 | /// <param name="exitContext"></param> | ||
500 | /// <param name="e">Filled with the exception if one was thrown</param> | ||
501 | /// <returns>The result of the work item</returns> | ||
502 | /// On timeout throws WorkItemTimeoutException | ||
503 | TResult GetResult( | ||
504 | int millisecondsTimeout, | ||
505 | bool exitContext, | ||
506 | out Exception e); | ||
507 | |||
508 | /// <summary> | ||
509 | /// Get the result of the work item. | ||
510 | /// If the work item didn't run yet then the caller waits until timeout. | ||
511 | /// </summary> | ||
512 | /// <param name="exitContext"></param> | ||
513 | /// <param name="e">Filled with the exception if one was thrown</param> | ||
514 | /// <param name="timeout"></param> | ||
515 | /// <returns>The result of the work item</returns> | ||
516 | /// On timeout throws WorkItemTimeoutException | ||
517 | TResult GetResult( | ||
518 | TimeSpan timeout, | ||
519 | bool exitContext, | ||
520 | out Exception e); | ||
521 | |||
522 | /// <summary> | ||
523 | /// Get the result of the work item. | ||
524 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | ||
525 | /// </summary> | ||
526 | /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param> | ||
527 | /// <param name="exitContext"> | ||
528 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | ||
529 | /// </param> | ||
530 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param> | ||
531 | /// <param name="e">Filled with the exception if one was thrown</param> | ||
532 | /// <returns>The result of the work item</returns> | ||
533 | /// On timeout throws WorkItemTimeoutException | ||
534 | /// On cancel throws WorkItemCancelException | ||
535 | TResult GetResult( | ||
536 | int millisecondsTimeout, | ||
537 | bool exitContext, | ||
538 | WaitHandle cancelWaitHandle, | ||
539 | out Exception e); | ||
540 | |||
541 | /// <summary> | ||
542 | /// Get the result of the work item. | ||
543 | /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. | ||
544 | /// </summary> | ||
545 | /// <returns>The result of the work item</returns> | ||
546 | /// <param name="cancelWaitHandle"></param> | ||
547 | /// <param name="e">Filled with the exception if one was thrown</param> | ||
548 | /// <param name="timeout"></param> | ||
549 | /// <param name="exitContext"></param> | ||
550 | /// On timeout throws WorkItemTimeoutException | ||
551 | /// On cancel throws WorkItemCancelException | ||
552 | TResult GetResult( | ||
553 | TimeSpan timeout, | ||
554 | bool exitContext, | ||
555 | WaitHandle cancelWaitHandle, | ||
556 | out Exception e); | ||
557 | |||
558 | /// <summary> | ||
559 | /// Gets an indication whether the asynchronous operation has completed. | ||
560 | /// </summary> | ||
561 | bool IsCompleted { get; } | ||
562 | |||
563 | /// <summary> | ||
564 | /// Gets an indication whether the asynchronous operation has been canceled. | ||
565 | /// </summary> | ||
566 | bool IsCanceled { get; } | ||
567 | |||
568 | /// <summary> | ||
569 | /// Gets the user-defined object that contains context data | ||
570 | /// for the work item method. | ||
571 | /// </summary> | ||
572 | object State { get; } | ||
573 | |||
574 | /// <summary> | ||
575 | /// Same as Cancel(false). | ||
576 | /// </summary> | ||
577 | bool Cancel(); | ||
578 | |||
579 | /// <summary> | ||
580 | /// Cancel the work item execution. | ||
581 | /// If the work item is in the queue then it won't execute | ||
582 | /// If the work item is completed, it will remain completed | ||
583 | /// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled | ||
584 | /// property to check if the work item has been cancelled. If the abortExecution is set to true then | ||
585 | /// the Smart Thread Pool will send an AbortException to the running thread to stop the execution | ||
586 | /// of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException. | ||
587 | /// If the work item is already cancelled it will remain cancelled | ||
588 | /// </summary> | ||
589 | /// <param name="abortExecution">When true send an AbortException to the executing thread.</param> | ||
590 | /// <returns>Returns true if the work item was not completed, otherwise false.</returns> | ||
591 | bool Cancel(bool abortExecution); | ||
592 | |||
593 | /// <summary> | ||
594 | /// Get the work item's priority | ||
595 | /// </summary> | ||
596 | WorkItemPriority WorkItemPriority { get; } | ||
597 | |||
598 | /// <summary> | ||
599 | /// Return the result, same as GetResult() | ||
600 | /// </summary> | ||
601 | TResult Result { get; } | ||
602 | |||
603 | /// <summary> | ||
604 | /// Returns the exception if occured otherwise returns null. | ||
605 | /// </summary> | ||
606 | object Exception { get; } | ||
607 | } | ||
608 | |||
609 | #endregion | ||
610 | |||
611 | #region .NET 3.5 | ||
612 | |||
613 | // All these delegate are built-in .NET 3.5 | ||
614 | // Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity. | ||
615 | |||
616 | public delegate void Action(); | ||
617 | public delegate void Action<T1, T2>(T1 arg1, T2 arg2); | ||
618 | public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3); | ||
619 | public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); | ||
620 | |||
621 | public delegate TResult Func<TResult>(); | ||
622 | public delegate TResult Func<T, TResult>(T arg1); | ||
623 | public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2); | ||
624 | public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3); | ||
625 | public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); | ||
626 | |||
627 | #endregion | ||
628 | } | ||
diff --git a/ThirdParty/SmartThreadPool/InternalInterfaces.cs b/ThirdParty/SmartThreadPool/InternalInterfaces.cs new file mode 100644 index 0000000..8be7161 --- /dev/null +++ b/ThirdParty/SmartThreadPool/InternalInterfaces.cs | |||
@@ -0,0 +1,27 @@ | |||
1 |  | ||
2 | namespace Amib.Threading.Internal | ||
3 | { | ||
4 | /// <summary> | ||
5 | /// An internal delegate to call when the WorkItem starts or completes | ||
6 | /// </summary> | ||
7 | internal delegate void WorkItemStateCallback(WorkItem workItem); | ||
8 | |||
9 | internal interface IInternalWorkItemResult | ||
10 | { | ||
11 | event WorkItemStateCallback OnWorkItemStarted; | ||
12 | event WorkItemStateCallback OnWorkItemCompleted; | ||
13 | } | ||
14 | |||
15 | internal interface IInternalWaitableResult | ||
16 | { | ||
17 | /// <summary> | ||
18 | /// This method is intent for internal use. | ||
19 | /// </summary> | ||
20 | IWorkItemResult GetWorkItemResult(); | ||
21 | } | ||
22 | |||
23 | public interface IHasWorkItemPriority | ||
24 | { | ||
25 | WorkItemPriority WorkItemPriority { get; } | ||
26 | } | ||
27 | } | ||
diff --git a/ThirdParty/SmartThreadPool/PriorityQueue.cs b/ThirdParty/SmartThreadPool/PriorityQueue.cs index 63d5e84..6245fd8 100644 --- a/ThirdParty/SmartThreadPool/PriorityQueue.cs +++ b/ThirdParty/SmartThreadPool/PriorityQueue.cs | |||
@@ -1,240 +1,239 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | using System.Collections; |
3 | 3 | using System.Collections.Generic; | |
4 | using System; | 4 | using System.Diagnostics; |
5 | using System.Collections; | 5 | |
6 | using System.Diagnostics; | 6 | namespace Amib.Threading.Internal |
7 | 7 | { | |
8 | namespace Amib.Threading.Internal | 8 | #region PriorityQueue class |
9 | { | 9 | |
10 | #region PriorityQueue class | 10 | /// <summary> |
11 | 11 | /// PriorityQueue class | |
12 | /// <summary> | 12 | /// This class is not thread safe because we use external lock |
13 | /// PriorityQueue class | 13 | /// </summary> |
14 | /// This class is not thread safe because we use external lock | 14 | public sealed class PriorityQueue : IEnumerable |
15 | /// </summary> | 15 | { |
16 | public sealed class PriorityQueue : IEnumerable | 16 | #region Private members |
17 | { | 17 | |
18 | #region Private members | 18 | /// <summary> |
19 | 19 | /// The number of queues, there is one for each type of priority | |
20 | /// <summary> | 20 | /// </summary> |
21 | /// The number of queues, there is one for each type of priority | 21 | private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; |
22 | /// </summary> | 22 | |
23 | private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; | 23 | /// <summary> |
24 | 24 | /// Work items queues. There is one for each type of priority | |
25 | /// <summary> | 25 | /// </summary> |
26 | /// Work items queues. There is one for each type of priority | 26 | private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount]; |
27 | /// </summary> | 27 | |
28 | private Queue [] _queues = new Queue[_queuesCount]; | 28 | /// <summary> |
29 | 29 | /// The total number of work items within the queues | |
30 | /// <summary> | 30 | /// </summary> |
31 | /// The total number of work items within the queues | 31 | private int _workItemsCount; |
32 | /// </summary> | 32 | |
33 | private int _workItemsCount = 0; | 33 | /// <summary> |
34 | 34 | /// Use with IEnumerable interface | |
35 | /// <summary> | 35 | /// </summary> |
36 | /// Use with IEnumerable interface | 36 | private int _version; |
37 | /// </summary> | 37 | |
38 | private int _version = 0; | 38 | #endregion |
39 | 39 | ||
40 | #endregion | 40 | #region Contructor |
41 | 41 | ||
42 | #region Contructor | 42 | public PriorityQueue() |
43 | 43 | { | |
44 | public PriorityQueue() | 44 | for(int i = 0; i < _queues.Length; ++i) |
45 | { | 45 | { |
46 | for(int i = 0; i < _queues.Length; ++i) | 46 | _queues[i] = new LinkedList<IHasWorkItemPriority>(); |
47 | { | 47 | } |
48 | _queues[i] = new Queue(); | 48 | } |
49 | } | 49 | |
50 | } | 50 | #endregion |
51 | 51 | ||
52 | #endregion | 52 | #region Methods |
53 | 53 | ||
54 | #region Methods | 54 | /// <summary> |
55 | 55 | /// Enqueue a work item. | |
56 | /// <summary> | 56 | /// </summary> |
57 | /// Enqueue a work item. | 57 | /// <param name="workItem">A work item</param> |
58 | /// </summary> | 58 | public void Enqueue(IHasWorkItemPriority workItem) |
59 | /// <param name="workItem">A work item</param> | 59 | { |
60 | public void Enqueue(IHasWorkItemPriority workItem) | 60 | Debug.Assert(null != workItem); |
61 | { | 61 | |
62 | Debug.Assert(null != workItem); | 62 | int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; |
63 | 63 | Debug.Assert(queueIndex >= 0); | |
64 | int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; | 64 | Debug.Assert(queueIndex < _queuesCount); |
65 | Debug.Assert(queueIndex >= 0); | 65 | |
66 | Debug.Assert(queueIndex < _queuesCount); | 66 | _queues[queueIndex].AddLast(workItem); |
67 | 67 | ++_workItemsCount; | |
68 | _queues[queueIndex].Enqueue(workItem); | 68 | ++_version; |
69 | ++_workItemsCount; | 69 | } |
70 | ++_version; | 70 | |
71 | } | 71 | /// <summary> |
72 | 72 | /// Dequeque a work item. | |
73 | /// <summary> | 73 | /// </summary> |
74 | /// Dequeque a work item. | 74 | /// <returns>Returns the next work item</returns> |
75 | /// </summary> | 75 | public IHasWorkItemPriority Dequeue() |
76 | /// <returns>Returns the next work item</returns> | 76 | { |
77 | public IHasWorkItemPriority Dequeue() | 77 | IHasWorkItemPriority workItem = null; |
78 | { | 78 | |
79 | IHasWorkItemPriority workItem = null; | 79 | if(_workItemsCount > 0) |
80 | 80 | { | |
81 | if(_workItemsCount > 0) | 81 | int queueIndex = GetNextNonEmptyQueue(-1); |
82 | { | 82 | Debug.Assert(queueIndex >= 0); |
83 | int queueIndex = GetNextNonEmptyQueue(-1); | 83 | workItem = _queues[queueIndex].First.Value; |
84 | Debug.Assert(queueIndex >= 0); | 84 | _queues[queueIndex].RemoveFirst(); |
85 | workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority; | 85 | Debug.Assert(null != workItem); |
86 | Debug.Assert(null != workItem); | 86 | --_workItemsCount; |
87 | --_workItemsCount; | 87 | ++_version; |
88 | ++_version; | 88 | } |
89 | } | 89 | |
90 | 90 | return workItem; | |
91 | return workItem; | 91 | } |
92 | } | 92 | |
93 | 93 | /// <summary> | |
94 | /// <summary> | 94 | /// Find the next non empty queue starting at queue queueIndex+1 |
95 | /// Find the next non empty queue starting at queue queueIndex+1 | 95 | /// </summary> |
96 | /// </summary> | 96 | /// <param name="queueIndex">The index-1 to start from</param> |
97 | /// <param name="queueIndex">The index-1 to start from</param> | 97 | /// <returns> |
98 | /// <returns> | 98 | /// The index of the next non empty queue or -1 if all the queues are empty |
99 | /// The index of the next non empty queue or -1 if all the queues are empty | 99 | /// </returns> |
100 | /// </returns> | 100 | private int GetNextNonEmptyQueue(int queueIndex) |
101 | private int GetNextNonEmptyQueue(int queueIndex) | 101 | { |
102 | { | 102 | for(int i = queueIndex+1; i < _queuesCount; ++i) |
103 | for(int i = queueIndex+1; i < _queuesCount; ++i) | 103 | { |
104 | { | 104 | if(_queues[i].Count > 0) |
105 | if(_queues[i].Count > 0) | 105 | { |
106 | { | 106 | return i; |
107 | return i; | 107 | } |
108 | } | 108 | } |
109 | } | 109 | return -1; |
110 | return -1; | 110 | } |
111 | } | 111 | |
112 | 112 | /// <summary> | |
113 | /// <summary> | 113 | /// The number of work items |
114 | /// The number of work items | 114 | /// </summary> |
115 | /// </summary> | 115 | public int Count |
116 | public int Count | 116 | { |
117 | { | 117 | get |
118 | get | 118 | { |
119 | { | 119 | return _workItemsCount; |
120 | return _workItemsCount; | 120 | } |
121 | } | 121 | } |
122 | } | 122 | |
123 | 123 | /// <summary> | |
124 | /// <summary> | 124 | /// Clear all the work items |
125 | /// Clear all the work items | 125 | /// </summary> |
126 | /// </summary> | 126 | public void Clear() |
127 | public void Clear() | 127 | { |
128 | { | 128 | if (_workItemsCount > 0) |
129 | if (_workItemsCount > 0) | 129 | { |
130 | { | 130 | foreach(LinkedList<IHasWorkItemPriority> queue in _queues) |
131 | foreach(Queue queue in _queues) | 131 | { |
132 | { | 132 | queue.Clear(); |
133 | queue.Clear(); | 133 | } |
134 | } | 134 | _workItemsCount = 0; |
135 | _workItemsCount = 0; | 135 | ++_version; |
136 | ++_version; | 136 | } |
137 | } | 137 | } |
138 | } | 138 | |
139 | 139 | #endregion | |
140 | #endregion | 140 | |
141 | 141 | #region IEnumerable Members | |
142 | #region IEnumerable Members | 142 | |
143 | 143 | /// <summary> | |
144 | /// <summary> | 144 | /// Returns an enumerator to iterate over the work items |
145 | /// Returns an enumerator to iterate over the work items | 145 | /// </summary> |
146 | /// </summary> | 146 | /// <returns>Returns an enumerator</returns> |
147 | /// <returns>Returns an enumerator</returns> | 147 | public IEnumerator GetEnumerator() |
148 | public IEnumerator GetEnumerator() | 148 | { |
149 | { | 149 | return new PriorityQueueEnumerator(this); |
150 | return new PriorityQueueEnumerator(this); | 150 | } |
151 | } | 151 | |
152 | 152 | #endregion | |
153 | #endregion | 153 | |
154 | 154 | #region PriorityQueueEnumerator | |
155 | #region PriorityQueueEnumerator | 155 | |
156 | 156 | /// <summary> | |
157 | /// <summary> | 157 | /// The class the implements the enumerator |
158 | /// The class the implements the enumerator | 158 | /// </summary> |
159 | /// </summary> | 159 | private class PriorityQueueEnumerator : IEnumerator |
160 | private class PriorityQueueEnumerator : IEnumerator | 160 | { |
161 | { | 161 | private readonly PriorityQueue _priorityQueue; |
162 | private PriorityQueue _priorityQueue; | 162 | private int _version; |
163 | private int _version; | 163 | private int _queueIndex; |
164 | private int _queueIndex; | 164 | private IEnumerator _enumerator; |
165 | private IEnumerator _enumerator; | 165 | |
166 | 166 | public PriorityQueueEnumerator(PriorityQueue priorityQueue) | |
167 | public PriorityQueueEnumerator(PriorityQueue priorityQueue) | 167 | { |
168 | { | 168 | _priorityQueue = priorityQueue; |
169 | _priorityQueue = priorityQueue; | 169 | _version = _priorityQueue._version; |
170 | _version = _priorityQueue._version; | 170 | _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); |
171 | _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); | 171 | if (_queueIndex >= 0) |
172 | if (_queueIndex >= 0) | 172 | { |
173 | { | 173 | _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); |
174 | _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); | 174 | } |
175 | } | 175 | else |
176 | else | 176 | { |
177 | { | 177 | _enumerator = null; |
178 | _enumerator = null; | 178 | } |
179 | } | 179 | } |
180 | } | 180 | |
181 | 181 | #region IEnumerator Members | |
182 | #region IEnumerator Members | 182 | |
183 | 183 | public void Reset() | |
184 | public void Reset() | 184 | { |
185 | { | 185 | _version = _priorityQueue._version; |
186 | _version = _priorityQueue._version; | 186 | _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); |
187 | _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); | 187 | if (_queueIndex >= 0) |
188 | if (_queueIndex >= 0) | 188 | { |
189 | { | 189 | _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); |
190 | _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); | 190 | } |
191 | } | 191 | else |
192 | else | 192 | { |
193 | { | 193 | _enumerator = null; |
194 | _enumerator = null; | 194 | } |
195 | } | 195 | } |
196 | } | 196 | |
197 | 197 | public object Current | |
198 | public object Current | 198 | { |
199 | { | 199 | get |
200 | get | 200 | { |
201 | { | 201 | Debug.Assert(null != _enumerator); |
202 | Debug.Assert(null != _enumerator); | 202 | return _enumerator.Current; |
203 | return _enumerator.Current; | 203 | } |
204 | } | 204 | } |
205 | } | 205 | |
206 | 206 | public bool MoveNext() | |
207 | public bool MoveNext() | 207 | { |
208 | { | 208 | if (null == _enumerator) |
209 | if (null == _enumerator) | 209 | { |
210 | { | 210 | return false; |
211 | return false; | 211 | } |
212 | } | 212 | |
213 | 213 | if(_version != _priorityQueue._version) | |
214 | if(_version != _priorityQueue._version) | 214 | { |
215 | { | 215 | throw new InvalidOperationException("The collection has been modified"); |
216 | throw new InvalidOperationException("The collection has been modified"); | 216 | |
217 | 217 | } | |
218 | } | 218 | if (!_enumerator.MoveNext()) |
219 | if (!_enumerator.MoveNext()) | 219 | { |
220 | { | 220 | _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex); |
221 | _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex); | 221 | if(-1 == _queueIndex) |
222 | if(-1 == _queueIndex) | 222 | { |
223 | { | 223 | return false; |
224 | return false; | 224 | } |
225 | } | 225 | _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); |
226 | _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); | 226 | _enumerator.MoveNext(); |
227 | _enumerator.MoveNext(); | 227 | return true; |
228 | return true; | 228 | } |
229 | } | 229 | return true; |
230 | return true; | 230 | } |
231 | } | 231 | |
232 | 232 | #endregion | |
233 | #endregion | 233 | } |
234 | } | 234 | |
235 | 235 | #endregion | |
236 | #endregion | 236 | } |
237 | } | 237 | |
238 | 238 | #endregion | |
239 | #endregion | 239 | } |
240 | } | ||
diff --git a/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1651e78 --- /dev/null +++ b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs | |||
@@ -0,0 +1,23 @@ | |||
1 | using System.Reflection; | ||
2 | using System.Runtime.CompilerServices; | ||
3 | using System.Runtime.InteropServices; | ||
4 | |||
5 | [assembly: AssemblyTitle("Amib.Threading")] | ||
6 | [assembly: AssemblyDescription("Smart Thread Pool")] | ||
7 | [assembly: AssemblyConfiguration("")] | ||
8 | [assembly: AssemblyCompany("")] | ||
9 | [assembly: AssemblyProduct("Amib.Threading")] | ||
10 | [assembly: AssemblyCopyright("")] | ||
11 | [assembly: AssemblyTrademark("")] | ||
12 | [assembly: AssemblyCulture("")] | ||
13 | [assembly: ComVisible(false)] | ||
14 | [assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")] | ||
15 | [assembly: AssemblyVersion("2.2.3.0")] | ||
16 | |||
17 | #if (_PUBLISH) | ||
18 | [assembly: InternalsVisibleTo("STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad")] | ||
19 | #else | ||
20 | [assembly: InternalsVisibleTo("STPTests")] | ||
21 | #endif | ||
22 | |||
23 | |||
diff --git a/ThirdParty/SmartThreadPool/SLExt.cs b/ThirdParty/SmartThreadPool/SLExt.cs new file mode 100644 index 0000000..fafff19 --- /dev/null +++ b/ThirdParty/SmartThreadPool/SLExt.cs | |||
@@ -0,0 +1,16 @@ | |||
1 | #if _SILVERLIGHT | ||
2 | |||
3 | using System.Threading; | ||
4 | |||
5 | namespace Amib.Threading | ||
6 | { | ||
7 | public enum ThreadPriority | ||
8 | { | ||
9 | Lowest, | ||
10 | BelowNormal, | ||
11 | Normal, | ||
12 | AboveNormal, | ||
13 | Highest, | ||
14 | } | ||
15 | } | ||
16 | #endif | ||
diff --git a/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs new file mode 100644 index 0000000..3b92645 --- /dev/null +++ b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs | |||
@@ -0,0 +1,62 @@ | |||
1 | #if !(_WINDOWS_CE) | ||
2 | |||
3 | using System; | ||
4 | using System.Threading; | ||
5 | |||
6 | namespace Amib.Threading.Internal | ||
7 | { | ||
8 | #if _WINDOWS || WINDOWS_PHONE | ||
9 | internal static class STPEventWaitHandle | ||
10 | { | ||
11 | public const int WaitTimeout = Timeout.Infinite; | ||
12 | |||
13 | internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) | ||
14 | { | ||
15 | return WaitHandle.WaitAll(waitHandles, millisecondsTimeout); | ||
16 | } | ||
17 | |||
18 | internal static int WaitAny(WaitHandle[] waitHandles) | ||
19 | { | ||
20 | return WaitHandle.WaitAny(waitHandles); | ||
21 | } | ||
22 | |||
23 | internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) | ||
24 | { | ||
25 | return WaitHandle.WaitAny(waitHandles, millisecondsTimeout); | ||
26 | } | ||
27 | |||
28 | internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) | ||
29 | { | ||
30 | return waitHandle.WaitOne(millisecondsTimeout); | ||
31 | } | ||
32 | } | ||
33 | #else | ||
34 | internal static class STPEventWaitHandle | ||
35 | { | ||
36 | public const int WaitTimeout = Timeout.Infinite; | ||
37 | |||
38 | internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) | ||
39 | { | ||
40 | return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); | ||
41 | } | ||
42 | |||
43 | internal static int WaitAny(WaitHandle[] waitHandles) | ||
44 | { | ||
45 | return WaitHandle.WaitAny(waitHandles); | ||
46 | } | ||
47 | |||
48 | internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) | ||
49 | { | ||
50 | return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); | ||
51 | } | ||
52 | |||
53 | internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) | ||
54 | { | ||
55 | return waitHandle.WaitOne(millisecondsTimeout, exitContext); | ||
56 | } | ||
57 | } | ||
58 | #endif | ||
59 | |||
60 | } | ||
61 | |||
62 | #endif \ No newline at end of file | ||
diff --git a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs index 077cf17..2508661 100644 --- a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs +++ b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs | |||
@@ -1,354 +1,448 @@ | |||
1 | using System; | 1 | using System; |
2 | using System.Diagnostics; | 2 | using System.Diagnostics; |
3 | 3 | using System.Threading; | |
4 | namespace Amib.Threading.Internal | 4 | |
5 | { | 5 | namespace Amib.Threading |
6 | internal enum STPPerformanceCounterType | 6 | { |
7 | { | 7 | public interface ISTPPerformanceCountersReader |
8 | // Fields | 8 | { |
9 | ActiveThreads = 0, | 9 | long InUseThreads { get; } |
10 | InUseThreads = 1, | 10 | long ActiveThreads { get; } |
11 | OverheadThreads = 2, | 11 | long WorkItemsQueued { get; } |
12 | OverheadThreadsPercent = 3, | 12 | long WorkItemsProcessed { get; } |
13 | OverheadThreadsPercentBase = 4, | 13 | } |
14 | 14 | } | |
15 | WorkItems = 5, | 15 | |
16 | WorkItemsInQueue = 6, | 16 | namespace Amib.Threading.Internal |
17 | WorkItemsProcessed = 7, | 17 | { |
18 | 18 | internal interface ISTPInstancePerformanceCounters : IDisposable | |
19 | WorkItemsQueuedPerSecond = 8, | 19 | { |
20 | WorkItemsProcessedPerSecond = 9, | 20 | void Close(); |
21 | 21 | void SampleThreads(long activeThreads, long inUseThreads); | |
22 | AvgWorkItemWaitTime = 10, | 22 | void SampleWorkItems(long workItemsQueued, long workItemsProcessed); |
23 | AvgWorkItemWaitTimeBase = 11, | 23 | void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime); |
24 | 24 | void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime); | |
25 | AvgWorkItemProcessTime = 12, | 25 | } |
26 | AvgWorkItemProcessTimeBase = 13, | 26 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
27 | 27 | ||
28 | WorkItemsGroups = 14, | 28 | internal enum STPPerformanceCounterType |
29 | 29 | { | |
30 | LastCounter = 14, | 30 | // Fields |
31 | } | 31 | ActiveThreads = 0, |
32 | 32 | InUseThreads = 1, | |
33 | 33 | OverheadThreads = 2, | |
34 | /// <summary> | 34 | OverheadThreadsPercent = 3, |
35 | /// Summary description for STPPerformanceCounter. | 35 | OverheadThreadsPercentBase = 4, |
36 | /// </summary> | 36 | |
37 | internal class STPPerformanceCounter | 37 | WorkItems = 5, |
38 | { | 38 | WorkItemsInQueue = 6, |
39 | // Fields | 39 | WorkItemsProcessed = 7, |
40 | private PerformanceCounterType _pcType; | 40 | |
41 | protected string _counterHelp; | 41 | WorkItemsQueuedPerSecond = 8, |
42 | protected string _counterName; | 42 | WorkItemsProcessedPerSecond = 9, |
43 | 43 | ||
44 | // Methods | 44 | AvgWorkItemWaitTime = 10, |
45 | public STPPerformanceCounter( | 45 | AvgWorkItemWaitTimeBase = 11, |
46 | string counterName, | 46 | |
47 | string counterHelp, | 47 | AvgWorkItemProcessTime = 12, |
48 | PerformanceCounterType pcType) | 48 | AvgWorkItemProcessTimeBase = 13, |
49 | { | 49 | |
50 | this._counterName = counterName; | 50 | WorkItemsGroups = 14, |
51 | this._counterHelp = counterHelp; | 51 | |
52 | this._pcType = pcType; | 52 | LastCounter = 14, |
53 | } | 53 | } |
54 | 54 | ||
55 | public void AddCounterToCollection(CounterCreationDataCollection counterData) | 55 | |
56 | { | 56 | /// <summary> |
57 | CounterCreationData counterCreationData = new CounterCreationData( | 57 | /// Summary description for STPPerformanceCounter. |
58 | _counterName, | 58 | /// </summary> |
59 | _counterHelp, | 59 | internal class STPPerformanceCounter |
60 | _pcType); | 60 | { |
61 | 61 | // Fields | |
62 | counterData.Add(counterCreationData); | 62 | private readonly PerformanceCounterType _pcType; |
63 | } | 63 | protected string _counterHelp; |
64 | 64 | protected string _counterName; | |
65 | // Properties | 65 | |
66 | public string Name | 66 | // Methods |
67 | { | 67 | public STPPerformanceCounter( |
68 | get | 68 | string counterName, |
69 | { | 69 | string counterHelp, |
70 | return _counterName; | 70 | PerformanceCounterType pcType) |
71 | } | 71 | { |
72 | } | 72 | _counterName = counterName; |
73 | } | 73 | _counterHelp = counterHelp; |
74 | 74 | _pcType = pcType; | |
75 | internal class STPPerformanceCounters | 75 | } |
76 | { | 76 | |
77 | // Fields | 77 | public void AddCounterToCollection(CounterCreationDataCollection counterData) |
78 | internal STPPerformanceCounter[] _stpPerformanceCounters; | 78 | { |
79 | private static STPPerformanceCounters _instance; | 79 | CounterCreationData counterCreationData = new CounterCreationData( |
80 | internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; | 80 | _counterName, |
81 | internal const string _stpCategoryName = "SmartThreadPool"; | 81 | _counterHelp, |
82 | 82 | _pcType); | |
83 | // Methods | 83 | |
84 | static STPPerformanceCounters() | 84 | counterData.Add(counterCreationData); |
85 | { | 85 | } |
86 | _instance = new STPPerformanceCounters(); | 86 | |
87 | } | 87 | // Properties |
88 | 88 | public string Name | |
89 | private STPPerformanceCounters() | 89 | { |
90 | { | 90 | get |
91 | STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] | 91 | { |
92 | { | 92 | return _counterName; |
93 | new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), | 93 | } |
94 | new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), | 94 | } |
95 | new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), | 95 | } |
96 | new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), | 96 | |
97 | new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), | 97 | internal class STPPerformanceCounters |
98 | 98 | { | |
99 | new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), | 99 | // Fields |
100 | new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), | 100 | internal STPPerformanceCounter[] _stpPerformanceCounters; |
101 | new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), | 101 | private static readonly STPPerformanceCounters _instance; |
102 | 102 | internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; | |
103 | new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), | 103 | internal const string _stpCategoryName = "SmartThreadPool"; |
104 | new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), | 104 | |
105 | 105 | // Methods | |
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), | 106 | static STPPerformanceCounters() |
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), | 107 | { |
108 | 108 | _instance = new STPPerformanceCounters(); | |
109 | new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), | 109 | } |
110 | new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), | 110 | |
111 | 111 | private STPPerformanceCounters() | |
112 | new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), | 112 | { |
113 | }; | 113 | STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] |
114 | 114 | { | |
115 | _stpPerformanceCounters = stpPerformanceCounters; | 115 | new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), |
116 | SetupCategory(); | 116 | new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), |
117 | } | 117 | new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), |
118 | 118 | new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), | |
119 | private void SetupCategory() | 119 | new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), |
120 | { | 120 | |
121 | if (!PerformanceCounterCategory.Exists(_stpCategoryName)) | 121 | new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), |
122 | { | 122 | new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), |
123 | CounterCreationDataCollection counters = new CounterCreationDataCollection(); | 123 | new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), |
124 | 124 | ||
125 | for (int i = 0; i < _stpPerformanceCounters.Length; i++) | 125 | new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), |
126 | { | 126 | new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), |
127 | _stpPerformanceCounters[i].AddCounterToCollection(counters); | 127 | |
128 | } | 128 | 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), |
129 | 129 | 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), | |
130 | 130 | ||
131 | // *********** Remark for .NET 2.0 *********** | 131 | new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), |
132 | // If you are here, it means you got the warning that this overload | 132 | new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), |
133 | // of the method is deprecated in .NET 2.0. To use the correct | 133 | |
134 | // method overload, uncomment the third argument of | 134 | new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), |
135 | // the method. | 135 | }; |
136 | #pragma warning disable 0618 | 136 | |
137 | PerformanceCounterCategory.Create( | 137 | _stpPerformanceCounters = stpPerformanceCounters; |
138 | _stpCategoryName, | 138 | SetupCategory(); |
139 | _stpCategoryHelp, | 139 | } |
140 | //PerformanceCounterCategoryType.MultiInstance, | 140 | |
141 | counters); | 141 | private void SetupCategory() |
142 | #pragma warning restore 0618 | 142 | { |
143 | } | 143 | if (!PerformanceCounterCategory.Exists(_stpCategoryName)) |
144 | } | 144 | { |
145 | 145 | CounterCreationDataCollection counters = new CounterCreationDataCollection(); | |
146 | // Properties | 146 | |
147 | public static STPPerformanceCounters Instance | 147 | for (int i = 0; i < _stpPerformanceCounters.Length; i++) |
148 | { | 148 | { |
149 | get | 149 | _stpPerformanceCounters[i].AddCounterToCollection(counters); |
150 | { | 150 | } |
151 | return _instance; | 151 | |
152 | } | 152 | PerformanceCounterCategory.Create( |
153 | } | 153 | _stpCategoryName, |
154 | } | 154 | _stpCategoryHelp, |
155 | 155 | PerformanceCounterCategoryType.MultiInstance, | |
156 | internal class STPInstancePerformanceCounter : IDisposable | 156 | counters); |
157 | { | 157 | |
158 | // Fields | 158 | } |
159 | private PerformanceCounter _pcs; | 159 | } |
160 | 160 | ||
161 | // Methods | 161 | // Properties |
162 | protected STPInstancePerformanceCounter() | 162 | public static STPPerformanceCounters Instance |
163 | { | 163 | { |
164 | } | 164 | get |
165 | 165 | { | |
166 | public STPInstancePerformanceCounter( | 166 | return _instance; |
167 | string instance, | 167 | } |
168 | STPPerformanceCounterType spcType) | 168 | } |
169 | { | 169 | } |
170 | STPPerformanceCounters counters = STPPerformanceCounters.Instance; | 170 | |
171 | _pcs = new PerformanceCounter( | 171 | internal class STPInstancePerformanceCounter : IDisposable |
172 | STPPerformanceCounters._stpCategoryName, | 172 | { |
173 | counters._stpPerformanceCounters[(int) spcType].Name, | 173 | // Fields |
174 | instance, | 174 | private bool _isDisposed; |
175 | false); | 175 | private PerformanceCounter _pcs; |
176 | _pcs.RawValue = _pcs.RawValue; | 176 | |
177 | } | 177 | // Methods |
178 | 178 | protected STPInstancePerformanceCounter() | |
179 | ~STPInstancePerformanceCounter() | 179 | { |
180 | { | 180 | _isDisposed = false; |
181 | Close(); | 181 | } |
182 | } | 182 | |
183 | 183 | public STPInstancePerformanceCounter( | |
184 | public void Close() | 184 | string instance, |
185 | { | 185 | STPPerformanceCounterType spcType) : this() |
186 | if (_pcs != null) | 186 | { |
187 | { | 187 | STPPerformanceCounters counters = STPPerformanceCounters.Instance; |
188 | _pcs.RemoveInstance(); | 188 | _pcs = new PerformanceCounter( |
189 | _pcs.Close(); | 189 | STPPerformanceCounters._stpCategoryName, |
190 | _pcs = null; | 190 | counters._stpPerformanceCounters[(int) spcType].Name, |
191 | } | 191 | instance, |
192 | } | 192 | false); |
193 | 193 | _pcs.RawValue = _pcs.RawValue; | |
194 | public void Dispose() | 194 | } |
195 | { | 195 | |
196 | Close(); | 196 | |
197 | GC.SuppressFinalize(this); | 197 | public void Close() |
198 | } | 198 | { |
199 | 199 | if (_pcs != null) | |
200 | public virtual void Increment() | 200 | { |
201 | { | 201 | _pcs.RemoveInstance(); |
202 | _pcs.Increment(); | 202 | _pcs.Close(); |
203 | } | 203 | _pcs = null; |
204 | 204 | } | |
205 | public virtual void IncrementBy(long val) | 205 | } |
206 | { | 206 | |
207 | _pcs.IncrementBy(val); | 207 | public void Dispose() |
208 | } | 208 | { |
209 | 209 | Dispose(true); | |
210 | public virtual void Set(long val) | 210 | } |
211 | { | 211 | |
212 | _pcs.RawValue = val; | 212 | public virtual void Dispose(bool disposing) |
213 | } | 213 | { |
214 | } | 214 | if (!_isDisposed) |
215 | 215 | { | |
216 | internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter | 216 | if (disposing) |
217 | { | 217 | { |
218 | // Methods | 218 | Close(); |
219 | public STPInstanceNullPerformanceCounter() {} | 219 | } |
220 | public override void Increment() {} | 220 | } |
221 | public override void IncrementBy(long value) {} | 221 | _isDisposed = true; |
222 | public override void Set(long val) {} | 222 | } |
223 | } | 223 | |
224 | 224 | public virtual void Increment() | |
225 | internal interface ISTPInstancePerformanceCounters : IDisposable | 225 | { |
226 | { | 226 | _pcs.Increment(); |
227 | void Close(); | 227 | } |
228 | void SampleThreads(long activeThreads, long inUseThreads); | 228 | |
229 | void SampleWorkItems(long workItemsQueued, long workItemsProcessed); | 229 | public virtual void IncrementBy(long val) |
230 | void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime); | 230 | { |
231 | void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime); | 231 | _pcs.IncrementBy(val); |
232 | } | 232 | } |
233 | 233 | ||
234 | 234 | public virtual void Set(long val) | |
235 | internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable | 235 | { |
236 | { | 236 | _pcs.RawValue = val; |
237 | // Fields | 237 | } |
238 | private STPInstancePerformanceCounter[] _pcs; | 238 | } |
239 | private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter; | 239 | |
240 | 240 | internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter | |
241 | // Methods | 241 | { |
242 | static STPInstancePerformanceCounters() | 242 | // Methods |
243 | { | 243 | public override void Increment() {} |
244 | _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter(); | 244 | public override void IncrementBy(long value) {} |
245 | } | 245 | public override void Set(long val) {} |
246 | 246 | } | |
247 | public STPInstancePerformanceCounters(string instance) | 247 | |
248 | { | 248 | |
249 | _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter]; | 249 | |
250 | // STPPerformanceCounters counters = STPPerformanceCounters.Instance; | 250 | internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters |
251 | for (int i = 0; i < _pcs.Length; i++) | 251 | { |
252 | { | 252 | private bool _isDisposed; |
253 | if (instance != null) | 253 | // Fields |
254 | { | 254 | private STPInstancePerformanceCounter[] _pcs; |
255 | _pcs[i] = new STPInstancePerformanceCounter( | 255 | private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter; |
256 | instance, | 256 | |
257 | (STPPerformanceCounterType) i); | 257 | // Methods |
258 | } | 258 | static STPInstancePerformanceCounters() |
259 | else | 259 | { |
260 | { | 260 | _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter(); |
261 | _pcs[i] = _stpInstanceNullPerformanceCounter; | 261 | } |
262 | } | 262 | |
263 | } | 263 | public STPInstancePerformanceCounters(string instance) |
264 | } | 264 | { |
265 | 265 | _isDisposed = false; | |
266 | 266 | _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter]; | |
267 | public void Close() | 267 | |
268 | { | 268 | // Call the STPPerformanceCounters.Instance so the static constructor will |
269 | if (null != _pcs) | 269 | // intialize the STPPerformanceCounters singleton. |
270 | { | 270 | STPPerformanceCounters.Instance.GetHashCode(); |
271 | for (int i = 0; i < _pcs.Length; i++) | 271 | |
272 | { | 272 | for (int i = 0; i < _pcs.Length; i++) |
273 | if (null != _pcs[i]) | 273 | { |
274 | { | 274 | if (instance != null) |
275 | _pcs[i].Close(); | 275 | { |
276 | } | 276 | _pcs[i] = new STPInstancePerformanceCounter( |
277 | } | 277 | instance, |
278 | _pcs = null; | 278 | (STPPerformanceCounterType) i); |
279 | } | 279 | } |
280 | } | 280 | else |
281 | 281 | { | |
282 | ~STPInstancePerformanceCounters() | 282 | _pcs[i] = _stpInstanceNullPerformanceCounter; |
283 | { | 283 | } |
284 | Close(); | 284 | } |
285 | } | 285 | } |
286 | 286 | ||
287 | public void Dispose() | 287 | |
288 | { | 288 | public void Close() |
289 | Close(); | 289 | { |
290 | GC.SuppressFinalize(this); | 290 | if (null != _pcs) |
291 | } | 291 | { |
292 | 292 | for (int i = 0; i < _pcs.Length; i++) | |
293 | private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType) | 293 | { |
294 | { | 294 | if (null != _pcs[i]) |
295 | return _pcs[(int) spcType]; | 295 | { |
296 | } | 296 | _pcs[i].Dispose(); |
297 | 297 | } | |
298 | public void SampleThreads(long activeThreads, long inUseThreads) | 298 | } |
299 | { | 299 | _pcs = null; |
300 | GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads); | 300 | } |
301 | GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads); | 301 | } |
302 | GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads); | 302 | |
303 | 303 | public void Dispose() | |
304 | GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads); | 304 | { |
305 | GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads); | 305 | Dispose(true); |
306 | } | 306 | } |
307 | 307 | ||
308 | public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) | 308 | public virtual void Dispose(bool disposing) |
309 | { | 309 | { |
310 | GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed); | 310 | if (!_isDisposed) |
311 | GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued); | 311 | { |
312 | GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed); | 312 | if (disposing) |
313 | 313 | { | |
314 | GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued); | 314 | Close(); |
315 | GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed); | 315 | } |
316 | } | 316 | } |
317 | 317 | _isDisposed = true; | |
318 | public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) | 318 | } |
319 | { | 319 | |
320 | GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds); | 320 | private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType) |
321 | GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment(); | 321 | { |
322 | } | 322 | return _pcs[(int) spcType]; |
323 | 323 | } | |
324 | public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) | 324 | |
325 | { | 325 | public void SampleThreads(long activeThreads, long inUseThreads) |
326 | GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds); | 326 | { |
327 | GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment(); | 327 | GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads); |
328 | } | 328 | GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads); |
329 | } | 329 | GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads); |
330 | 330 | ||
331 | internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable | 331 | GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads); |
332 | { | 332 | GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads); |
333 | static NullSTPInstancePerformanceCounters() | 333 | } |
334 | { | 334 | |
335 | } | 335 | public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) |
336 | 336 | { | |
337 | private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null); | 337 | GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed); |
338 | 338 | GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued); | |
339 | public static NullSTPInstancePerformanceCounters Instance | 339 | GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed); |
340 | { | 340 | |
341 | get { return _instance; } | 341 | GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued); |
342 | } | 342 | GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed); |
343 | 343 | } | |
344 | public NullSTPInstancePerformanceCounters(string instance) {} | 344 | |
345 | public void Close() {} | 345 | public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) |
346 | public void Dispose() {} | 346 | { |
347 | 347 | GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds); | |
348 | public void SampleThreads(long activeThreads, long inUseThreads) {} | 348 | GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment(); |
349 | public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {} | 349 | } |
350 | public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {} | 350 | |
351 | public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {} | 351 | public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) |
352 | } | 352 | { |
353 | 353 | GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds); | |
354 | } | 354 | GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment(); |
355 | } | ||
356 | } | ||
357 | #endif | ||
358 | |||
359 | internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader | ||
360 | { | ||
361 | private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(); | ||
362 | |||
363 | public static NullSTPInstancePerformanceCounters Instance | ||
364 | { | ||
365 | get { return _instance; } | ||
366 | } | ||
367 | |||
368 | public void Close() {} | ||
369 | public void Dispose() {} | ||
370 | |||
371 | public void SampleThreads(long activeThreads, long inUseThreads) {} | ||
372 | public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {} | ||
373 | public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {} | ||
374 | public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {} | ||
375 | public long InUseThreads | ||
376 | { | ||
377 | get { return 0; } | ||
378 | } | ||
379 | |||
380 | public long ActiveThreads | ||
381 | { | ||
382 | get { return 0; } | ||
383 | } | ||
384 | |||
385 | public long WorkItemsQueued | ||
386 | { | ||
387 | get { return 0; } | ||
388 | } | ||
389 | |||
390 | public long WorkItemsProcessed | ||
391 | { | ||
392 | get { return 0; } | ||
393 | } | ||
394 | } | ||
395 | |||
396 | internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader | ||
397 | { | ||
398 | public void Close() { } | ||
399 | public void Dispose() { } | ||
400 | |||
401 | private long _activeThreads; | ||
402 | private long _inUseThreads; | ||
403 | private long _workItemsQueued; | ||
404 | private long _workItemsProcessed; | ||
405 | |||
406 | public long InUseThreads | ||
407 | { | ||
408 | get { return _inUseThreads; } | ||
409 | } | ||
410 | |||
411 | public long ActiveThreads | ||
412 | { | ||
413 | get { return _activeThreads; } | ||
414 | } | ||
415 | |||
416 | public long WorkItemsQueued | ||
417 | { | ||
418 | get { return _workItemsQueued; } | ||
419 | } | ||
420 | |||
421 | public long WorkItemsProcessed | ||
422 | { | ||
423 | get { return _workItemsProcessed; } | ||
424 | } | ||
425 | |||
426 | public void SampleThreads(long activeThreads, long inUseThreads) | ||
427 | { | ||
428 | _activeThreads = activeThreads; | ||
429 | _inUseThreads = inUseThreads; | ||
430 | } | ||
431 | |||
432 | public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) | ||
433 | { | ||
434 | _workItemsQueued = workItemsQueued; | ||
435 | _workItemsProcessed = workItemsProcessed; | ||
436 | } | ||
437 | |||
438 | public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) | ||
439 | { | ||
440 | // Not supported | ||
441 | } | ||
442 | |||
443 | public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) | ||
444 | { | ||
445 | // Not supported | ||
446 | } | ||
447 | } | ||
448 | } | ||
diff --git a/ThirdParty/SmartThreadPool/STPStartInfo.cs b/ThirdParty/SmartThreadPool/STPStartInfo.cs index fa9ceb4..2ec8dc6 100644 --- a/ThirdParty/SmartThreadPool/STPStartInfo.cs +++ b/ThirdParty/SmartThreadPool/STPStartInfo.cs | |||
@@ -1,113 +1,212 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | using System.Threading; |
3 | 3 | ||
4 | using System.Threading; | 4 | namespace Amib.Threading |
5 | 5 | { | |
6 | namespace Amib.Threading | 6 | /// <summary> |
7 | { | 7 | /// Summary description for STPStartInfo. |
8 | /// <summary> | 8 | /// </summary> |
9 | /// Summary description for STPStartInfo. | 9 | public class STPStartInfo : WIGStartInfo |
10 | /// </summary> | 10 | { |
11 | public class STPStartInfo : WIGStartInfo | 11 | private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout; |
12 | { | 12 | private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; |
13 | /// <summary> | 13 | private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; |
14 | /// Idle timeout in milliseconds. | 14 | #if !(WINDOWS_PHONE) |
15 | /// If a thread is idle for _idleTimeout milliseconds then | 15 | private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority; |
16 | /// it may quit. | 16 | #endif |
17 | /// </summary> | 17 | private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; |
18 | private int _idleTimeout; | 18 | private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground; |
19 | 19 | private bool _enableLocalPerformanceCounters; | |
20 | /// <summary> | 20 | private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName; |
21 | /// The lower limit of threads in the pool. | 21 | private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize; |
22 | /// </summary> | 22 | |
23 | private int _minWorkerThreads; | 23 | public STPStartInfo() |
24 | 24 | { | |
25 | /// <summary> | 25 | _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; |
26 | /// The upper limit of threads in the pool. | 26 | #if !(WINDOWS_PHONE) |
27 | /// </summary> | 27 | _threadPriority = SmartThreadPool.DefaultThreadPriority; |
28 | private int _maxWorkerThreads; | 28 | #endif |
29 | 29 | _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; | |
30 | /// <summary> | 30 | _idleTimeout = SmartThreadPool.DefaultIdleTimeout; |
31 | /// The priority of the threads in the pool | 31 | _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; |
32 | /// </summary> | 32 | } |
33 | private ThreadPriority _threadPriority; | 33 | |
34 | 34 | public STPStartInfo(STPStartInfo stpStartInfo) | |
35 | /// <summary> | 35 | : base(stpStartInfo) |
36 | /// The thread pool name. Threads will get names depending on this. | 36 | { |
37 | /// </summary> | 37 | _idleTimeout = stpStartInfo.IdleTimeout; |
38 | private string _threadPoolName; | 38 | _minWorkerThreads = stpStartInfo.MinWorkerThreads; |
39 | 39 | _maxWorkerThreads = stpStartInfo.MaxWorkerThreads; | |
40 | /// <summary> | 40 | #if !(WINDOWS_PHONE) |
41 | /// If this field is not null then the performance counters are enabled | 41 | _threadPriority = stpStartInfo.ThreadPriority; |
42 | /// and use the string as the name of the instance. | 42 | #endif |
43 | /// </summary> | 43 | _performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName; |
44 | private string _pcInstanceName; | 44 | _enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters; |
45 | 45 | _threadPoolName = stpStartInfo._threadPoolName; | |
46 | private int _stackSize; | 46 | _areThreadsBackground = stpStartInfo.AreThreadsBackground; |
47 | 47 | #if !(_SILVERLIGHT) && !(WINDOWS_PHONE) | |
48 | public STPStartInfo() : base() | 48 | _apartmentState = stpStartInfo._apartmentState; |
49 | { | 49 | #endif |
50 | _idleTimeout = SmartThreadPool.DefaultIdleTimeout; | 50 | } |
51 | _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; | 51 | |
52 | _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; | 52 | /// <summary> |
53 | _threadPriority = SmartThreadPool.DefaultThreadPriority; | 53 | /// Get/Set the idle timeout in milliseconds. |
54 | _threadPoolName = SmartThreadPool.DefaultThreadPoolName; | 54 | /// If a thread is idle (starved) longer than IdleTimeout then it may quit. |
55 | _pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; | 55 | /// </summary> |
56 | _stackSize = SmartThreadPool.DefaultStackSize; | 56 | public virtual int IdleTimeout |
57 | } | 57 | { |
58 | 58 | get { return _idleTimeout; } | |
59 | public STPStartInfo(STPStartInfo stpStartInfo) : base(stpStartInfo) | 59 | set |
60 | { | 60 | { |
61 | _idleTimeout = stpStartInfo._idleTimeout; | 61 | ThrowIfReadOnly(); |
62 | _minWorkerThreads = stpStartInfo._minWorkerThreads; | 62 | _idleTimeout = value; |
63 | _maxWorkerThreads = stpStartInfo._maxWorkerThreads; | 63 | } |
64 | _threadPriority = stpStartInfo._threadPriority; | 64 | } |
65 | _threadPoolName = stpStartInfo._threadPoolName; | 65 | |
66 | _pcInstanceName = stpStartInfo._pcInstanceName; | 66 | |
67 | _stackSize = stpStartInfo._stackSize; | 67 | /// <summary> |
68 | } | 68 | /// Get/Set the lower limit of threads in the pool. |
69 | 69 | /// </summary> | |
70 | public int IdleTimeout | 70 | public virtual int MinWorkerThreads |
71 | { | 71 | { |
72 | get { return _idleTimeout; } | 72 | get { return _minWorkerThreads; } |
73 | set { _idleTimeout = value; } | 73 | set |
74 | } | 74 | { |
75 | 75 | ThrowIfReadOnly(); | |
76 | public int MinWorkerThreads | 76 | _minWorkerThreads = value; |
77 | { | 77 | } |
78 | get { return _minWorkerThreads; } | 78 | } |
79 | set { _minWorkerThreads = value; } | 79 | |
80 | } | 80 | |
81 | 81 | /// <summary> | |
82 | public int MaxWorkerThreads | 82 | /// Get/Set the upper limit of threads in the pool. |
83 | { | 83 | /// </summary> |
84 | get { return _maxWorkerThreads; } | 84 | public virtual int MaxWorkerThreads |
85 | set { _maxWorkerThreads = value; } | 85 | { |
86 | } | 86 | get { return _maxWorkerThreads; } |
87 | 87 | set | |
88 | public ThreadPriority ThreadPriority | 88 | { |
89 | { | 89 | ThrowIfReadOnly(); |
90 | get { return _threadPriority; } | 90 | _maxWorkerThreads = value; |
91 | set { _threadPriority = value; } | 91 | } |
92 | } | 92 | } |
93 | 93 | ||
94 | public virtual string ThreadPoolName | 94 | #if !(WINDOWS_PHONE) |
95 | { | 95 | /// <summary> |
96 | get { return _threadPoolName; } | 96 | /// Get/Set the scheduling priority of the threads in the pool. |
97 | set { _threadPoolName = value; } | 97 | /// The Os handles the scheduling. |
98 | } | 98 | /// </summary> |
99 | 99 | public virtual ThreadPriority ThreadPriority | |
100 | 100 | { | |
101 | public string PerformanceCounterInstanceName | 101 | get { return _threadPriority; } |
102 | { | 102 | set |
103 | get { return _pcInstanceName; } | 103 | { |
104 | set { _pcInstanceName = value; } | 104 | ThrowIfReadOnly(); |
105 | } | 105 | _threadPriority = value; |
106 | 106 | } | |
107 | public int StackSize | 107 | } |
108 | { | 108 | #endif |
109 | get { return _stackSize; } | 109 | /// <summary> |
110 | set { _stackSize = value; } | 110 | /// Get/Set the thread pool name. Threads will get names depending on this. |
111 | } | 111 | /// </summary> |
112 | } | 112 | public virtual string ThreadPoolName { |
113 | } | 113 | get { return _threadPoolName; } |
114 | set | ||
115 | { | ||
116 | ThrowIfReadOnly (); | ||
117 | _threadPoolName = value; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /// <summary> | ||
122 | /// Get/Set the performance counter instance name of this SmartThreadPool | ||
123 | /// The default is null which indicate not to use performance counters at all. | ||
124 | /// </summary> | ||
125 | public virtual string PerformanceCounterInstanceName | ||
126 | { | ||
127 | get { return _performanceCounterInstanceName; } | ||
128 | set | ||
129 | { | ||
130 | ThrowIfReadOnly(); | ||
131 | _performanceCounterInstanceName = value; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /// <summary> | ||
136 | /// Enable/Disable the local performance counter. | ||
137 | /// This enables the user to get some performance information about the SmartThreadPool | ||
138 | /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.) | ||
139 | /// The default is false. | ||
140 | /// </summary> | ||
141 | public virtual bool EnableLocalPerformanceCounters | ||
142 | { | ||
143 | get { return _enableLocalPerformanceCounters; } | ||
144 | set | ||
145 | { | ||
146 | ThrowIfReadOnly(); | ||
147 | _enableLocalPerformanceCounters = value; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /// <summary> | ||
152 | /// Get/Set backgroundness of thread in thread pool. | ||
153 | /// </summary> | ||
154 | public virtual bool AreThreadsBackground | ||
155 | { | ||
156 | get { return _areThreadsBackground; } | ||
157 | set | ||
158 | { | ||
159 | ThrowIfReadOnly (); | ||
160 | _areThreadsBackground = value; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /// <summary> | ||
165 | /// Get a readonly version of this STPStartInfo. | ||
166 | /// </summary> | ||
167 | /// <returns>Returns a readonly reference to this STPStartInfo</returns> | ||
168 | public new STPStartInfo AsReadOnly() | ||
169 | { | ||
170 | return new STPStartInfo(this) { _readOnly = true }; | ||
171 | } | ||
172 | |||
173 | #if !(_SILVERLIGHT) && !(WINDOWS_PHONE) | ||
174 | |||
175 | private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState; | ||
176 | |||
177 | /// <summary> | ||
178 | /// Get/Set the apartment state of threads in the thread pool | ||
179 | /// </summary> | ||
180 | public ApartmentState ApartmentState | ||
181 | { | ||
182 | get { return _apartmentState; } | ||
183 | set | ||
184 | { | ||
185 | ThrowIfReadOnly(); | ||
186 | _apartmentState = value; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | #if !(_SILVERLIGHT) && !(WINDOWS_PHONE) | ||
191 | |||
192 | /// <summary> | ||
193 | /// Get/Set the max stack size of threads in the thread pool | ||
194 | /// </summary> | ||
195 | public int? MaxStackSize | ||
196 | { | ||
197 | get { return _maxStackSize; } | ||
198 | set | ||
199 | { | ||
200 | ThrowIfReadOnly(); | ||
201 | if (value.HasValue && value.Value < 0) | ||
202 | { | ||
203 | throw new ArgumentOutOfRangeException("value", "Value must be greater than 0."); | ||
204 | } | ||
205 | _maxStackSize = value; | ||
206 | } | ||
207 | } | ||
208 | #endif | ||
209 | |||
210 | #endif | ||
211 | } | ||
212 | } | ||
diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs new file mode 100644 index 0000000..ba7d73f --- /dev/null +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs | |||
@@ -0,0 +1,60 @@ | |||
1 |  | ||
2 | using System; | ||
3 | using Amib.Threading.Internal; | ||
4 | |||
5 | namespace Amib.Threading | ||
6 | { | ||
7 | public partial class SmartThreadPool | ||
8 | { | ||
9 | #region ThreadEntry class | ||
10 | |||
11 | internal class ThreadEntry | ||
12 | { | ||
13 | /// <summary> | ||
14 | /// The thread creation time | ||
15 | /// The value is stored as UTC value. | ||
16 | /// </summary> | ||
17 | private readonly DateTime _creationTime; | ||
18 | |||
19 | /// <summary> | ||
20 | /// The last time this thread has been running | ||
21 | /// It is updated by IAmAlive() method | ||
22 | /// The value is stored as UTC value. | ||
23 | /// </summary> | ||
24 | private DateTime _lastAliveTime; | ||
25 | |||
26 | /// <summary> | ||
27 | /// A reference from each thread in the thread pool to its SmartThreadPool | ||
28 | /// object container. | ||
29 | /// With this variable a thread can know whatever it belongs to a | ||
30 | /// SmartThreadPool. | ||
31 | /// </summary> | ||
32 | private readonly SmartThreadPool _associatedSmartThreadPool; | ||
33 | |||
34 | /// <summary> | ||
35 | /// A reference to the current work item a thread from the thread pool | ||
36 | /// is executing. | ||
37 | /// </summary> | ||
38 | public WorkItem CurrentWorkItem { get; set; } | ||
39 | |||
40 | public ThreadEntry(SmartThreadPool stp) | ||
41 | { | ||
42 | _associatedSmartThreadPool = stp; | ||
43 | _creationTime = DateTime.UtcNow; | ||
44 | _lastAliveTime = DateTime.MinValue; | ||
45 | } | ||
46 | |||
47 | public SmartThreadPool AssociatedSmartThreadPool | ||
48 | { | ||
49 | get { return _associatedSmartThreadPool; } | ||
50 | } | ||
51 | |||
52 | public void IAmAlive() | ||
53 | { | ||
54 | _lastAliveTime = DateTime.UtcNow; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | #endregion | ||
59 | } | ||
60 | } \ No newline at end of file | ||
diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs index 19a0007..9256777 100644 --- a/ThirdParty/SmartThreadPool/SmartThreadPool.cs +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs | |||
@@ -1,1448 +1,1732 @@ | |||
1 | // Ami Bar | 1 | #region Release History |
2 | // amibar@gmail.com | 2 | |
3 | // | 3 | // Smart Thread Pool |
4 | // Smart thread pool in C#. | 4 | // 7 Aug 2004 - Initial release |
5 | // 7 Aug 2004 - Initial release | 5 | // |
6 | // 14 Sep 2004 - Bug fixes | 6 | // 14 Sep 2004 - Bug fixes |
7 | // 15 Oct 2004 - Added new features | 7 | // |
8 | // - Work items return result. | 8 | // 15 Oct 2004 - Added new features |
9 | // - Support waiting synchronization for multiple work items. | 9 | // - Work items return result. |
10 | // - Work items can be cancelled. | 10 | // - Support waiting synchronization for multiple work items. |
11 | // - Passage of the caller thread’s context to the thread in the pool. | 11 | // - Work items can be cancelled. |
12 | // - Minimal usage of WIN32 handles. | 12 | // - Passage of the caller thread’s context to the thread in the pool. |
13 | // - Minor bug fixes. | 13 | // - Minimal usage of WIN32 handles. |
14 | // 26 Dec 2004 - Changes: | 14 | // - Minor bug fixes. |
15 | // - Removed static constructors. | 15 | // |
16 | // - Added finalizers. | 16 | // 26 Dec 2004 - Changes: |
17 | // - Changed Exceptions so they are serializable. | 17 | // - Removed static constructors. |
18 | // - Fixed the bug in one of the SmartThreadPool constructors. | 18 | // - Added finalizers. |
19 | // - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. | 19 | // - Changed Exceptions so they are serializable. |
20 | // The SmartThreadPool.WaitAny() is still limited by the .NET Framework. | 20 | // - Fixed the bug in one of the SmartThreadPool constructors. |
21 | // - Added PostExecute with options on which cases to call it. | 21 | // - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. |
22 | // - Added option to dispose of the state objects. | 22 | // The SmartThreadPool.WaitAny() is still limited by the .NET Framework. |
23 | // - Added a WaitForIdle() method that waits until the work items queue is empty. | 23 | // - Added PostExecute with options on which cases to call it. |
24 | // - Added an STPStartInfo class for the initialization of the thread pool. | 24 | // - Added option to dispose of the state objects. |
25 | // - Changed exception handling so if a work item throws an exception it | 25 | // - Added a WaitForIdle() method that waits until the work items queue is empty. |
26 | // is rethrown at GetResult(), rather then firing an UnhandledException event. | 26 | // - Added an STPStartInfo class for the initialization of the thread pool. |
27 | // Note that PostExecute exception are always ignored. | 27 | // - Changed exception handling so if a work item throws an exception it |
28 | // 25 Mar 2005 - Changes: | 28 | // is rethrown at GetResult(), rather then firing an UnhandledException event. |
29 | // - Fixed lost of work items bug | 29 | // Note that PostExecute exception are always ignored. |
30 | // 3 Jul 2005: Changes. | 30 | // |
31 | // - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. | 31 | // 25 Mar 2005 - Changes: |
32 | // 16 Aug 2005: Changes. | 32 | // - Fixed lost of work items bug |
33 | // - Fixed bug where the InUseThreads becomes negative when canceling work items. | 33 | // |
34 | // | 34 | // 3 Jul 2005: Changes. |
35 | // 31 Jan 2006 - Changes: | 35 | // - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. |
36 | // - Added work items priority | 36 | // |
37 | // - Removed support of chained delegates in callbacks and post executes (nobody really use this) | 37 | // 16 Aug 2005: Changes. |
38 | // - Added work items groups | 38 | // - Fixed bug where the InUseThreads becomes negative when canceling work items. |
39 | // - Added work items groups idle event | 39 | // |
40 | // - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array | 40 | // 31 Jan 2006 - Changes: |
41 | // it returns true rather then throwing an exception. | 41 | // - Added work items priority |
42 | // - Added option to start the STP and the WIG as suspended | 42 | // - Removed support of chained delegates in callbacks and post executes (nobody really use this) |
43 | // - Exception behavior changed, the real exception is returned by an | 43 | // - Added work items groups |
44 | // inner exception | 44 | // - Added work items groups idle event |
45 | // - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) | 45 | // - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array |
46 | // - Added performance counters | 46 | // it returns true rather then throwing an exception. |
47 | // - Added priority to the threads in the pool | 47 | // - Added option to start the STP and the WIG as suspended |
48 | // | 48 | // - Exception behavior changed, the real exception is returned by an |
49 | // 13 Feb 2006 - Changes: | 49 | // inner exception |
50 | // - Added a call to the dispose of the Performance Counter so | 50 | // - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) |
51 | // their won't be a Performance Counter leak. | 51 | // - Added performance counters |
52 | // - Added exception catch in case the Performance Counters cannot | 52 | // - Added priority to the threads in the pool |
53 | // be created. | 53 | // |
54 | 54 | // 13 Feb 2006 - Changes: | |
55 | using System; | 55 | // - Added a call to the dispose of the Performance Counter so |
56 | using System.Security; | 56 | // their won't be a Performance Counter leak. |
57 | using System.Threading; | 57 | // - Added exception catch in case the Performance Counters cannot |
58 | using System.Collections; | 58 | // be created. |
59 | using System.Diagnostics; | 59 | // |
60 | using System.Runtime.CompilerServices; | 60 | // 17 May 2008 - Changes: |
61 | 61 | // - Changed the dispose behavior and removed the Finalizers. | |
62 | using Amib.Threading.Internal; | 62 | // - Enabled the change of the MaxThreads and MinThreads at run time. |
63 | 63 | // - Enabled the change of the Concurrency of a IWorkItemsGroup at run | |
64 | namespace Amib.Threading | 64 | // time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency |
65 | { | 65 | // refers to the MaxThreads. |
66 | #region SmartThreadPool class | 66 | // - Improved the cancel behavior. |
67 | /// <summary> | 67 | // - Added events for thread creation and termination. |
68 | /// Smart thread pool class. | 68 | // - Fixed the HttpContext context capture. |
69 | /// </summary> | 69 | // - Changed internal collections so they use generic collections |
70 | public class SmartThreadPool : IWorkItemsGroup, IDisposable | 70 | // - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup |
71 | { | 71 | // - Added support for WinCE |
72 | #region Default Constants | 72 | // - Added support for Action<T> and Func<T> |
73 | 73 | // | |
74 | /// <summary> | 74 | // 07 April 2009 - Changes: |
75 | /// Default minimum number of threads the thread pool contains. (0) | 75 | // - Added support for Silverlight and Mono |
76 | /// </summary> | 76 | // - Added Join, Choice, and Pipe to SmartThreadPool. |
77 | public const int DefaultMinWorkerThreads = 0; | 77 | // - Added local performance counters (for Mono, Silverlight, and WindowsCE) |
78 | 78 | // - Changed duration measures from DateTime.Now to Stopwatch. | |
79 | /// <summary> | 79 | // - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList<T>. |
80 | /// Default maximum number of threads the thread pool contains. (25) | 80 | // |
81 | /// </summary> | 81 | // 21 December 2009 - Changes: |
82 | public const int DefaultMaxWorkerThreads = 25; | 82 | // - Added work item timeout (passive) |
83 | 83 | // | |
84 | /// <summary> | 84 | // 20 August 2012 - Changes: |
85 | /// Default idle timeout in milliseconds. (One minute) | 85 | // - Added set name to threads |
86 | /// </summary> | 86 | // - Fixed the WorkItemsQueue.Dequeue. |
87 | public const int DefaultIdleTimeout = 60*1000; // One minute | 87 | // Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... } |
88 | 88 | // - Fixed SmartThreadPool.Pipe | |
89 | /// <summary> | 89 | // - Added IsBackground option to threads |
90 | /// Indicate to copy the security context of the caller and then use it in the call. (false) | 90 | // - Added ApartmentState to threads |
91 | /// </summary> | 91 | // - Fixed thread creation when queuing many work items at the same time. |
92 | public const bool DefaultUseCallerCallContext = false; | 92 | // |
93 | 93 | // 24 August 2012 - Changes: | |
94 | /// <summary> | 94 | // - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan |
95 | /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) | 95 | // - Added option to set MaxStackSize of threads |
96 | /// </summary> | 96 | |
97 | public const bool DefaultUseCallerHttpContext = false; | 97 | #endregion |
98 | 98 | ||
99 | /// <summary> | 99 | using System; |
100 | /// Indicate to dispose of the state objects if they support the IDispose interface. (false) | 100 | using System.Security; |
101 | /// </summary> | 101 | using System.Threading; |
102 | public const bool DefaultDisposeOfStateObjects = false; | 102 | using System.Collections; |
103 | 103 | using System.Collections.Generic; | |
104 | /// <summary> | 104 | using System.Diagnostics; |
105 | /// The default option to run the post execute | 105 | using System.Runtime.CompilerServices; |
106 | /// </summary> | 106 | |
107 | public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; | 107 | using Amib.Threading.Internal; |
108 | 108 | ||
109 | /// <summary> | 109 | namespace Amib.Threading |
110 | /// The default post execute method to run. | 110 | { |
111 | /// When null it means not to call it. | 111 | #region SmartThreadPool class |
112 | /// </summary> | 112 | /// <summary> |
113 | public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null; | 113 | /// Smart thread pool class. |
114 | 114 | /// </summary> | |
115 | /// <summary> | 115 | public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable |
116 | /// The default work item priority | 116 | { |
117 | /// </summary> | 117 | #region Public Default Constants |
118 | public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; | 118 | |
119 | 119 | /// <summary> | |
120 | /// <summary> | 120 | /// Default minimum number of threads the thread pool contains. (0) |
121 | /// The default is to work on work items as soon as they arrive | 121 | /// </summary> |
122 | /// and not to wait for the start. | 122 | public const int DefaultMinWorkerThreads = 0; |
123 | /// </summary> | 123 | |
124 | public const bool DefaultStartSuspended = false; | 124 | /// <summary> |
125 | 125 | /// Default maximum number of threads the thread pool contains. (25) | |
126 | /// <summary> | 126 | /// </summary> |
127 | /// The default is not to use the performance counters | 127 | public const int DefaultMaxWorkerThreads = 25; |
128 | /// </summary> | 128 | |
129 | public static readonly string DefaultPerformanceCounterInstanceName = null; | 129 | /// <summary> |
130 | 130 | /// Default idle timeout in milliseconds. (One minute) | |
131 | public static readonly int DefaultStackSize = 0; | 131 | /// </summary> |
132 | 132 | public const int DefaultIdleTimeout = 60*1000; // One minute | |
133 | /// <summary> | 133 | |
134 | /// The default thread priority | 134 | /// <summary> |
135 | /// </summary> | 135 | /// Indicate to copy the security context of the caller and then use it in the call. (false) |
136 | public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; | 136 | /// </summary> |
137 | 137 | public const bool DefaultUseCallerCallContext = false; | |
138 | /// <summary> | 138 | |
139 | /// The default thread pool name | 139 | /// <summary> |
140 | /// </summary> | 140 | /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) |
141 | public const string DefaultThreadPoolName = "SmartThreadPool"; | 141 | /// </summary> |
142 | 142 | public const bool DefaultUseCallerHttpContext = false; | |
143 | #endregion | 143 | |
144 | 144 | /// <summary> | |
145 | #region Member Variables | 145 | /// Indicate to dispose of the state objects if they support the IDispose interface. (false) |
146 | 146 | /// </summary> | |
147 | /// <summary> | 147 | public const bool DefaultDisposeOfStateObjects = false; |
148 | /// Contains the name of this instance of SmartThreadPool. | 148 | |
149 | /// Can be changed by the user. | 149 | /// <summary> |
150 | /// </summary> | 150 | /// The default option to run the post execute (CallToPostExecute.Always) |
151 | private string _name = DefaultThreadPoolName; | 151 | /// </summary> |
152 | 152 | public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; | |
153 | /// <summary> | 153 | |
154 | /// Hashtable of all the threads in the thread pool. | 154 | /// <summary> |
155 | /// </summary> | 155 | /// The default post execute method to run. (None) |
156 | private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable()); | 156 | /// When null it means not to call it. |
157 | 157 | /// </summary> | |
158 | /// <summary> | 158 | public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback; |
159 | /// Queue of work items. | 159 | |
160 | /// </summary> | 160 | /// <summary> |
161 | private WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); | 161 | /// The default work item priority (WorkItemPriority.Normal) |
162 | 162 | /// </summary> | |
163 | /// <summary> | 163 | public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; |
164 | /// Count the work items handled. | 164 | |
165 | /// Used by the performance counter. | 165 | /// <summary> |
166 | /// </summary> | 166 | /// The default is to work on work items as soon as they arrive |
167 | private long _workItemsProcessed = 0; | 167 | /// and not to wait for the start. (false) |
168 | 168 | /// </summary> | |
169 | /// <summary> | 169 | public const bool DefaultStartSuspended = false; |
170 | /// Number of threads that currently work (not idle). | 170 | |
171 | /// </summary> | 171 | /// <summary> |
172 | private int _inUseWorkerThreads = 0; | 172 | /// The default name to use for the performance counters instance. (null) |
173 | 173 | /// </summary> | |
174 | /// <summary> | 174 | public static readonly string DefaultPerformanceCounterInstanceName; |
175 | /// Start information to use. | 175 | |
176 | /// It is simpler than providing many constructors. | 176 | #if !(WINDOWS_PHONE) |
177 | /// </summary> | 177 | |
178 | private STPStartInfo _stpStartInfo = new STPStartInfo(); | 178 | /// <summary> |
179 | 179 | /// The default thread priority (ThreadPriority.Normal) | |
180 | /// <summary> | 180 | /// </summary> |
181 | /// Total number of work items that are stored in the work items queue | 181 | public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; |
182 | /// plus the work items that the threads in the pool are working on. | 182 | #endif |
183 | /// </summary> | 183 | /// <summary> |
184 | private int _currentWorkItemsCount = 0; | 184 | /// The default thread pool name. (SmartThreadPool) |
185 | 185 | /// </summary> | |
186 | /// <summary> | 186 | public const string DefaultThreadPoolName = "SmartThreadPool"; |
187 | /// Signaled when the thread pool is idle, i.e. no thread is busy | 187 | |
188 | /// and the work items queue is empty | 188 | /// <summary> |
189 | /// </summary> | 189 | /// The default Max Stack Size. (SmartThreadPool) |
190 | private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); | 190 | /// </summary> |
191 | 191 | public static readonly int? DefaultMaxStackSize = null; | |
192 | /// <summary> | 192 | |
193 | /// An event to signal all the threads to quit immediately. | 193 | /// <summary> |
194 | /// </summary> | 194 | /// The default fill state with params. (false) |
195 | private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); | 195 | /// It is relevant only to QueueWorkItem of Action<...>/Func<...> |
196 | 196 | /// </summary> | |
197 | /// <summary> | 197 | public const bool DefaultFillStateWithArgs = false; |
198 | /// A flag to indicate the threads to quit. | 198 | |
199 | /// </summary> | 199 | /// <summary> |
200 | private bool _shutdown = false; | 200 | /// The default thread backgroundness. (true) |
201 | 201 | /// </summary> | |
202 | /// <summary> | 202 | public const bool DefaultAreThreadsBackground = true; |
203 | /// Counts the threads created in the pool. | 203 | |
204 | /// It is used to name the threads. | 204 | #if !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
205 | /// </summary> | 205 | /// <summary> |
206 | private int _threadCounter = 0; | 206 | /// The default apartment state of a thread in the thread pool. |
207 | 207 | /// The default is ApartmentState.Unknown which means the STP will not | |
208 | /// <summary> | 208 | /// set the apartment of the thread. It will use the .NET default. |
209 | /// Indicate that the SmartThreadPool has been disposed | 209 | /// </summary> |
210 | /// </summary> | 210 | public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; |
211 | private bool _isDisposed = false; | 211 | #endif |
212 | 212 | ||
213 | /// <summary> | 213 | #endregion |
214 | /// Event to send that the thread pool is idle | 214 | |
215 | /// </summary> | 215 | #region Member Variables |
216 | private event EventHandler _stpIdle; | 216 | |
217 | 217 | /// <summary> | |
218 | /// <summary> | 218 | /// Dictionary of all the threads in the thread pool. |
219 | /// On idle event | 219 | /// </summary> |
220 | /// </summary> | 220 | private readonly SynchronizedDictionary<Thread, ThreadEntry> _workerThreads = new SynchronizedDictionary<Thread, ThreadEntry>(); |
221 | //private event WorkItemsGroupIdleHandler _onIdle; | 221 | |
222 | 222 | /// <summary> | |
223 | /// <summary> | 223 | /// Queue of work items. |
224 | /// Holds all the WorkItemsGroup instaces that have at least one | 224 | /// </summary> |
225 | /// work item int the SmartThreadPool | 225 | private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); |
226 | /// This variable is used in case of Shutdown | 226 | |
227 | /// </summary> | 227 | /// <summary> |
228 | private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable()); | 228 | /// Count the work items handled. |
229 | 229 | /// Used by the performance counter. | |
230 | /// <summary> | 230 | /// </summary> |
231 | /// A reference from each thread in the thread pool to its SmartThreadPool | 231 | private int _workItemsProcessed; |
232 | /// object container. | 232 | |
233 | /// With this variable a thread can know whatever it belongs to a | 233 | /// <summary> |
234 | /// SmartThreadPool. | 234 | /// Number of threads that currently work (not idle). |
235 | /// </summary> | 235 | /// </summary> |
236 | [ThreadStatic] | 236 | private int _inUseWorkerThreads; |
237 | private static SmartThreadPool _smartThreadPool; | 237 | |
238 | 238 | /// <summary> | |
239 | /// <summary> | 239 | /// Stores a copy of the original STPStartInfo. |
240 | /// A reference to the current work item a thread from the thread pool | 240 | /// It is used to change the MinThread and MaxThreads |
241 | /// is executing. | 241 | /// </summary> |
242 | /// </summary> | 242 | private STPStartInfo _stpStartInfo; |
243 | [ThreadStatic] | 243 | |
244 | private static WorkItem _currentWorkItem; | 244 | /// <summary> |
245 | 245 | /// Total number of work items that are stored in the work items queue | |
246 | /// <summary> | 246 | /// plus the work items that the threads in the pool are working on. |
247 | /// STP performance counters | 247 | /// </summary> |
248 | /// </summary> | 248 | private int _currentWorkItemsCount; |
249 | private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance; | 249 | |
250 | 250 | /// <summary> | |
251 | #endregion | 251 | /// Signaled when the thread pool is idle, i.e. no thread is busy |
252 | 252 | /// and the work items queue is empty | |
253 | #region Construction and Finalization | 253 | /// </summary> |
254 | 254 | //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); | |
255 | /// <summary> | 255 | private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); |
256 | /// Constructor | 256 | |
257 | /// </summary> | 257 | /// <summary> |
258 | public SmartThreadPool() | 258 | /// An event to signal all the threads to quit immediately. |
259 | { | 259 | /// </summary> |
260 | Initialize(); | 260 | //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); |
261 | } | 261 | private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false); |
262 | 262 | ||
263 | /// <summary> | 263 | /// <summary> |
264 | /// Constructor | 264 | /// A flag to indicate if the Smart Thread Pool is now suspended. |
265 | /// </summary> | 265 | /// </summary> |
266 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | 266 | private bool _isSuspended; |
267 | public SmartThreadPool(int idleTimeout) | 267 | |
268 | { | 268 | /// <summary> |
269 | _stpStartInfo.IdleTimeout = idleTimeout; | 269 | /// A flag to indicate the threads to quit. |
270 | Initialize(); | 270 | /// </summary> |
271 | } | 271 | private bool _shutdown; |
272 | 272 | ||
273 | /// <summary> | 273 | /// <summary> |
274 | /// Constructor | 274 | /// Counts the threads created in the pool. |
275 | /// </summary> | 275 | /// It is used to name the threads. |
276 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | 276 | /// </summary> |
277 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> | 277 | private int _threadCounter; |
278 | public SmartThreadPool( | 278 | |
279 | int idleTimeout, | 279 | /// <summary> |
280 | int maxWorkerThreads) | 280 | /// Indicate that the SmartThreadPool has been disposed |
281 | { | 281 | /// </summary> |
282 | _stpStartInfo.IdleTimeout = idleTimeout; | 282 | private bool _isDisposed; |
283 | _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; | 283 | |
284 | Initialize(); | 284 | /// <summary> |
285 | } | 285 | /// Holds all the WorkItemsGroup instaces that have at least one |
286 | 286 | /// work item int the SmartThreadPool | |
287 | /// <summary> | 287 | /// This variable is used in case of Shutdown |
288 | /// Constructor | 288 | /// </summary> |
289 | /// </summary> | 289 | private readonly SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup> _workItemsGroups = new SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup>(); |
290 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | 290 | |
291 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> | 291 | /// <summary> |
292 | /// <param name="minWorkerThreads">Lower limit of threads in the pool</param> | 292 | /// A common object for all the work items int the STP |
293 | public SmartThreadPool( | 293 | /// so we can mark them to cancel in O(1) |
294 | int idleTimeout, | 294 | /// </summary> |
295 | int maxWorkerThreads, | 295 | private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup(); |
296 | int minWorkerThreads) | 296 | |
297 | { | 297 | /// <summary> |
298 | _stpStartInfo.IdleTimeout = idleTimeout; | 298 | /// Windows STP performance counters |
299 | _stpStartInfo.MaxWorkerThreads = maxWorkerThreads; | 299 | /// </summary> |
300 | _stpStartInfo.MinWorkerThreads = minWorkerThreads; | 300 | private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance; |
301 | Initialize(); | 301 | |
302 | } | 302 | /// <summary> |
303 | 303 | /// Local STP performance counters | |
304 | /// <summary> | 304 | /// </summary> |
305 | /// Constructor | 305 | private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance; |
306 | /// </summary> | 306 | |
307 | public SmartThreadPool(STPStartInfo stpStartInfo) | 307 | |
308 | { | 308 | #if (WINDOWS_PHONE) |
309 | _stpStartInfo = new STPStartInfo(stpStartInfo); | 309 | private static readonly Dictionary<int, ThreadEntry> _threadEntries = new Dictionary<int, ThreadEntry>(); |
310 | Initialize(); | 310 | #elif (_WINDOWS_CE) |
311 | } | 311 | private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot(); |
312 | 312 | #else | |
313 | private void Initialize() | 313 | [ThreadStatic] |
314 | { | 314 | private static ThreadEntry _threadEntry; |
315 | Name = _stpStartInfo.ThreadPoolName; | 315 | |
316 | ValidateSTPStartInfo(); | 316 | #endif |
317 | 317 | ||
318 | if (null != _stpStartInfo.PerformanceCounterInstanceName) | 318 | /// <summary> |
319 | { | 319 | /// An event to call after a thread is created, but before |
320 | try | 320 | /// it's first use. |
321 | { | 321 | /// </summary> |
322 | _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); | 322 | private event ThreadInitializationHandler _onThreadInitialization; |
323 | } | 323 | |
324 | catch(Exception e) | 324 | /// <summary> |
325 | { | 325 | /// An event to call when a thread is about to exit, after |
326 | Debug.WriteLine("Unable to create Performance Counters: " + e.ToString()); | 326 | /// it is no longer belong to the pool. |
327 | _pcs = NullSTPInstancePerformanceCounters.Instance; | 327 | /// </summary> |
328 | } | 328 | private event ThreadTerminationHandler _onThreadTermination; |
329 | } | 329 | |
330 | 330 | #endregion | |
331 | StartOptimalNumberOfThreads(); | 331 | |
332 | } | 332 | #region Per thread properties |
333 | 333 | ||
334 | private void StartOptimalNumberOfThreads() | 334 | /// <summary> |
335 | { | 335 | /// A reference to the current work item a thread from the thread pool |
336 | int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); | 336 | /// is executing. |
337 | threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); | 337 | /// </summary> |
338 | StartThreads(threadsCount); | 338 | internal static ThreadEntry CurrentThreadEntry |
339 | } | 339 | { |
340 | 340 | #if (WINDOWS_PHONE) | |
341 | private void ValidateSTPStartInfo() | 341 | get |
342 | { | 342 | { |
343 | if (_stpStartInfo.MinWorkerThreads < 0) | 343 | lock(_threadEntries) |
344 | { | 344 | { |
345 | throw new ArgumentOutOfRangeException( | 345 | ThreadEntry threadEntry; |
346 | "MinWorkerThreads", "MinWorkerThreads cannot be negative"); | 346 | if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry)) |
347 | } | 347 | { |
348 | 348 | return threadEntry; | |
349 | if (_stpStartInfo.MaxWorkerThreads <= 0) | 349 | } |
350 | { | 350 | } |
351 | throw new ArgumentOutOfRangeException( | 351 | return null; |
352 | "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); | 352 | } |
353 | } | 353 | set |
354 | 354 | { | |
355 | if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) | 355 | lock(_threadEntries) |
356 | { | 356 | { |
357 | throw new ArgumentOutOfRangeException( | 357 | _threadEntries[Thread.CurrentThread.ManagedThreadId] = value; |
358 | "MinWorkerThreads, maxWorkerThreads", | 358 | } |
359 | "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); | 359 | } |
360 | } | 360 | #elif (_WINDOWS_CE) |
361 | } | 361 | get |
362 | 362 | { | |
363 | private void ValidateCallback(Delegate callback) | 363 | //Thread.CurrentThread.ManagedThreadId |
364 | { | 364 | return Thread.GetData(_threadEntrySlot) as ThreadEntry; |
365 | if(callback.GetInvocationList().Length > 1) | 365 | } |
366 | { | 366 | set |
367 | throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); | 367 | { |
368 | } | 368 | Thread.SetData(_threadEntrySlot, value); |
369 | } | 369 | } |
370 | 370 | #else | |
371 | #endregion | 371 | get |
372 | 372 | { | |
373 | #region Thread Processing | 373 | return _threadEntry; |
374 | 374 | } | |
375 | /// <summary> | 375 | set |
376 | /// Waits on the queue for a work item, shutdown, or timeout. | 376 | { |
377 | /// </summary> | 377 | _threadEntry = value; |
378 | /// <returns> | 378 | } |
379 | /// Returns the WaitingCallback or null in case of timeout or shutdown. | 379 | #endif |
380 | /// </returns> | 380 | } |
381 | private WorkItem Dequeue() | 381 | #endregion |
382 | { | 382 | |
383 | WorkItem workItem = | 383 | #region Construction and Finalization |
384 | _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); | 384 | |
385 | 385 | /// <summary> | |
386 | return workItem; | 386 | /// Constructor |
387 | } | 387 | /// </summary> |
388 | 388 | public SmartThreadPool() | |
389 | /// <summary> | 389 | { |
390 | /// Put a new work item in the queue | 390 | _stpStartInfo = new STPStartInfo(); |
391 | /// </summary> | 391 | Initialize(); |
392 | /// <param name="workItem">A work item to queue</param> | 392 | } |
393 | private void Enqueue(WorkItem workItem) | 393 | |
394 | { | 394 | /// <summary> |
395 | Enqueue(workItem, true); | 395 | /// Constructor |
396 | } | 396 | /// </summary> |
397 | 397 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> | |
398 | /// <summary> | 398 | public SmartThreadPool(int idleTimeout) |
399 | /// Put a new work item in the queue | 399 | { |
400 | /// </summary> | 400 | _stpStartInfo = new STPStartInfo |
401 | /// <param name="workItem">A work item to queue</param> | 401 | { |
402 | internal void Enqueue(WorkItem workItem, bool incrementWorkItems) | 402 | IdleTimeout = idleTimeout, |
403 | { | 403 | }; |
404 | // Make sure the workItem is not null | 404 | Initialize(); |
405 | Debug.Assert(null != workItem); | 405 | } |
406 | 406 | ||
407 | if (incrementWorkItems) | 407 | /// <summary> |
408 | { | 408 | /// Constructor |
409 | IncrementWorkItemsCount(); | 409 | /// </summary> |
410 | } | 410 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> |
411 | 411 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> | |
412 | _workItemsQueue.EnqueueWorkItem(workItem); | 412 | public SmartThreadPool( |
413 | workItem.WorkItemIsQueued(); | 413 | int idleTimeout, |
414 | 414 | int maxWorkerThreads) | |
415 | // If all the threads are busy then try to create a new one | 415 | { |
416 | if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count) | 416 | _stpStartInfo = new STPStartInfo |
417 | { | 417 | { |
418 | StartThreads(1); | 418 | IdleTimeout = idleTimeout, |
419 | } | 419 | MaxWorkerThreads = maxWorkerThreads, |
420 | } | 420 | }; |
421 | 421 | Initialize(); | |
422 | private void IncrementWorkItemsCount() | 422 | } |
423 | { | 423 | |
424 | _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); | 424 | /// <summary> |
425 | 425 | /// Constructor | |
426 | int count = Interlocked.Increment(ref _currentWorkItemsCount); | 426 | /// </summary> |
427 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); | 427 | /// <param name="idleTimeout">Idle timeout in milliseconds</param> |
428 | if (count == 1) | 428 | /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> |
429 | { | 429 | /// <param name="minWorkerThreads">Lower limit of threads in the pool</param> |
430 | //Trace.WriteLine("STP is NOT idle"); | 430 | public SmartThreadPool( |
431 | _isIdleWaitHandle.Reset(); | 431 | int idleTimeout, |
432 | } | 432 | int maxWorkerThreads, |
433 | } | 433 | int minWorkerThreads) |
434 | 434 | { | |
435 | private void DecrementWorkItemsCount() | 435 | _stpStartInfo = new STPStartInfo |
436 | { | 436 | { |
437 | ++_workItemsProcessed; | 437 | IdleTimeout = idleTimeout, |
438 | 438 | MaxWorkerThreads = maxWorkerThreads, | |
439 | // The counter counts even if the work item was cancelled | 439 | MinWorkerThreads = minWorkerThreads, |
440 | _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); | 440 | }; |
441 | 441 | Initialize(); | |
442 | int count = Interlocked.Decrement(ref _currentWorkItemsCount); | 442 | } |
443 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); | 443 | |
444 | if (count == 0) | 444 | /// <summary> |
445 | { | 445 | /// Constructor |
446 | //Trace.WriteLine("STP is idle"); | 446 | /// </summary> |
447 | _isIdleWaitHandle.Set(); | 447 | /// <param name="stpStartInfo">A SmartThreadPool configuration that overrides the default behavior</param> |
448 | } | 448 | public SmartThreadPool(STPStartInfo stpStartInfo) |
449 | } | 449 | { |
450 | 450 | _stpStartInfo = new STPStartInfo(stpStartInfo); | |
451 | internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | 451 | Initialize(); |
452 | { | 452 | } |
453 | _workItemsGroups[workItemsGroup] = workItemsGroup; | 453 | |
454 | } | 454 | private void Initialize() |
455 | 455 | { | |
456 | internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | 456 | Name = _stpStartInfo.ThreadPoolName; |
457 | { | 457 | ValidateSTPStartInfo(); |
458 | if (_workItemsGroups.Contains(workItemsGroup)) | 458 | |
459 | { | 459 | // _stpStartInfoRW stores a read/write copy of the STPStartInfo. |
460 | _workItemsGroups.Remove(workItemsGroup); | 460 | // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten |
461 | } | 461 | |
462 | } | 462 | _isSuspended = _stpStartInfo.StartSuspended; |
463 | 463 | ||
464 | /// <summary> | 464 | #if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE) |
465 | /// Inform that the current thread is about to quit or quiting. | 465 | if (null != _stpStartInfo.PerformanceCounterInstanceName) |
466 | /// The same thread may call this method more than once. | 466 | { |
467 | /// </summary> | 467 | throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); |
468 | private void InformCompleted() | 468 | } |
469 | { | 469 | #else |
470 | // There is no need to lock the two methods together | 470 | if (null != _stpStartInfo.PerformanceCounterInstanceName) |
471 | // since only the current thread removes itself | 471 | { |
472 | // and the _workerThreads is a synchronized hashtable | 472 | try |
473 | if (_workerThreads.Contains(Thread.CurrentThread)) | 473 | { |
474 | { | 474 | _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); |
475 | _workerThreads.Remove(Thread.CurrentThread); | 475 | } |
476 | _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); | 476 | catch (Exception e) |
477 | } | 477 | { |
478 | } | 478 | Debug.WriteLine("Unable to create Performance Counters: " + e); |
479 | 479 | _windowsPCs = NullSTPInstancePerformanceCounters.Instance; | |
480 | /// <summary> | 480 | } |
481 | /// Starts new threads | 481 | } |
482 | /// </summary> | 482 | #endif |
483 | /// <param name="threadsCount">The number of threads to start</param> | 483 | |
484 | private void StartThreads(int threadsCount) | 484 | if (_stpStartInfo.EnableLocalPerformanceCounters) |
485 | { | 485 | { |
486 | if (_stpStartInfo.StartSuspended) | 486 | _localPCs = new LocalSTPInstancePerformanceCounters(); |
487 | { | 487 | } |
488 | return; | 488 | |
489 | } | 489 | // If the STP is not started suspended then start the threads. |
490 | 490 | if (!_isSuspended) | |
491 | lock(_workerThreads.SyncRoot) | 491 | { |
492 | { | 492 | StartOptimalNumberOfThreads(); |
493 | // Don't start threads on shut down | 493 | } |
494 | if (_shutdown) | 494 | } |
495 | { | 495 | |
496 | return; | 496 | private void StartOptimalNumberOfThreads() |
497 | } | 497 | { |
498 | 498 | int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); | |
499 | for(int i = 0; i < threadsCount; ++i) | 499 | threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); |
500 | { | 500 | threadsCount -= _workerThreads.Count; |
501 | // Don't create more threads then the upper limit | 501 | if (threadsCount > 0) |
502 | if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) | 502 | { |
503 | { | 503 | StartThreads(threadsCount); |
504 | return; | 504 | } |
505 | } | 505 | } |
506 | 506 | ||
507 | // Create a new thread | 507 | private void ValidateSTPStartInfo() |
508 | Thread workerThread; | 508 | { |
509 | if (_stpStartInfo.StackSize > 0) | 509 | if (_stpStartInfo.MinWorkerThreads < 0) |
510 | workerThread = new Thread(ProcessQueuedItems, _stpStartInfo.StackSize); | 510 | { |
511 | else | 511 | throw new ArgumentOutOfRangeException( |
512 | workerThread = new Thread(ProcessQueuedItems); | 512 | "MinWorkerThreads", "MinWorkerThreads cannot be negative"); |
513 | 513 | } | |
514 | // Configure the new thread and start it | 514 | |
515 | workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; | 515 | if (_stpStartInfo.MaxWorkerThreads <= 0) |
516 | workerThread.IsBackground = true; | 516 | { |
517 | workerThread.Priority = _stpStartInfo.ThreadPriority; | 517 | throw new ArgumentOutOfRangeException( |
518 | workerThread.Start(); | 518 | "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); |
519 | ++_threadCounter; | 519 | } |
520 | 520 | ||
521 | // Add the new thread to the hashtable and update its creation | 521 | if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) |
522 | // time. | 522 | { |
523 | _workerThreads[workerThread] = DateTime.Now; | 523 | throw new ArgumentOutOfRangeException( |
524 | _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); | 524 | "MinWorkerThreads, maxWorkerThreads", |
525 | } | 525 | "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); |
526 | } | 526 | } |
527 | } | 527 | } |
528 | 528 | ||
529 | /// <summary> | 529 | private static void ValidateCallback(Delegate callback) |
530 | /// A worker thread method that processes work items from the work items queue. | 530 | { |
531 | /// </summary> | 531 | if(callback.GetInvocationList().Length > 1) |
532 | private void ProcessQueuedItems() | 532 | { |
533 | { | 533 | throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); |
534 | // Initialize the _smartThreadPool variable | 534 | } |
535 | _smartThreadPool = this; | 535 | } |
536 | 536 | ||
537 | try | 537 | #endregion |
538 | { | 538 | |
539 | bool bInUseWorkerThreadsWasIncremented = false; | 539 | #region Thread Processing |
540 | 540 | ||
541 | // Process until shutdown. | 541 | /// <summary> |
542 | while(!_shutdown) | 542 | /// Waits on the queue for a work item, shutdown, or timeout. |
543 | { | 543 | /// </summary> |
544 | // Update the last time this thread was seen alive. | 544 | /// <returns> |
545 | // It's good for debugging. | 545 | /// Returns the WaitingCallback or null in case of timeout or shutdown. |
546 | _workerThreads[Thread.CurrentThread] = DateTime.Now; | 546 | /// </returns> |
547 | 547 | private WorkItem Dequeue() | |
548 | // Wait for a work item, shutdown, or timeout | 548 | { |
549 | WorkItem workItem = Dequeue(); | 549 | WorkItem workItem = |
550 | 550 | _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); | |
551 | // Update the last time this thread was seen alive. | 551 | |
552 | // It's good for debugging. | 552 | return workItem; |
553 | _workerThreads[Thread.CurrentThread] = DateTime.Now; | 553 | } |
554 | 554 | ||
555 | // On timeout or shut down. | 555 | /// <summary> |
556 | if (null == workItem) | 556 | /// Put a new work item in the queue |
557 | { | 557 | /// </summary> |
558 | // Double lock for quit. | 558 | /// <param name="workItem">A work item to queue</param> |
559 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) | 559 | internal override void Enqueue(WorkItem workItem) |
560 | { | 560 | { |
561 | lock(_workerThreads.SyncRoot) | 561 | // Make sure the workItem is not null |
562 | { | 562 | Debug.Assert(null != workItem); |
563 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) | 563 | |
564 | { | 564 | IncrementWorkItemsCount(); |
565 | // Inform that the thread is quiting and then quit. | 565 | |
566 | // This method must be called within this lock or else | 566 | workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; |
567 | // more threads will quit and the thread pool will go | 567 | _workItemsQueue.EnqueueWorkItem(workItem); |
568 | // below the lower limit. | 568 | workItem.WorkItemIsQueued(); |
569 | InformCompleted(); | 569 | |
570 | break; | 570 | // If all the threads are busy then try to create a new one |
571 | } | 571 | if (_currentWorkItemsCount > _workerThreads.Count) |
572 | } | 572 | { |
573 | } | 573 | StartThreads(1); |
574 | } | 574 | } |
575 | 575 | } | |
576 | // If we didn't quit then skip to the next iteration. | 576 | |
577 | if (null == workItem) | 577 | private void IncrementWorkItemsCount() |
578 | { | 578 | { |
579 | continue; | 579 | _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
580 | } | 580 | _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
581 | 581 | ||
582 | try | 582 | int count = Interlocked.Increment(ref _currentWorkItemsCount); |
583 | { | 583 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); |
584 | // Initialize the value to false | 584 | if (count == 1) |
585 | bInUseWorkerThreadsWasIncremented = false; | 585 | { |
586 | 586 | IsIdle = false; | |
587 | // Change the state of the work item to 'in progress' if possible. | 587 | _isIdleWaitHandle.Reset(); |
588 | // We do it here so if the work item has been canceled we won't | 588 | } |
589 | // increment the _inUseWorkerThreads. | 589 | } |
590 | // The cancel mechanism doesn't delete items from the queue, | 590 | |
591 | // it marks the work item as canceled, and when the work item | 591 | private void DecrementWorkItemsCount() |
592 | // is dequeued, we just skip it. | 592 | { |
593 | // If the post execute of work item is set to always or to | 593 | int count = Interlocked.Decrement(ref _currentWorkItemsCount); |
594 | // call when the work item is canceled then the StartingWorkItem() | 594 | //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); |
595 | // will return true, so the post execute can run. | 595 | if (count == 0) |
596 | if (!workItem.StartingWorkItem()) | 596 | { |
597 | { | 597 | IsIdle = true; |
598 | continue; | 598 | _isIdleWaitHandle.Set(); |
599 | } | 599 | } |
600 | 600 | ||
601 | // Execute the callback. Make sure to accurately | 601 | Interlocked.Increment(ref _workItemsProcessed); |
602 | // record how many callbacks are currently executing. | 602 | |
603 | int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); | 603 | if (!_shutdown) |
604 | _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); | 604 | { |
605 | 605 | // The counter counts even if the work item was cancelled | |
606 | // Mark that the _inUseWorkerThreads incremented, so in the finally{} | 606 | _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
607 | // statement we will decrement it correctly. | 607 | _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); |
608 | bInUseWorkerThreadsWasIncremented = true; | 608 | } |
609 | 609 | ||
610 | // Set the _currentWorkItem to the current work item | 610 | } |
611 | _currentWorkItem = workItem; | 611 | |
612 | 612 | internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | |
613 | lock(workItem) | 613 | { |
614 | { | 614 | _workItemsGroups[workItemsGroup] = workItemsGroup; |
615 | workItem.currentThread = Thread.CurrentThread; | 615 | } |
616 | } | 616 | |
617 | 617 | internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) | |
618 | ExecuteWorkItem(workItem); | 618 | { |
619 | 619 | if (_workItemsGroups.Contains(workItemsGroup)) | |
620 | lock(workItem) | 620 | { |
621 | { | 621 | _workItemsGroups.Remove(workItemsGroup); |
622 | workItem.currentThread = null; | 622 | } |
623 | } | 623 | } |
624 | 624 | ||
625 | } | 625 | /// <summary> |
626 | catch(ThreadAbortException ex) | 626 | /// Inform that the current thread is about to quit or quiting. |
627 | { | 627 | /// The same thread may call this method more than once. |
628 | lock(workItem) | 628 | /// </summary> |
629 | { | 629 | private void InformCompleted() |
630 | workItem.currentThread = null; | 630 | { |
631 | } | 631 | // There is no need to lock the two methods together |
632 | ex.GetHashCode(); | 632 | // since only the current thread removes itself |
633 | Thread.ResetAbort(); | 633 | // and the _workerThreads is a synchronized dictionary |
634 | } | 634 | if (_workerThreads.Contains(Thread.CurrentThread)) |
635 | catch(Exception ex) | 635 | { |
636 | { | 636 | _workerThreads.Remove(Thread.CurrentThread); |
637 | ex.GetHashCode(); | 637 | _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
638 | // Do nothing | 638 | _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
639 | } | 639 | } |
640 | finally | 640 | } |
641 | { | 641 | |
642 | lock(workItem) | 642 | /// <summary> |
643 | { | 643 | /// Starts new threads |
644 | workItem.currentThread = null; | 644 | /// </summary> |
645 | } | 645 | /// <param name="threadsCount">The number of threads to start</param> |
646 | 646 | private void StartThreads(int threadsCount) | |
647 | if (null != workItem) | 647 | { |
648 | { | 648 | if (_isSuspended) |
649 | workItem.DisposeOfState(); | 649 | { |
650 | } | 650 | return; |
651 | 651 | } | |
652 | // Set the _currentWorkItem to null, since we | 652 | |
653 | // no longer run user's code. | 653 | lock(_workerThreads.SyncRoot) |
654 | _currentWorkItem = null; | 654 | { |
655 | 655 | // Don't start threads on shut down | |
656 | // Decrement the _inUseWorkerThreads only if we had | 656 | if (_shutdown) |
657 | // incremented it. Note the cancelled work items don't | 657 | { |
658 | // increment _inUseWorkerThreads. | 658 | return; |
659 | if (bInUseWorkerThreadsWasIncremented) | 659 | } |
660 | { | 660 | |
661 | int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); | 661 | for(int i = 0; i < threadsCount; ++i) |
662 | _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); | 662 | { |
663 | } | 663 | // Don't create more threads then the upper limit |
664 | 664 | if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) | |
665 | // Notify that the work item has been completed. | 665 | { |
666 | // WorkItemsGroup may enqueue their next work item. | 666 | return; |
667 | workItem.FireWorkItemCompleted(); | 667 | } |
668 | 668 | ||
669 | // Decrement the number of work items here so the idle | 669 | // Create a new thread |
670 | // ManualResetEvent won't fluctuate. | 670 | |
671 | DecrementWorkItemsCount(); | 671 | #if (_SILVERLIGHT) || (WINDOWS_PHONE) |
672 | } | 672 | Thread workerThread = new Thread(ProcessQueuedItems); |
673 | } | 673 | #else |
674 | } | 674 | Thread workerThread = |
675 | catch(ThreadAbortException tae) | 675 | _stpStartInfo.MaxStackSize.HasValue |
676 | { | 676 | ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) |
677 | tae.GetHashCode(); | 677 | : new Thread(ProcessQueuedItems); |
678 | // Handle the abort exception gracfully. | 678 | #endif |
679 | Thread.ResetAbort(); | 679 | // Configure the new thread and start it |
680 | } | 680 | workerThread.Name = "STP " + Name + " Thread #" + _threadCounter; |
681 | catch(Exception e) | 681 | workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; |
682 | { | 682 | |
683 | Debug.Assert(null != e); | 683 | #if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE) |
684 | } | 684 | if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) |
685 | finally | 685 | { |
686 | { | 686 | workerThread.SetApartmentState(_stpStartInfo.ApartmentState); |
687 | InformCompleted(); | 687 | } |
688 | } | 688 | #endif |
689 | } | 689 | |
690 | 690 | #if !(_SILVERLIGHT) && !(WINDOWS_PHONE) | |
691 | private void ExecuteWorkItem(WorkItem workItem) | 691 | workerThread.Priority = _stpStartInfo.ThreadPriority; |
692 | { | 692 | #endif |
693 | _pcs.SampleWorkItemsWaitTime(workItem.WaitingTime); | 693 | workerThread.Start(); |
694 | try | 694 | ++_threadCounter; |
695 | { | 695 | |
696 | workItem.Execute(); | 696 | // Add it to the dictionary and update its creation time. |
697 | } | 697 | _workerThreads[workerThread] = new ThreadEntry(this); |
698 | catch | 698 | |
699 | { | 699 | _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
700 | throw; | 700 | _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); |
701 | } | 701 | } |
702 | finally | 702 | } |
703 | { | 703 | } |
704 | _pcs.SampleWorkItemsProcessTime(workItem.ProcessTime); | 704 | |
705 | } | 705 | /// <summary> |
706 | } | 706 | /// A worker thread method that processes work items from the work items queue. |
707 | 707 | /// </summary> | |
708 | 708 | private void ProcessQueuedItems() | |
709 | #endregion | 709 | { |
710 | 710 | // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks | |
711 | #region Public Methods | 711 | // of the dictionary. |
712 | 712 | CurrentThreadEntry = _workerThreads[Thread.CurrentThread]; | |
713 | /// <summary> | 713 | |
714 | /// Queue a work item | 714 | FireOnThreadInitialization(); |
715 | /// </summary> | 715 | |
716 | /// <param name="callback">A callback to execute</param> | 716 | try |
717 | /// <returns>Returns a work item result</returns> | 717 | { |
718 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback) | 718 | bool bInUseWorkerThreadsWasIncremented = false; |
719 | { | 719 | |
720 | ValidateNotDisposed(); | 720 | // Process until shutdown. |
721 | ValidateCallback(callback); | 721 | while(!_shutdown) |
722 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback); | 722 | { |
723 | Enqueue(workItem); | 723 | // Update the last time this thread was seen alive. |
724 | return workItem.GetWorkItemResult(); | 724 | // It's good for debugging. |
725 | } | 725 | CurrentThreadEntry.IAmAlive(); |
726 | 726 | ||
727 | /// <summary> | 727 | // The following block handles the when the MaxWorkerThreads has been |
728 | /// Queue a work item | 728 | // incremented by the user at run-time. |
729 | /// </summary> | 729 | // Double lock for quit. |
730 | /// <param name="callback">A callback to execute</param> | 730 | if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) |
731 | /// <param name="workItemPriority">The priority of the work item</param> | 731 | { |
732 | /// <returns>Returns a work item result</returns> | 732 | lock (_workerThreads.SyncRoot) |
733 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) | 733 | { |
734 | { | 734 | if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) |
735 | ValidateNotDisposed(); | 735 | { |
736 | ValidateCallback(callback); | 736 | // Inform that the thread is quiting and then quit. |
737 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority); | 737 | // This method must be called within this lock or else |
738 | Enqueue(workItem); | 738 | // more threads will quit and the thread pool will go |
739 | return workItem.GetWorkItemResult(); | 739 | // below the lower limit. |
740 | } | 740 | InformCompleted(); |
741 | 741 | break; | |
742 | /// <summary> | 742 | } |
743 | /// Queue a work item | 743 | } |
744 | /// </summary> | 744 | } |
745 | /// <param name="workItemInfo">Work item info</param> | 745 | |
746 | /// <param name="callback">A callback to execute</param> | 746 | // Wait for a work item, shutdown, or timeout |
747 | /// <returns>Returns a work item result</returns> | 747 | WorkItem workItem = Dequeue(); |
748 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) | 748 | |
749 | { | 749 | // Update the last time this thread was seen alive. |
750 | ValidateNotDisposed(); | 750 | // It's good for debugging. |
751 | ValidateCallback(callback); | 751 | CurrentThreadEntry.IAmAlive(); |
752 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback); | 752 | |
753 | Enqueue(workItem); | 753 | // On timeout or shut down. |
754 | return workItem.GetWorkItemResult(); | 754 | if (null == workItem) |
755 | } | 755 | { |
756 | 756 | // Double lock for quit. | |
757 | /// <summary> | 757 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) |
758 | /// Queue a work item | 758 | { |
759 | /// </summary> | 759 | lock(_workerThreads.SyncRoot) |
760 | /// <param name="callback">A callback to execute</param> | 760 | { |
761 | /// <param name="state"> | 761 | if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) |
762 | /// The context object of the work item. Used for passing arguments to the work item. | 762 | { |
763 | /// </param> | 763 | // Inform that the thread is quiting and then quit. |
764 | /// <returns>Returns a work item result</returns> | 764 | // This method must be called within this lock or else |
765 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) | 765 | // more threads will quit and the thread pool will go |
766 | { | 766 | // below the lower limit. |
767 | ValidateNotDisposed(); | 767 | InformCompleted(); |
768 | ValidateCallback(callback); | 768 | break; |
769 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state); | 769 | } |
770 | Enqueue(workItem); | 770 | } |
771 | return workItem.GetWorkItemResult(); | 771 | } |
772 | } | 772 | } |
773 | 773 | ||
774 | /// <summary> | 774 | // If we didn't quit then skip to the next iteration. |
775 | /// Queue a work item | 775 | if (null == workItem) |
776 | /// </summary> | 776 | { |
777 | /// <param name="callback">A callback to execute</param> | 777 | continue; |
778 | /// <param name="state"> | 778 | } |
779 | /// The context object of the work item. Used for passing arguments to the work item. | 779 | |
780 | /// </param> | 780 | try |
781 | /// <param name="workItemPriority">The work item priority</param> | 781 | { |
782 | /// <returns>Returns a work item result</returns> | 782 | // Initialize the value to false |
783 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) | 783 | bInUseWorkerThreadsWasIncremented = false; |
784 | { | 784 | |
785 | ValidateNotDisposed(); | 785 | // Set the Current Work Item of the thread. |
786 | ValidateCallback(callback); | 786 | // Store the Current Work Item before the workItem.StartingWorkItem() is called, |
787 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority); | 787 | // so WorkItem.Cancel can work when the work item is between InQueue and InProgress |
788 | Enqueue(workItem); | 788 | // states. |
789 | return workItem.GetWorkItemResult(); | 789 | // If the work item has been cancelled BEFORE the workItem.StartingWorkItem() |
790 | } | 790 | // (work item is in InQueue state) then workItem.StartingWorkItem() will return false. |
791 | 791 | // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then | |
792 | /// <summary> | 792 | // (work item is in InProgress state) then the thread will be aborted |
793 | /// Queue a work item | 793 | CurrentThreadEntry.CurrentWorkItem = workItem; |
794 | /// </summary> | 794 | |
795 | /// <param name="workItemInfo">Work item information</param> | 795 | // Change the state of the work item to 'in progress' if possible. |
796 | /// <param name="callback">A callback to execute</param> | 796 | // We do it here so if the work item has been canceled we won't |
797 | /// <param name="state"> | 797 | // increment the _inUseWorkerThreads. |
798 | /// The context object of the work item. Used for passing arguments to the work item. | 798 | // The cancel mechanism doesn't delete items from the queue, |
799 | /// </param> | 799 | // it marks the work item as canceled, and when the work item |
800 | /// <returns>Returns a work item result</returns> | 800 | // is dequeued, we just skip it. |
801 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) | 801 | // If the post execute of work item is set to always or to |
802 | { | 802 | // call when the work item is canceled then the StartingWorkItem() |
803 | ValidateNotDisposed(); | 803 | // will return true, so the post execute can run. |
804 | ValidateCallback(callback); | 804 | if (!workItem.StartingWorkItem()) |
805 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state); | 805 | { |
806 | Enqueue(workItem); | 806 | continue; |
807 | return workItem.GetWorkItemResult(); | 807 | } |
808 | } | 808 | |
809 | 809 | // Execute the callback. Make sure to accurately | |
810 | /// <summary> | 810 | // record how many callbacks are currently executing. |
811 | /// Queue a work item | 811 | int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); |
812 | /// </summary> | 812 | _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
813 | /// <param name="callback">A callback to execute</param> | 813 | _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
814 | /// <param name="state"> | 814 | |
815 | /// The context object of the work item. Used for passing arguments to the work item. | 815 | // Mark that the _inUseWorkerThreads incremented, so in the finally{} |
816 | /// </param> | 816 | // statement we will decrement it correctly. |
817 | /// <param name="postExecuteWorkItemCallback"> | 817 | bInUseWorkerThreadsWasIncremented = true; |
818 | /// A delegate to call after the callback completion | 818 | |
819 | /// </param> | 819 | workItem.FireWorkItemStarted(); |
820 | /// <returns>Returns a work item result</returns> | 820 | |
821 | public IWorkItemResult QueueWorkItem( | 821 | ExecuteWorkItem(workItem); |
822 | WorkItemCallback callback, | 822 | } |
823 | object state, | 823 | catch(Exception ex) |
824 | PostExecuteWorkItemCallback postExecuteWorkItemCallback) | 824 | { |
825 | { | 825 | ex.GetHashCode(); |
826 | ValidateNotDisposed(); | 826 | // Do nothing |
827 | ValidateCallback(callback); | 827 | } |
828 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback); | 828 | finally |
829 | Enqueue(workItem); | 829 | { |
830 | return workItem.GetWorkItemResult(); | 830 | workItem.DisposeOfState(); |
831 | } | 831 | |
832 | 832 | // Set the CurrentWorkItem to null, since we | |
833 | /// <summary> | 833 | // no longer run user's code. |
834 | /// Queue a work item | 834 | CurrentThreadEntry.CurrentWorkItem = null; |
835 | /// </summary> | 835 | |
836 | /// <param name="callback">A callback to execute</param> | 836 | // Decrement the _inUseWorkerThreads only if we had |
837 | /// <param name="state"> | 837 | // incremented it. Note the cancelled work items don't |
838 | /// The context object of the work item. Used for passing arguments to the work item. | 838 | // increment _inUseWorkerThreads. |
839 | /// </param> | 839 | if (bInUseWorkerThreadsWasIncremented) |
840 | /// <param name="postExecuteWorkItemCallback"> | 840 | { |
841 | /// A delegate to call after the callback completion | 841 | int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); |
842 | /// </param> | 842 | _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
843 | /// <param name="workItemPriority">The work item priority</param> | 843 | _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); |
844 | /// <returns>Returns a work item result</returns> | 844 | } |
845 | public IWorkItemResult QueueWorkItem( | 845 | |
846 | WorkItemCallback callback, | 846 | // Notify that the work item has been completed. |
847 | object state, | 847 | // WorkItemsGroup may enqueue their next work item. |
848 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 848 | workItem.FireWorkItemCompleted(); |
849 | WorkItemPriority workItemPriority) | 849 | |
850 | { | 850 | // Decrement the number of work items here so the idle |
851 | ValidateNotDisposed(); | 851 | // ManualResetEvent won't fluctuate. |
852 | ValidateCallback(callback); | 852 | DecrementWorkItemsCount(); |
853 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); | 853 | } |
854 | Enqueue(workItem); | 854 | } |
855 | return workItem.GetWorkItemResult(); | 855 | } |
856 | } | 856 | catch(ThreadAbortException tae) |
857 | 857 | { | |
858 | /// <summary> | 858 | tae.GetHashCode(); |
859 | /// Queue a work item | 859 | // Handle the abort exception gracfully. |
860 | /// </summary> | 860 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
861 | /// <param name="callback">A callback to execute</param> | 861 | Thread.ResetAbort(); |
862 | /// <param name="state"> | 862 | #endif |
863 | /// The context object of the work item. Used for passing arguments to the work item. | 863 | } |
864 | /// </param> | 864 | catch(Exception e) |
865 | /// <param name="postExecuteWorkItemCallback"> | 865 | { |
866 | /// A delegate to call after the callback completion | 866 | Debug.Assert(null != e); |
867 | /// </param> | 867 | } |
868 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 868 | finally |
869 | /// <returns>Returns a work item result</returns> | 869 | { |
870 | public IWorkItemResult QueueWorkItem( | 870 | InformCompleted(); |
871 | WorkItemCallback callback, | 871 | FireOnThreadTermination(); |
872 | object state, | 872 | } |
873 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 873 | } |
874 | CallToPostExecute callToPostExecute) | 874 | |
875 | { | 875 | private void ExecuteWorkItem(WorkItem workItem) |
876 | ValidateNotDisposed(); | 876 | { |
877 | ValidateCallback(callback); | 877 | _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); |
878 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); | 878 | _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); |
879 | Enqueue(workItem); | 879 | try |
880 | return workItem.GetWorkItemResult(); | 880 | { |
881 | } | 881 | workItem.Execute(); |
882 | 882 | } | |
883 | /// <summary> | 883 | finally |
884 | /// Queue a work item | 884 | { |
885 | /// </summary> | 885 | _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); |
886 | /// <param name="callback">A callback to execute</param> | 886 | _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); |
887 | /// <param name="state"> | 887 | } |
888 | /// The context object of the work item. Used for passing arguments to the work item. | 888 | } |
889 | /// </param> | 889 | |
890 | /// <param name="postExecuteWorkItemCallback"> | 890 | |
891 | /// A delegate to call after the callback completion | 891 | #endregion |
892 | /// </param> | 892 | |
893 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 893 | #region Public Methods |
894 | /// <param name="workItemPriority">The work item priority</param> | 894 | |
895 | /// <returns>Returns a work item result</returns> | 895 | private void ValidateWaitForIdle() |
896 | public IWorkItemResult QueueWorkItem( | 896 | { |
897 | WorkItemCallback callback, | 897 | if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this) |
898 | object state, | 898 | { |
899 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 899 | throw new NotSupportedException( |
900 | CallToPostExecute callToPostExecute, | 900 | "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); |
901 | WorkItemPriority workItemPriority) | 901 | } |
902 | { | 902 | } |
903 | ValidateNotDisposed(); | 903 | |
904 | ValidateCallback(callback); | 904 | internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) |
905 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); | 905 | { |
906 | Enqueue(workItem); | 906 | if (null == CurrentThreadEntry) |
907 | return workItem.GetWorkItemResult(); | 907 | { |
908 | } | 908 | return; |
909 | 909 | } | |
910 | /// <summary> | 910 | |
911 | /// Wait for the thread pool to be idle | 911 | WorkItem workItem = CurrentThreadEntry.CurrentWorkItem; |
912 | /// </summary> | 912 | ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem); |
913 | public void WaitForIdle() | 913 | if ((null != workItemsGroup) && |
914 | { | 914 | (null != workItem) && |
915 | WaitForIdle(Timeout.Infinite); | 915 | CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup)) |
916 | } | 916 | { |
917 | 917 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); | |
918 | /// <summary> | 918 | } |
919 | /// Wait for the thread pool to be idle | 919 | } |
920 | /// </summary> | 920 | |
921 | public bool WaitForIdle(TimeSpan timeout) | 921 | [MethodImpl(MethodImplOptions.NoInlining)] |
922 | { | 922 | private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) |
923 | return WaitForIdle((int)timeout.TotalMilliseconds); | 923 | { |
924 | } | 924 | if ((null != workItemsGroup) && |
925 | 925 | (null != workItem) && | |
926 | /// <summary> | 926 | workItem.WasQueuedBy(workItemsGroup)) |
927 | /// Wait for the thread pool to be idle | 927 | { |
928 | /// </summary> | 928 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); |
929 | public bool WaitForIdle(int millisecondsTimeout) | 929 | } |
930 | { | 930 | } |
931 | ValidateWaitForIdle(); | 931 | |
932 | return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); | 932 | /// <summary> |
933 | } | 933 | /// Force the SmartThreadPool to shutdown |
934 | 934 | /// </summary> | |
935 | private void ValidateWaitForIdle() | 935 | public void Shutdown() |
936 | { | 936 | { |
937 | if(_smartThreadPool == this) | 937 | Shutdown(true, 0); |
938 | { | 938 | } |
939 | throw new NotSupportedException( | 939 | |
940 | "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); | 940 | /// <summary> |
941 | } | 941 | /// Force the SmartThreadPool to shutdown with timeout |
942 | } | 942 | /// </summary> |
943 | 943 | public void Shutdown(bool forceAbort, TimeSpan timeout) | |
944 | internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) | 944 | { |
945 | { | 945 | Shutdown(forceAbort, (int)timeout.TotalMilliseconds); |
946 | ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem); | 946 | } |
947 | if ((null != workItemsGroup) && | 947 | |
948 | (null != SmartThreadPool._currentWorkItem) && | 948 | /// <summary> |
949 | SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup)) | 949 | /// Empties the queue of work items and abort the threads in the pool. |
950 | { | 950 | /// </summary> |
951 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); | 951 | public void Shutdown(bool forceAbort, int millisecondsTimeout) |
952 | } | 952 | { |
953 | } | 953 | ValidateNotDisposed(); |
954 | 954 | ||
955 | [MethodImpl(MethodImplOptions.NoInlining)] | 955 | ISTPInstancePerformanceCounters pcs = _windowsPCs; |
956 | private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) | 956 | |
957 | { | 957 | if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs) |
958 | if ((null != workItemsGroup) && | 958 | { |
959 | (null != workItem) && | 959 | // Set the _pcs to "null" to stop updating the performance |
960 | workItem.WasQueuedBy(workItemsGroup)) | 960 | // counters |
961 | { | 961 | _windowsPCs = NullSTPInstancePerformanceCounters.Instance; |
962 | throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); | 962 | |
963 | } | 963 | pcs.Dispose(); |
964 | } | 964 | } |
965 | 965 | ||
966 | 966 | Thread [] threads; | |
967 | 967 | lock(_workerThreads.SyncRoot) | |
968 | /// <summary> | 968 | { |
969 | /// Force the SmartThreadPool to shutdown | 969 | // Shutdown the work items queue |
970 | /// </summary> | 970 | _workItemsQueue.Dispose(); |
971 | public void Shutdown() | 971 | |
972 | { | 972 | // Signal the threads to exit |
973 | Shutdown(true, 0); | 973 | _shutdown = true; |
974 | } | 974 | _shuttingDownEvent.Set(); |
975 | 975 | ||
976 | public void Shutdown(bool forceAbort, TimeSpan timeout) | 976 | // Make a copy of the threads' references in the pool |
977 | { | 977 | threads = new Thread [_workerThreads.Count]; |
978 | Shutdown(forceAbort, (int)timeout.TotalMilliseconds); | 978 | _workerThreads.Keys.CopyTo(threads, 0); |
979 | } | 979 | } |
980 | 980 | ||
981 | /// <summary> | 981 | int millisecondsLeft = millisecondsTimeout; |
982 | /// Empties the queue of work items and abort the threads in the pool. | 982 | Stopwatch stopwatch = Stopwatch.StartNew(); |
983 | /// </summary> | 983 | //DateTime start = DateTime.UtcNow; |
984 | public void Shutdown(bool forceAbort, int millisecondsTimeout) | 984 | bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); |
985 | { | 985 | bool timeout = false; |
986 | ValidateNotDisposed(); | 986 | |
987 | 987 | // Each iteration we update the time left for the timeout. | |
988 | ISTPInstancePerformanceCounters pcs = _pcs; | 988 | foreach(Thread thread in threads) |
989 | 989 | { | |
990 | if (NullSTPInstancePerformanceCounters.Instance != _pcs) | 990 | // Join don't work with negative numbers |
991 | { | 991 | if (!waitInfinitely && (millisecondsLeft < 0)) |
992 | _pcs.Dispose(); | 992 | { |
993 | // Set the _pcs to "null" to stop updating the performance | 993 | timeout = true; |
994 | // counters | 994 | break; |
995 | _pcs = NullSTPInstancePerformanceCounters.Instance; | 995 | } |
996 | } | 996 | |
997 | 997 | // Wait for the thread to terminate | |
998 | Thread [] threads = null; | 998 | bool success = thread.Join(millisecondsLeft); |
999 | lock(_workerThreads.SyncRoot) | 999 | if(!success) |
1000 | { | 1000 | { |
1001 | // Shutdown the work items queue | 1001 | timeout = true; |
1002 | _workItemsQueue.Dispose(); | 1002 | break; |
1003 | 1003 | } | |
1004 | // Signal the threads to exit | 1004 | |
1005 | _shutdown = true; | 1005 | if(!waitInfinitely) |
1006 | _shuttingDownEvent.Set(); | 1006 | { |
1007 | 1007 | // Update the time left to wait | |
1008 | // Make a copy of the threads' references in the pool | 1008 | //TimeSpan ts = DateTime.UtcNow - start; |
1009 | threads = new Thread [_workerThreads.Count]; | 1009 | millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; |
1010 | _workerThreads.Keys.CopyTo(threads, 0); | 1010 | } |
1011 | } | 1011 | } |
1012 | 1012 | ||
1013 | int millisecondsLeft = millisecondsTimeout; | 1013 | if (timeout && forceAbort) |
1014 | DateTime start = DateTime.Now; | 1014 | { |
1015 | bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); | 1015 | // Abort the threads in the pool |
1016 | bool timeout = false; | 1016 | foreach(Thread thread in threads) |
1017 | 1017 | { | |
1018 | // Each iteration we update the time left for the timeout. | 1018 | |
1019 | foreach(Thread thread in threads) | 1019 | if ((thread != null) |
1020 | { | 1020 | #if !(_WINDOWS_CE) |
1021 | // Join don't work with negative numbers | 1021 | && thread.IsAlive |
1022 | if (!waitInfinitely && (millisecondsLeft < 0)) | 1022 | #endif |
1023 | { | 1023 | ) |
1024 | timeout = true; | 1024 | { |
1025 | break; | 1025 | try |
1026 | } | 1026 | { |
1027 | 1027 | thread.Abort(); // Shutdown | |
1028 | // Wait for the thread to terminate | 1028 | } |
1029 | bool success = thread.Join(millisecondsLeft); | 1029 | catch(SecurityException e) |
1030 | if(!success) | 1030 | { |
1031 | { | 1031 | e.GetHashCode(); |
1032 | timeout = true; | 1032 | } |
1033 | break; | 1033 | catch(ThreadStateException ex) |
1034 | } | 1034 | { |
1035 | 1035 | ex.GetHashCode(); | |
1036 | if(!waitInfinitely) | 1036 | // In case the thread has been terminated |
1037 | { | 1037 | // after the check if it is alive. |
1038 | // Update the time left to wait | 1038 | } |
1039 | TimeSpan ts = DateTime.Now - start; | 1039 | } |
1040 | millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds; | 1040 | } |
1041 | } | 1041 | } |
1042 | } | 1042 | } |
1043 | 1043 | ||
1044 | if (timeout && forceAbort) | 1044 | /// <summary> |
1045 | { | 1045 | /// Wait for all work items to complete |
1046 | // Abort the threads in the pool | 1046 | /// </summary> |
1047 | foreach(Thread thread in threads) | 1047 | /// <param name="waitableResults">Array of work item result objects</param> |
1048 | { | 1048 | /// <returns> |
1049 | if ((thread != null) && thread.IsAlive) | 1049 | /// true when every work item in workItemResults has completed; otherwise false. |
1050 | { | 1050 | /// </returns> |
1051 | try | 1051 | public static bool WaitAll( |
1052 | { | 1052 | IWaitableResult [] waitableResults) |
1053 | thread.Abort("Shutdown"); | 1053 | { |
1054 | } | 1054 | return WaitAll(waitableResults, Timeout.Infinite, true); |
1055 | catch(SecurityException e) | 1055 | } |
1056 | { | 1056 | |
1057 | e.GetHashCode(); | 1057 | /// <summary> |
1058 | } | 1058 | /// Wait for all work items to complete |
1059 | catch(ThreadStateException ex) | 1059 | /// </summary> |
1060 | { | 1060 | /// <param name="waitableResults">Array of work item result objects</param> |
1061 | ex.GetHashCode(); | 1061 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1062 | // In case the thread has been terminated | 1062 | /// <param name="exitContext"> |
1063 | // after the check if it is alive. | 1063 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1064 | } | 1064 | /// </param> |
1065 | } | 1065 | /// <returns> |
1066 | } | 1066 | /// true when every work item in workItemResults has completed; otherwise false. |
1067 | } | 1067 | /// </returns> |
1068 | 1068 | public static bool WaitAll( | |
1069 | // Dispose of the performance counters | 1069 | IWaitableResult [] waitableResults, |
1070 | pcs.Dispose(); | 1070 | TimeSpan timeout, |
1071 | } | 1071 | bool exitContext) |
1072 | 1072 | { | |
1073 | /// <summary> | 1073 | return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext); |
1074 | /// Wait for all work items to complete | 1074 | } |
1075 | /// </summary> | 1075 | |
1076 | /// <param name="workItemResults">Array of work item result objects</param> | 1076 | /// <summary> |
1077 | /// <returns> | 1077 | /// Wait for all work items to complete |
1078 | /// true when every work item in workItemResults has completed; otherwise false. | 1078 | /// </summary> |
1079 | /// </returns> | 1079 | /// <param name="waitableResults">Array of work item result objects</param> |
1080 | public static bool WaitAll( | 1080 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1081 | IWorkItemResult [] workItemResults) | 1081 | /// <param name="exitContext"> |
1082 | { | 1082 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1083 | return WaitAll(workItemResults, Timeout.Infinite, true); | 1083 | /// </param> |
1084 | } | 1084 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1085 | 1085 | /// <returns> | |
1086 | /// <summary> | 1086 | /// true when every work item in workItemResults has completed; otherwise false. |
1087 | /// Wait for all work items to complete | 1087 | /// </returns> |
1088 | /// </summary> | 1088 | public static bool WaitAll( |
1089 | /// <param name="workItemResults">Array of work item result objects</param> | 1089 | IWaitableResult[] waitableResults, |
1090 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1090 | TimeSpan timeout, |
1091 | /// <param name="exitContext"> | 1091 | bool exitContext, |
1092 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1092 | WaitHandle cancelWaitHandle) |
1093 | /// </param> | 1093 | { |
1094 | /// <returns> | 1094 | return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); |
1095 | /// true when every work item in workItemResults has completed; otherwise false. | 1095 | } |
1096 | /// </returns> | 1096 | |
1097 | public static bool WaitAll( | 1097 | /// <summary> |
1098 | IWorkItemResult [] workItemResults, | 1098 | /// Wait for all work items to complete |
1099 | TimeSpan timeout, | 1099 | /// </summary> |
1100 | bool exitContext) | 1100 | /// <param name="waitableResults">Array of work item result objects</param> |
1101 | { | 1101 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1102 | return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext); | 1102 | /// <param name="exitContext"> |
1103 | } | 1103 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1104 | 1104 | /// </param> | |
1105 | /// <summary> | 1105 | /// <returns> |
1106 | /// Wait for all work items to complete | 1106 | /// true when every work item in workItemResults has completed; otherwise false. |
1107 | /// </summary> | 1107 | /// </returns> |
1108 | /// <param name="workItemResults">Array of work item result objects</param> | 1108 | public static bool WaitAll( |
1109 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1109 | IWaitableResult [] waitableResults, |
1110 | /// <param name="exitContext"> | 1110 | int millisecondsTimeout, |
1111 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1111 | bool exitContext) |
1112 | /// </param> | 1112 | { |
1113 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1113 | return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null); |
1114 | /// <returns> | 1114 | } |
1115 | /// true when every work item in workItemResults has completed; otherwise false. | 1115 | |
1116 | /// </returns> | 1116 | /// <summary> |
1117 | public static bool WaitAll( | 1117 | /// Wait for all work items to complete |
1118 | IWorkItemResult [] workItemResults, | 1118 | /// </summary> |
1119 | TimeSpan timeout, | 1119 | /// <param name="waitableResults">Array of work item result objects</param> |
1120 | bool exitContext, | 1120 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1121 | WaitHandle cancelWaitHandle) | 1121 | /// <param name="exitContext"> |
1122 | { | 1122 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1123 | return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); | 1123 | /// </param> |
1124 | } | 1124 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1125 | 1125 | /// <returns> | |
1126 | /// <summary> | 1126 | /// true when every work item in workItemResults has completed; otherwise false. |
1127 | /// Wait for all work items to complete | 1127 | /// </returns> |
1128 | /// </summary> | 1128 | public static bool WaitAll( |
1129 | /// <param name="workItemResults">Array of work item result objects</param> | 1129 | IWaitableResult[] waitableResults, |
1130 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1130 | int millisecondsTimeout, |
1131 | /// <param name="exitContext"> | 1131 | bool exitContext, |
1132 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1132 | WaitHandle cancelWaitHandle) |
1133 | /// </param> | 1133 | { |
1134 | /// <returns> | 1134 | return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); |
1135 | /// true when every work item in workItemResults has completed; otherwise false. | 1135 | } |
1136 | /// </returns> | 1136 | |
1137 | public static bool WaitAll( | 1137 | |
1138 | IWorkItemResult [] workItemResults, | 1138 | /// <summary> |
1139 | int millisecondsTimeout, | 1139 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1140 | bool exitContext) | 1140 | /// </summary> |
1141 | { | 1141 | /// <param name="waitableResults">Array of work item result objects</param> |
1142 | return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null); | 1142 | /// <returns> |
1143 | } | 1143 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. |
1144 | 1144 | /// </returns> | |
1145 | /// <summary> | 1145 | public static int WaitAny( |
1146 | /// Wait for all work items to complete | 1146 | IWaitableResult [] waitableResults) |
1147 | /// </summary> | 1147 | { |
1148 | /// <param name="workItemResults">Array of work item result objects</param> | 1148 | return WaitAny(waitableResults, Timeout.Infinite, true); |
1149 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1149 | } |
1150 | /// <param name="exitContext"> | 1150 | |
1151 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1151 | /// <summary> |
1152 | /// </param> | 1152 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1153 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1153 | /// </summary> |
1154 | /// <returns> | 1154 | /// <param name="waitableResults">Array of work item result objects</param> |
1155 | /// true when every work item in workItemResults has completed; otherwise false. | 1155 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1156 | /// </returns> | 1156 | /// <param name="exitContext"> |
1157 | public static bool WaitAll( | 1157 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1158 | IWorkItemResult [] workItemResults, | 1158 | /// </param> |
1159 | int millisecondsTimeout, | 1159 | /// <returns> |
1160 | bool exitContext, | 1160 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. |
1161 | WaitHandle cancelWaitHandle) | 1161 | /// </returns> |
1162 | { | 1162 | public static int WaitAny( |
1163 | return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); | 1163 | IWaitableResult[] waitableResults, |
1164 | } | 1164 | TimeSpan timeout, |
1165 | 1165 | bool exitContext) | |
1166 | 1166 | { | |
1167 | /// <summary> | 1167 | return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext); |
1168 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1168 | } |
1169 | /// </summary> | 1169 | |
1170 | /// <param name="workItemResults">Array of work item result objects</param> | 1170 | /// <summary> |
1171 | /// <returns> | 1171 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1172 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. | 1172 | /// </summary> |
1173 | /// </returns> | 1173 | /// <param name="waitableResults">Array of work item result objects</param> |
1174 | public static int WaitAny( | 1174 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> |
1175 | IWorkItemResult [] workItemResults) | 1175 | /// <param name="exitContext"> |
1176 | { | 1176 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1177 | return WaitAny(workItemResults, Timeout.Infinite, true); | 1177 | /// </param> |
1178 | } | 1178 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1179 | 1179 | /// <returns> | |
1180 | /// <summary> | 1180 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. |
1181 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1181 | /// </returns> |
1182 | /// </summary> | 1182 | public static int WaitAny( |
1183 | /// <param name="workItemResults">Array of work item result objects</param> | 1183 | IWaitableResult [] waitableResults, |
1184 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1184 | TimeSpan timeout, |
1185 | /// <param name="exitContext"> | 1185 | bool exitContext, |
1186 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1186 | WaitHandle cancelWaitHandle) |
1187 | /// </param> | 1187 | { |
1188 | /// <returns> | 1188 | return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); |
1189 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. | 1189 | } |
1190 | /// </returns> | 1190 | |
1191 | public static int WaitAny( | 1191 | /// <summary> |
1192 | IWorkItemResult [] workItemResults, | 1192 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1193 | TimeSpan timeout, | 1193 | /// </summary> |
1194 | bool exitContext) | 1194 | /// <param name="waitableResults">Array of work item result objects</param> |
1195 | { | 1195 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1196 | return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext); | 1196 | /// <param name="exitContext"> |
1197 | } | 1197 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1198 | 1198 | /// </param> | |
1199 | /// <summary> | 1199 | /// <returns> |
1200 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1200 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. |
1201 | /// </summary> | 1201 | /// </returns> |
1202 | /// <param name="workItemResults">Array of work item result objects</param> | 1202 | public static int WaitAny( |
1203 | /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> | 1203 | IWaitableResult [] waitableResults, |
1204 | /// <param name="exitContext"> | 1204 | int millisecondsTimeout, |
1205 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1205 | bool exitContext) |
1206 | /// </param> | 1206 | { |
1207 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1207 | return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null); |
1208 | /// <returns> | 1208 | } |
1209 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. | 1209 | |
1210 | /// </returns> | 1210 | /// <summary> |
1211 | public static int WaitAny( | 1211 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
1212 | IWorkItemResult [] workItemResults, | 1212 | /// </summary> |
1213 | TimeSpan timeout, | 1213 | /// <param name="waitableResults">Array of work item result objects</param> |
1214 | bool exitContext, | 1214 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
1215 | WaitHandle cancelWaitHandle) | 1215 | /// <param name="exitContext"> |
1216 | { | 1216 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
1217 | return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); | 1217 | /// </param> |
1218 | } | 1218 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
1219 | 1219 | /// <returns> | |
1220 | /// <summary> | 1220 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. |
1221 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1221 | /// </returns> |
1222 | /// </summary> | 1222 | public static int WaitAny( |
1223 | /// <param name="workItemResults">Array of work item result objects</param> | 1223 | IWaitableResult [] waitableResults, |
1224 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1224 | int millisecondsTimeout, |
1225 | /// <param name="exitContext"> | 1225 | bool exitContext, |
1226 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1226 | WaitHandle cancelWaitHandle) |
1227 | /// </param> | 1227 | { |
1228 | /// <returns> | 1228 | return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); |
1229 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. | 1229 | } |
1230 | /// </returns> | 1230 | |
1231 | public static int WaitAny( | 1231 | /// <summary> |
1232 | IWorkItemResult [] workItemResults, | 1232 | /// Creates a new WorkItemsGroup. |
1233 | int millisecondsTimeout, | 1233 | /// </summary> |
1234 | bool exitContext) | 1234 | /// <param name="concurrency">The number of work items that can be run concurrently</param> |
1235 | { | 1235 | /// <returns>A reference to the WorkItemsGroup</returns> |
1236 | return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null); | 1236 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) |
1237 | } | 1237 | { |
1238 | 1238 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); | |
1239 | /// <summary> | 1239 | return workItemsGroup; |
1240 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 1240 | } |
1241 | /// </summary> | 1241 | |
1242 | /// <param name="workItemResults">Array of work item result objects</param> | 1242 | /// <summary> |
1243 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> | 1243 | /// Creates a new WorkItemsGroup. |
1244 | /// <param name="exitContext"> | 1244 | /// </summary> |
1245 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. | 1245 | /// <param name="concurrency">The number of work items that can be run concurrently</param> |
1246 | /// </param> | 1246 | /// <param name="wigStartInfo">A WorkItemsGroup configuration that overrides the default behavior</param> |
1247 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 1247 | /// <returns>A reference to the WorkItemsGroup</returns> |
1248 | /// <returns> | 1248 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) |
1249 | /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. | 1249 | { |
1250 | /// </returns> | 1250 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); |
1251 | public static int WaitAny( | 1251 | return workItemsGroup; |
1252 | IWorkItemResult [] workItemResults, | 1252 | } |
1253 | int millisecondsTimeout, | 1253 | |
1254 | bool exitContext, | 1254 | #region Fire Thread's Events |
1255 | WaitHandle cancelWaitHandle) | 1255 | |
1256 | { | 1256 | private void FireOnThreadInitialization() |
1257 | return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); | 1257 | { |
1258 | } | 1258 | if (null != _onThreadInitialization) |
1259 | 1259 | { | |
1260 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) | 1260 | foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList()) |
1261 | { | 1261 | { |
1262 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); | 1262 | try |
1263 | return workItemsGroup; | 1263 | { |
1264 | } | 1264 | tih(); |
1265 | 1265 | } | |
1266 | public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) | 1266 | catch (Exception e) |
1267 | { | 1267 | { |
1268 | IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); | 1268 | e.GetHashCode(); |
1269 | return workItemsGroup; | 1269 | Debug.Assert(false); |
1270 | } | 1270 | throw; |
1271 | 1271 | } | |
1272 | public event WorkItemsGroupIdleHandler OnIdle | 1272 | } |
1273 | { | 1273 | } |
1274 | add | 1274 | } |
1275 | { | 1275 | |
1276 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | 1276 | private void FireOnThreadTermination() |
1277 | //_onIdle += value; | 1277 | { |
1278 | } | 1278 | if (null != _onThreadTermination) |
1279 | remove | 1279 | { |
1280 | { | 1280 | foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList()) |
1281 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | 1281 | { |
1282 | //_onIdle -= value; | 1282 | try |
1283 | } | 1283 | { |
1284 | } | 1284 | tth(); |
1285 | 1285 | } | |
1286 | public void Cancel() | 1286 | catch (Exception e) |
1287 | { | 1287 | { |
1288 | ICollection workItemsGroups = _workItemsGroups.Values; | 1288 | e.GetHashCode(); |
1289 | foreach(WorkItemsGroup workItemsGroup in workItemsGroups) | 1289 | Debug.Assert(false); |
1290 | { | 1290 | throw; |
1291 | workItemsGroup.Cancel(); | 1291 | } |
1292 | } | 1292 | } |
1293 | } | 1293 | } |
1294 | 1294 | } | |
1295 | public void Start() | 1295 | |
1296 | { | 1296 | #endregion |
1297 | lock (this) | 1297 | |
1298 | { | 1298 | /// <summary> |
1299 | if (!this._stpStartInfo.StartSuspended) | 1299 | /// This event is fired when a thread is created. |
1300 | { | 1300 | /// Use it to initialize a thread before the work items use it. |
1301 | return; | 1301 | /// </summary> |
1302 | } | 1302 | public event ThreadInitializationHandler OnThreadInitialization |
1303 | _stpStartInfo.StartSuspended = false; | 1303 | { |
1304 | } | 1304 | add { _onThreadInitialization += value; } |
1305 | 1305 | remove { _onThreadInitialization -= value; } | |
1306 | ICollection workItemsGroups = _workItemsGroups.Values; | 1306 | } |
1307 | foreach(WorkItemsGroup workItemsGroup in workItemsGroups) | 1307 | |
1308 | { | 1308 | /// <summary> |
1309 | workItemsGroup.OnSTPIsStarting(); | 1309 | /// This event is fired when a thread is terminating. |
1310 | } | 1310 | /// Use it for cleanup. |
1311 | 1311 | /// </summary> | |
1312 | StartOptimalNumberOfThreads(); | 1312 | public event ThreadTerminationHandler OnThreadTermination |
1313 | } | 1313 | { |
1314 | 1314 | add { _onThreadTermination += value; } | |
1315 | #endregion | 1315 | remove { _onThreadTermination -= value; } |
1316 | 1316 | } | |
1317 | #region Properties | 1317 | |
1318 | 1318 | ||
1319 | /// <summary> | 1319 | internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig) |
1320 | /// Get/Set the name of the SmartThreadPool instance | 1320 | { |
1321 | /// </summary> | 1321 | foreach (ThreadEntry threadEntry in _workerThreads.Values) |
1322 | public string Name | 1322 | { |
1323 | { | 1323 | WorkItem workItem = threadEntry.CurrentWorkItem; |
1324 | get | 1324 | if (null != workItem && |
1325 | { | 1325 | workItem.WasQueuedBy(wig) && |
1326 | return _name; | 1326 | !workItem.IsCanceled) |
1327 | } | 1327 | { |
1328 | 1328 | threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); | |
1329 | set | 1329 | } |
1330 | { | 1330 | } |
1331 | _name = value; | 1331 | } |
1332 | } | 1332 | |
1333 | } | 1333 | |
1334 | 1334 | ||
1335 | /// <summary> | 1335 | #endregion |
1336 | /// Get the lower limit of threads in the pool. | 1336 | |
1337 | /// </summary> | 1337 | #region Properties |
1338 | public int MinThreads | 1338 | |
1339 | { | 1339 | /// <summary> |
1340 | get | 1340 | /// Get/Set the lower limit of threads in the pool. |
1341 | { | 1341 | /// </summary> |
1342 | ValidateNotDisposed(); | 1342 | public int MinThreads |
1343 | return _stpStartInfo.MinWorkerThreads; | 1343 | { |
1344 | } | 1344 | get |
1345 | } | 1345 | { |
1346 | 1346 | ValidateNotDisposed(); | |
1347 | /// <summary> | 1347 | return _stpStartInfo.MinWorkerThreads; |
1348 | /// Get the upper limit of threads in the pool. | 1348 | } |
1349 | /// </summary> | 1349 | set |
1350 | public int MaxThreads | 1350 | { |
1351 | { | 1351 | Debug.Assert(value >= 0); |
1352 | get | 1352 | Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads); |
1353 | { | 1353 | if (_stpStartInfo.MaxWorkerThreads < value) |
1354 | ValidateNotDisposed(); | 1354 | { |
1355 | return _stpStartInfo.MaxWorkerThreads; | 1355 | _stpStartInfo.MaxWorkerThreads = value; |
1356 | } | 1356 | } |
1357 | } | 1357 | _stpStartInfo.MinWorkerThreads = value; |
1358 | /// <summary> | 1358 | StartOptimalNumberOfThreads(); |
1359 | /// Get the number of threads in the thread pool. | 1359 | } |
1360 | /// Should be between the lower and the upper limits. | 1360 | } |
1361 | /// </summary> | 1361 | |
1362 | public int ActiveThreads | 1362 | /// <summary> |
1363 | { | 1363 | /// Get/Set the upper limit of threads in the pool. |
1364 | get | 1364 | /// </summary> |
1365 | { | 1365 | public int MaxThreads |
1366 | ValidateNotDisposed(); | 1366 | { |
1367 | return _workerThreads.Count; | 1367 | get |
1368 | } | 1368 | { |
1369 | } | 1369 | ValidateNotDisposed(); |
1370 | 1370 | return _stpStartInfo.MaxWorkerThreads; | |
1371 | /// <summary> | 1371 | } |
1372 | /// Get the number of busy (not idle) threads in the thread pool. | 1372 | |
1373 | /// </summary> | 1373 | set |
1374 | public int InUseThreads | 1374 | { |
1375 | { | 1375 | Debug.Assert(value > 0); |
1376 | get | 1376 | Debug.Assert(value >= _stpStartInfo.MinWorkerThreads); |
1377 | { | 1377 | if (_stpStartInfo.MinWorkerThreads > value) |
1378 | ValidateNotDisposed(); | 1378 | { |
1379 | return _inUseWorkerThreads; | 1379 | _stpStartInfo.MinWorkerThreads = value; |
1380 | } | 1380 | } |
1381 | } | 1381 | _stpStartInfo.MaxWorkerThreads = value; |
1382 | 1382 | StartOptimalNumberOfThreads(); | |
1383 | /// <summary> | 1383 | } |
1384 | /// Get the number of work items in the queue. | 1384 | } |
1385 | /// </summary> | 1385 | /// <summary> |
1386 | public int WaitingCallbacks | 1386 | /// Get the number of threads in the thread pool. |
1387 | { | 1387 | /// Should be between the lower and the upper limits. |
1388 | get | 1388 | /// </summary> |
1389 | { | 1389 | public int ActiveThreads |
1390 | ValidateNotDisposed(); | 1390 | { |
1391 | return _workItemsQueue.Count; | 1391 | get |
1392 | } | 1392 | { |
1393 | } | 1393 | ValidateNotDisposed(); |
1394 | 1394 | return _workerThreads.Count; | |
1395 | 1395 | } | |
1396 | public event EventHandler Idle | 1396 | } |
1397 | { | 1397 | |
1398 | add | 1398 | /// <summary> |
1399 | { | 1399 | /// Get the number of busy (not idle) threads in the thread pool. |
1400 | _stpIdle += value; | 1400 | /// </summary> |
1401 | } | 1401 | public int InUseThreads |
1402 | 1402 | { | |
1403 | remove | 1403 | get |
1404 | { | 1404 | { |
1405 | _stpIdle -= value; | 1405 | ValidateNotDisposed(); |
1406 | } | 1406 | return _inUseWorkerThreads; |
1407 | } | 1407 | } |
1408 | 1408 | } | |
1409 | #endregion | 1409 | |
1410 | 1410 | /// <summary> | |
1411 | #region IDisposable Members | 1411 | /// Returns true if the current running work item has been cancelled. |
1412 | 1412 | /// Must be used within the work item's callback method. | |
1413 | // ~SmartThreadPool() | 1413 | /// The work item should sample this value in order to know if it |
1414 | // { | 1414 | /// needs to quit before its completion. |
1415 | // Dispose(); | 1415 | /// </summary> |
1416 | // } | 1416 | public static bool IsWorkItemCanceled |
1417 | 1417 | { | |
1418 | public void Dispose() | 1418 | get |
1419 | { | 1419 | { |
1420 | if (!_isDisposed) | 1420 | return CurrentThreadEntry.CurrentWorkItem.IsCanceled; |
1421 | { | 1421 | } |
1422 | if (!_shutdown) | 1422 | } |
1423 | { | 1423 | |
1424 | Shutdown(); | 1424 | /// <summary> |
1425 | } | 1425 | /// Checks if the work item has been cancelled, and if yes then abort the thread. |
1426 | 1426 | /// Can be used with Cancel and timeout | |
1427 | if (null != _shuttingDownEvent) | 1427 | /// </summary> |
1428 | { | 1428 | public static void AbortOnWorkItemCancel() |
1429 | _shuttingDownEvent.Close(); | 1429 | { |
1430 | _shuttingDownEvent = null; | 1430 | if (IsWorkItemCanceled) |
1431 | } | 1431 | { |
1432 | _workerThreads.Clear(); | 1432 | Thread.CurrentThread.Abort(); |
1433 | _isDisposed = true; | 1433 | } |
1434 | GC.SuppressFinalize(this); | 1434 | } |
1435 | } | 1435 | |
1436 | } | 1436 | /// <summary> |
1437 | 1437 | /// Thread Pool start information (readonly) | |
1438 | private void ValidateNotDisposed() | 1438 | /// </summary> |
1439 | { | 1439 | public STPStartInfo STPStartInfo |
1440 | if(_isDisposed) | 1440 | { |
1441 | { | 1441 | get |
1442 | throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); | 1442 | { |
1443 | } | 1443 | return _stpStartInfo.AsReadOnly(); |
1444 | } | 1444 | } |
1445 | #endregion | 1445 | } |
1446 | } | 1446 | |
1447 | #endregion | 1447 | public bool IsShuttingdown |
1448 | } | 1448 | { |
1449 | get { return _shutdown; } | ||
1450 | } | ||
1451 | |||
1452 | /// <summary> | ||
1453 | /// Return the local calculated performance counters | ||
1454 | /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true. | ||
1455 | /// </summary> | ||
1456 | public ISTPPerformanceCountersReader PerformanceCountersReader | ||
1457 | { | ||
1458 | get { return (ISTPPerformanceCountersReader)_localPCs; } | ||
1459 | } | ||
1460 | |||
1461 | #endregion | ||
1462 | |||
1463 | #region IDisposable Members | ||
1464 | |||
1465 | public void Dispose() | ||
1466 | { | ||
1467 | if (!_isDisposed) | ||
1468 | { | ||
1469 | if (!_shutdown) | ||
1470 | { | ||
1471 | Shutdown(); | ||
1472 | } | ||
1473 | |||
1474 | if (null != _shuttingDownEvent) | ||
1475 | { | ||
1476 | _shuttingDownEvent.Close(); | ||
1477 | _shuttingDownEvent = null; | ||
1478 | } | ||
1479 | _workerThreads.Clear(); | ||
1480 | |||
1481 | if (null != _isIdleWaitHandle) | ||
1482 | { | ||
1483 | _isIdleWaitHandle.Close(); | ||
1484 | _isIdleWaitHandle = null; | ||
1485 | } | ||
1486 | |||
1487 | _isDisposed = true; | ||
1488 | } | ||
1489 | } | ||
1490 | |||
1491 | private void ValidateNotDisposed() | ||
1492 | { | ||
1493 | if(_isDisposed) | ||
1494 | { | ||
1495 | throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); | ||
1496 | } | ||
1497 | } | ||
1498 | #endregion | ||
1499 | |||
1500 | #region WorkItemsGroupBase Overrides | ||
1501 | |||
1502 | /// <summary> | ||
1503 | /// Get/Set the maximum number of work items that execute cocurrency on the thread pool | ||
1504 | /// </summary> | ||
1505 | public override int Concurrency | ||
1506 | { | ||
1507 | get { return MaxThreads; } | ||
1508 | set { MaxThreads = value; } | ||
1509 | } | ||
1510 | |||
1511 | /// <summary> | ||
1512 | /// Get the number of work items in the queue. | ||
1513 | /// </summary> | ||
1514 | public override int WaitingCallbacks | ||
1515 | { | ||
1516 | get | ||
1517 | { | ||
1518 | ValidateNotDisposed(); | ||
1519 | return _workItemsQueue.Count; | ||
1520 | } | ||
1521 | } | ||
1522 | |||
1523 | /// <summary> | ||
1524 | /// Get an array with all the state objects of the currently running items. | ||
1525 | /// The array represents a snap shot and impact performance. | ||
1526 | /// </summary> | ||
1527 | public override object[] GetStates() | ||
1528 | { | ||
1529 | object[] states = _workItemsQueue.GetStates(); | ||
1530 | return states; | ||
1531 | } | ||
1532 | |||
1533 | /// <summary> | ||
1534 | /// WorkItemsGroup start information (readonly) | ||
1535 | /// </summary> | ||
1536 | public override WIGStartInfo WIGStartInfo | ||
1537 | { | ||
1538 | get { return _stpStartInfo.AsReadOnly(); } | ||
1539 | } | ||
1540 | |||
1541 | /// <summary> | ||
1542 | /// Start the thread pool if it was started suspended. | ||
1543 | /// If it is already running, this method is ignored. | ||
1544 | /// </summary> | ||
1545 | public override void Start() | ||
1546 | { | ||
1547 | if (!_isSuspended) | ||
1548 | { | ||
1549 | return; | ||
1550 | } | ||
1551 | _isSuspended = false; | ||
1552 | |||
1553 | ICollection workItemsGroups = _workItemsGroups.Values; | ||
1554 | foreach (WorkItemsGroup workItemsGroup in workItemsGroups) | ||
1555 | { | ||
1556 | workItemsGroup.OnSTPIsStarting(); | ||
1557 | } | ||
1558 | |||
1559 | StartOptimalNumberOfThreads(); | ||
1560 | } | ||
1561 | |||
1562 | /// <summary> | ||
1563 | /// Cancel all work items using thread abortion | ||
1564 | /// </summary> | ||
1565 | /// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param> | ||
1566 | public override void Cancel(bool abortExecution) | ||
1567 | { | ||
1568 | _canceledSmartThreadPool.IsCanceled = true; | ||
1569 | _canceledSmartThreadPool = new CanceledWorkItemsGroup(); | ||
1570 | |||
1571 | ICollection workItemsGroups = _workItemsGroups.Values; | ||
1572 | foreach (WorkItemsGroup workItemsGroup in workItemsGroups) | ||
1573 | { | ||
1574 | workItemsGroup.Cancel(abortExecution); | ||
1575 | } | ||
1576 | |||
1577 | if (abortExecution) | ||
1578 | { | ||
1579 | foreach (ThreadEntry threadEntry in _workerThreads.Values) | ||
1580 | { | ||
1581 | WorkItem workItem = threadEntry.CurrentWorkItem; | ||
1582 | if (null != workItem && | ||
1583 | threadEntry.AssociatedSmartThreadPool == this && | ||
1584 | !workItem.IsCanceled) | ||
1585 | { | ||
1586 | threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); | ||
1587 | } | ||
1588 | } | ||
1589 | } | ||
1590 | } | ||
1591 | |||
1592 | /// <summary> | ||
1593 | /// Wait for the thread pool to be idle | ||
1594 | /// </summary> | ||
1595 | public override bool WaitForIdle(int millisecondsTimeout) | ||
1596 | { | ||
1597 | ValidateWaitForIdle(); | ||
1598 | return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); | ||
1599 | } | ||
1600 | |||
1601 | /// <summary> | ||
1602 | /// This event is fired when all work items are completed. | ||
1603 | /// (When IsIdle changes to true) | ||
1604 | /// This event only work on WorkItemsGroup. On SmartThreadPool | ||
1605 | /// it throws the NotImplementedException. | ||
1606 | /// </summary> | ||
1607 | public override event WorkItemsGroupIdleHandler OnIdle | ||
1608 | { | ||
1609 | add | ||
1610 | { | ||
1611 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | ||
1612 | //_onIdle += value; | ||
1613 | } | ||
1614 | remove | ||
1615 | { | ||
1616 | throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); | ||
1617 | //_onIdle -= value; | ||
1618 | } | ||
1619 | } | ||
1620 | |||
1621 | internal override void PreQueueWorkItem() | ||
1622 | { | ||
1623 | ValidateNotDisposed(); | ||
1624 | } | ||
1625 | |||
1626 | #endregion | ||
1627 | |||
1628 | #region Join, Choice, Pipe, etc. | ||
1629 | |||
1630 | /// <summary> | ||
1631 | /// Executes all actions in parallel. | ||
1632 | /// Returns when they all finish. | ||
1633 | /// </summary> | ||
1634 | /// <param name="actions">Actions to execute</param> | ||
1635 | public void Join(IEnumerable<Action> actions) | ||
1636 | { | ||
1637 | WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; | ||
1638 | IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); | ||
1639 | foreach (Action action in actions) | ||
1640 | { | ||
1641 | workItemsGroup.QueueWorkItem(action); | ||
1642 | } | ||
1643 | workItemsGroup.Start(); | ||
1644 | workItemsGroup.WaitForIdle(); | ||
1645 | } | ||
1646 | |||
1647 | /// <summary> | ||
1648 | /// Executes all actions in parallel. | ||
1649 | /// Returns when they all finish. | ||
1650 | /// </summary> | ||
1651 | /// <param name="actions">Actions to execute</param> | ||
1652 | public void Join(params Action[] actions) | ||
1653 | { | ||
1654 | Join((IEnumerable<Action>)actions); | ||
1655 | } | ||
1656 | |||
1657 | private class ChoiceIndex | ||
1658 | { | ||
1659 | public int _index = -1; | ||
1660 | } | ||
1661 | |||
1662 | /// <summary> | ||
1663 | /// Executes all actions in parallel | ||
1664 | /// Returns when the first one completes | ||
1665 | /// </summary> | ||
1666 | /// <param name="actions">Actions to execute</param> | ||
1667 | public int Choice(IEnumerable<Action> actions) | ||
1668 | { | ||
1669 | WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; | ||
1670 | IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); | ||
1671 | |||
1672 | ManualResetEvent anActionCompleted = new ManualResetEvent(false); | ||
1673 | |||
1674 | ChoiceIndex choiceIndex = new ChoiceIndex(); | ||
1675 | |||
1676 | int i = 0; | ||
1677 | foreach (Action action in actions) | ||
1678 | { | ||
1679 | Action act = action; | ||
1680 | int value = i; | ||
1681 | workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); }); | ||
1682 | ++i; | ||
1683 | } | ||
1684 | workItemsGroup.Start(); | ||
1685 | anActionCompleted.WaitOne(); | ||
1686 | |||
1687 | return choiceIndex._index; | ||
1688 | } | ||
1689 | |||
1690 | /// <summary> | ||
1691 | /// Executes all actions in parallel | ||
1692 | /// Returns when the first one completes | ||
1693 | /// </summary> | ||
1694 | /// <param name="actions">Actions to execute</param> | ||
1695 | public int Choice(params Action[] actions) | ||
1696 | { | ||
1697 | return Choice((IEnumerable<Action>)actions); | ||
1698 | } | ||
1699 | |||
1700 | /// <summary> | ||
1701 | /// Executes actions in sequence asynchronously. | ||
1702 | /// Returns immediately. | ||
1703 | /// </summary> | ||
1704 | /// <param name="pipeState">A state context that passes </param> | ||
1705 | /// <param name="actions">Actions to execute in the order they should run</param> | ||
1706 | public void Pipe<T>(T pipeState, IEnumerable<Action<T>> actions) | ||
1707 | { | ||
1708 | WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; | ||
1709 | IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo); | ||
1710 | foreach (Action<T> action in actions) | ||
1711 | { | ||
1712 | Action<T> act = action; | ||
1713 | workItemsGroup.QueueWorkItem(() => act(pipeState)); | ||
1714 | } | ||
1715 | workItemsGroup.Start(); | ||
1716 | workItemsGroup.WaitForIdle(); | ||
1717 | } | ||
1718 | |||
1719 | /// <summary> | ||
1720 | /// Executes actions in sequence asynchronously. | ||
1721 | /// Returns immediately. | ||
1722 | /// </summary> | ||
1723 | /// <param name="pipeState"></param> | ||
1724 | /// <param name="actions">Actions to execute in the order they should run</param> | ||
1725 | public void Pipe<T>(T pipeState, params Action<T>[] actions) | ||
1726 | { | ||
1727 | Pipe(pipeState, (IEnumerable<Action<T>>)actions); | ||
1728 | } | ||
1729 | #endregion | ||
1730 | } | ||
1731 | #endregion | ||
1732 | } | ||
diff --git a/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs new file mode 100644 index 0000000..3532cca --- /dev/null +++ b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs | |||
@@ -0,0 +1,89 @@ | |||
1 | using System.Collections.Generic; | ||
2 | |||
3 | namespace Amib.Threading.Internal | ||
4 | { | ||
5 | internal class SynchronizedDictionary<TKey, TValue> | ||
6 | { | ||
7 | private readonly Dictionary<TKey, TValue> _dictionary; | ||
8 | private readonly object _lock; | ||
9 | |||
10 | public SynchronizedDictionary() | ||
11 | { | ||
12 | _lock = new object(); | ||
13 | _dictionary = new Dictionary<TKey, TValue>(); | ||
14 | } | ||
15 | |||
16 | public int Count | ||
17 | { | ||
18 | get { return _dictionary.Count; } | ||
19 | } | ||
20 | |||
21 | public bool Contains(TKey key) | ||
22 | { | ||
23 | lock (_lock) | ||
24 | { | ||
25 | return _dictionary.ContainsKey(key); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | public void Remove(TKey key) | ||
30 | { | ||
31 | lock (_lock) | ||
32 | { | ||
33 | _dictionary.Remove(key); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | public object SyncRoot | ||
38 | { | ||
39 | get { return _lock; } | ||
40 | } | ||
41 | |||
42 | public TValue this[TKey key] | ||
43 | { | ||
44 | get | ||
45 | { | ||
46 | lock (_lock) | ||
47 | { | ||
48 | return _dictionary[key]; | ||
49 | } | ||
50 | } | ||
51 | set | ||
52 | { | ||
53 | lock (_lock) | ||
54 | { | ||
55 | _dictionary[key] = value; | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | public Dictionary<TKey, TValue>.KeyCollection Keys | ||
61 | { | ||
62 | get | ||
63 | { | ||
64 | lock (_lock) | ||
65 | { | ||
66 | return _dictionary.Keys; | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | public Dictionary<TKey, TValue>.ValueCollection Values | ||
72 | { | ||
73 | get | ||
74 | { | ||
75 | lock (_lock) | ||
76 | { | ||
77 | return _dictionary.Values; | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | public void Clear() | ||
82 | { | ||
83 | lock (_lock) | ||
84 | { | ||
85 | _dictionary.Clear(); | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | } | ||
diff --git a/ThirdParty/SmartThreadPool/WIGStartInfo.cs b/ThirdParty/SmartThreadPool/WIGStartInfo.cs index 150317f..e5ff150 100644 --- a/ThirdParty/SmartThreadPool/WIGStartInfo.cs +++ b/ThirdParty/SmartThreadPool/WIGStartInfo.cs | |||
@@ -1,99 +1,171 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | |
3 | 3 | namespace Amib.Threading | |
4 | namespace Amib.Threading | 4 | { |
5 | { | 5 | /// <summary> |
6 | /// <summary> | 6 | /// Summary description for WIGStartInfo. |
7 | /// Summary description for WIGStartInfo. | 7 | /// </summary> |
8 | /// </summary> | 8 | public class WIGStartInfo |
9 | public class WIGStartInfo | 9 | { |
10 | { | 10 | private bool _useCallerCallContext; |
11 | /// <summary> | 11 | private bool _useCallerHttpContext; |
12 | /// Use the caller's security context | 12 | private bool _disposeOfStateObjects; |
13 | /// </summary> | 13 | private CallToPostExecute _callToPostExecute; |
14 | private bool _useCallerCallContext; | 14 | private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; |
15 | 15 | private bool _startSuspended; | |
16 | /// <summary> | 16 | private WorkItemPriority _workItemPriority; |
17 | /// Use the caller's HTTP context | 17 | private bool _fillStateWithArgs; |
18 | /// </summary> | 18 | |
19 | private bool _useCallerHttpContext; | 19 | protected bool _readOnly; |
20 | 20 | ||
21 | /// <summary> | 21 | public WIGStartInfo() |
22 | /// Dispose of the state object of a work item | 22 | { |
23 | /// </summary> | 23 | _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs; |
24 | private bool _disposeOfStateObjects; | 24 | _workItemPriority = SmartThreadPool.DefaultWorkItemPriority; |
25 | 25 | _startSuspended = SmartThreadPool.DefaultStartSuspended; | |
26 | /// <summary> | 26 | _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; |
27 | /// The option to run the post execute | 27 | _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; |
28 | /// </summary> | 28 | _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; |
29 | private CallToPostExecute _callToPostExecute; | 29 | _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; |
30 | 30 | _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; | |
31 | /// <summary> | 31 | } |
32 | /// A post execute callback to call when none is provided in | 32 | |
33 | /// the QueueWorkItem method. | 33 | public WIGStartInfo(WIGStartInfo wigStartInfo) |
34 | /// </summary> | 34 | { |
35 | private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; | 35 | _useCallerCallContext = wigStartInfo.UseCallerCallContext; |
36 | 36 | _useCallerHttpContext = wigStartInfo.UseCallerHttpContext; | |
37 | /// <summary> | 37 | _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; |
38 | /// Indicate the WorkItemsGroup to suspend the handling of the work items | 38 | _callToPostExecute = wigStartInfo.CallToPostExecute; |
39 | /// until the Start() method is called. | 39 | _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; |
40 | /// </summary> | 40 | _workItemPriority = wigStartInfo.WorkItemPriority; |
41 | private bool _startSuspended; | 41 | _startSuspended = wigStartInfo.StartSuspended; |
42 | 42 | _fillStateWithArgs = wigStartInfo.FillStateWithArgs; | |
43 | public WIGStartInfo() | 43 | } |
44 | { | 44 | |
45 | _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; | 45 | protected void ThrowIfReadOnly() |
46 | _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; | 46 | { |
47 | _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; | 47 | if (_readOnly) |
48 | _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; | 48 | { |
49 | _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; | 49 | throw new NotSupportedException("This is a readonly instance and set is not supported"); |
50 | _startSuspended = SmartThreadPool.DefaultStartSuspended; | 50 | } |
51 | } | 51 | } |
52 | 52 | ||
53 | public WIGStartInfo(WIGStartInfo wigStartInfo) | 53 | /// <summary> |
54 | { | 54 | /// Get/Set if to use the caller's security context |
55 | _useCallerCallContext = wigStartInfo._useCallerCallContext; | 55 | /// </summary> |
56 | _useCallerHttpContext = wigStartInfo._useCallerHttpContext; | 56 | public virtual bool UseCallerCallContext |
57 | _disposeOfStateObjects = wigStartInfo._disposeOfStateObjects; | 57 | { |
58 | _callToPostExecute = wigStartInfo._callToPostExecute; | 58 | get { return _useCallerCallContext; } |
59 | _postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback; | 59 | set |
60 | _startSuspended = wigStartInfo._startSuspended; | 60 | { |
61 | } | 61 | ThrowIfReadOnly(); |
62 | 62 | _useCallerCallContext = value; | |
63 | public bool UseCallerCallContext | 63 | } |
64 | { | 64 | } |
65 | get { return _useCallerCallContext; } | 65 | |
66 | set { _useCallerCallContext = value; } | 66 | |
67 | } | 67 | /// <summary> |
68 | 68 | /// Get/Set if to use the caller's HTTP context | |
69 | public bool UseCallerHttpContext | 69 | /// </summary> |
70 | { | 70 | public virtual bool UseCallerHttpContext |
71 | get { return _useCallerHttpContext; } | 71 | { |
72 | set { _useCallerHttpContext = value; } | 72 | get { return _useCallerHttpContext; } |
73 | } | 73 | set |
74 | 74 | { | |
75 | public bool DisposeOfStateObjects | 75 | ThrowIfReadOnly(); |
76 | { | 76 | _useCallerHttpContext = value; |
77 | get { return _disposeOfStateObjects; } | 77 | } |
78 | set { _disposeOfStateObjects = value; } | 78 | } |
79 | } | 79 | |
80 | 80 | ||
81 | public CallToPostExecute CallToPostExecute | 81 | /// <summary> |
82 | { | 82 | /// Get/Set if to dispose of the state object of a work item |
83 | get { return _callToPostExecute; } | 83 | /// </summary> |
84 | set { _callToPostExecute = value; } | 84 | public virtual bool DisposeOfStateObjects |
85 | } | 85 | { |
86 | 86 | get { return _disposeOfStateObjects; } | |
87 | public PostExecuteWorkItemCallback PostExecuteWorkItemCallback | 87 | set |
88 | { | 88 | { |
89 | get { return _postExecuteWorkItemCallback; } | 89 | ThrowIfReadOnly(); |
90 | set { _postExecuteWorkItemCallback = value; } | 90 | _disposeOfStateObjects = value; |
91 | } | 91 | } |
92 | 92 | } | |
93 | public bool StartSuspended | 93 | |
94 | { | 94 | |
95 | get { return _startSuspended; } | 95 | /// <summary> |
96 | set { _startSuspended = value; } | 96 | /// Get/Set the run the post execute options |
97 | } | 97 | /// </summary> |
98 | } | 98 | public virtual CallToPostExecute CallToPostExecute |
99 | } | 99 | { |
100 | get { return _callToPostExecute; } | ||
101 | set | ||
102 | { | ||
103 | ThrowIfReadOnly(); | ||
104 | _callToPostExecute = value; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | |||
109 | /// <summary> | ||
110 | /// Get/Set the default post execute callback | ||
111 | /// </summary> | ||
112 | public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback | ||
113 | { | ||
114 | get { return _postExecuteWorkItemCallback; } | ||
115 | set | ||
116 | { | ||
117 | ThrowIfReadOnly(); | ||
118 | _postExecuteWorkItemCallback = value; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | |||
123 | /// <summary> | ||
124 | /// Get/Set if the work items execution should be suspended until the Start() | ||
125 | /// method is called. | ||
126 | /// </summary> | ||
127 | public virtual bool StartSuspended | ||
128 | { | ||
129 | get { return _startSuspended; } | ||
130 | set | ||
131 | { | ||
132 | ThrowIfReadOnly(); | ||
133 | _startSuspended = value; | ||
134 | } | ||
135 | } | ||
136 | |||
137 | |||
138 | /// <summary> | ||
139 | /// Get/Set the default priority that a work item gets when it is enqueued | ||
140 | /// </summary> | ||
141 | public virtual WorkItemPriority WorkItemPriority | ||
142 | { | ||
143 | get { return _workItemPriority; } | ||
144 | set { _workItemPriority = value; } | ||
145 | } | ||
146 | |||
147 | /// <summary> | ||
148 | /// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the | ||
149 | /// arguments as an object array into the state of the work item. | ||
150 | /// The arguments can be access later by IWorkItemResult.State. | ||
151 | /// </summary> | ||
152 | public virtual bool FillStateWithArgs | ||
153 | { | ||
154 | get { return _fillStateWithArgs; } | ||
155 | set | ||
156 | { | ||
157 | ThrowIfReadOnly(); | ||
158 | _fillStateWithArgs = value; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | /// <summary> | ||
163 | /// Get a readonly version of this WIGStartInfo | ||
164 | /// </summary> | ||
165 | /// <returns>Returns a readonly reference to this WIGStartInfoRO</returns> | ||
166 | public WIGStartInfo AsReadOnly() | ||
167 | { | ||
168 | return new WIGStartInfo(this) { _readOnly = true }; | ||
169 | } | ||
170 | } | ||
171 | } | ||
diff --git a/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs new file mode 100644 index 0000000..5745c15 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs | |||
@@ -0,0 +1,190 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.Text; | ||
4 | using System.Threading; | ||
5 | |||
6 | namespace Amib.Threading.Internal | ||
7 | { | ||
8 | public partial class WorkItem | ||
9 | { | ||
10 | #region WorkItemResult class | ||
11 | |||
12 | private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult | ||
13 | { | ||
14 | /// <summary> | ||
15 | /// A back reference to the work item | ||
16 | /// </summary> | ||
17 | private readonly WorkItem _workItem; | ||
18 | |||
19 | public WorkItemResult(WorkItem workItem) | ||
20 | { | ||
21 | _workItem = workItem; | ||
22 | } | ||
23 | |||
24 | internal WorkItem GetWorkItem() | ||
25 | { | ||
26 | return _workItem; | ||
27 | } | ||
28 | |||
29 | #region IWorkItemResult Members | ||
30 | |||
31 | public bool IsCompleted | ||
32 | { | ||
33 | get | ||
34 | { | ||
35 | return _workItem.IsCompleted; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | public bool IsCanceled | ||
40 | { | ||
41 | get | ||
42 | { | ||
43 | return _workItem.IsCanceled; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | public object GetResult() | ||
48 | { | ||
49 | return _workItem.GetResult(Timeout.Infinite, true, null); | ||
50 | } | ||
51 | |||
52 | public object GetResult(int millisecondsTimeout, bool exitContext) | ||
53 | { | ||
54 | return _workItem.GetResult(millisecondsTimeout, exitContext, null); | ||
55 | } | ||
56 | |||
57 | public object GetResult(TimeSpan timeout, bool exitContext) | ||
58 | { | ||
59 | return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); | ||
60 | } | ||
61 | |||
62 | public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) | ||
63 | { | ||
64 | return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); | ||
65 | } | ||
66 | |||
67 | public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) | ||
68 | { | ||
69 | return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); | ||
70 | } | ||
71 | |||
72 | public object GetResult(out Exception e) | ||
73 | { | ||
74 | return _workItem.GetResult(Timeout.Infinite, true, null, out e); | ||
75 | } | ||
76 | |||
77 | public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) | ||
78 | { | ||
79 | return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); | ||
80 | } | ||
81 | |||
82 | public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) | ||
83 | { | ||
84 | return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); | ||
85 | } | ||
86 | |||
87 | public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) | ||
88 | { | ||
89 | return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); | ||
90 | } | ||
91 | |||
92 | public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) | ||
93 | { | ||
94 | return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); | ||
95 | } | ||
96 | |||
97 | public bool Cancel() | ||
98 | { | ||
99 | return Cancel(false); | ||
100 | } | ||
101 | |||
102 | public bool Cancel(bool abortExecution) | ||
103 | { | ||
104 | return _workItem.Cancel(abortExecution); | ||
105 | } | ||
106 | |||
107 | public object State | ||
108 | { | ||
109 | get | ||
110 | { | ||
111 | return _workItem._state; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | public WorkItemPriority WorkItemPriority | ||
116 | { | ||
117 | get | ||
118 | { | ||
119 | return _workItem._workItemInfo.WorkItemPriority; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /// <summary> | ||
124 | /// Return the result, same as GetResult() | ||
125 | /// </summary> | ||
126 | public object Result | ||
127 | { | ||
128 | get { return GetResult(); } | ||
129 | } | ||
130 | |||
131 | /// <summary> | ||
132 | /// Returns the exception if occured otherwise returns null. | ||
133 | /// This value is valid only after the work item completed, | ||
134 | /// before that it is always null. | ||
135 | /// </summary> | ||
136 | public object Exception | ||
137 | { | ||
138 | get { return _workItem._exception; } | ||
139 | } | ||
140 | |||
141 | #endregion | ||
142 | |||
143 | #region IInternalWorkItemResult Members | ||
144 | |||
145 | public event WorkItemStateCallback OnWorkItemStarted | ||
146 | { | ||
147 | add | ||
148 | { | ||
149 | _workItem.OnWorkItemStarted += value; | ||
150 | } | ||
151 | remove | ||
152 | { | ||
153 | _workItem.OnWorkItemStarted -= value; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | |||
158 | public event WorkItemStateCallback OnWorkItemCompleted | ||
159 | { | ||
160 | add | ||
161 | { | ||
162 | _workItem.OnWorkItemCompleted += value; | ||
163 | } | ||
164 | remove | ||
165 | { | ||
166 | _workItem.OnWorkItemCompleted -= value; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | #endregion | ||
171 | |||
172 | #region IInternalWorkItemResult Members | ||
173 | |||
174 | public IWorkItemResult GetWorkItemResult() | ||
175 | { | ||
176 | return this; | ||
177 | } | ||
178 | |||
179 | public IWorkItemResult<TResult> GetWorkItemResultT<TResult>() | ||
180 | { | ||
181 | return new WorkItemResultTWrapper<TResult>(this); | ||
182 | } | ||
183 | |||
184 | #endregion | ||
185 | } | ||
186 | |||
187 | #endregion | ||
188 | |||
189 | } | ||
190 | } | ||
diff --git a/ThirdParty/SmartThreadPool/WorkItem.cs b/ThirdParty/SmartThreadPool/WorkItem.cs index d0c0524..f229d1f 100644 --- a/ThirdParty/SmartThreadPool/WorkItem.cs +++ b/ThirdParty/SmartThreadPool/WorkItem.cs | |||
@@ -1,58 +1,13 @@ | |||
1 | // Ami Bar | ||
2 | // amibar@gmail.com | ||
3 | |||
4 | using System; | 1 | using System; |
5 | using System.Threading; | 2 | using System.Threading; |
6 | using System.Diagnostics; | 3 | using System.Diagnostics; |
7 | 4 | ||
8 | namespace Amib.Threading.Internal | 5 | namespace Amib.Threading.Internal |
9 | { | 6 | { |
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> | 7 | /// <summary> |
53 | /// Holds a callback delegate and the state for that delegate. | 8 | /// Holds a callback delegate and the state for that delegate. |
54 | /// </summary> | 9 | /// </summary> |
55 | public class WorkItem : IHasWorkItemPriority, IWorkItem | 10 | public partial class WorkItem : IHasWorkItemPriority |
56 | { | 11 | { |
57 | #region WorkItemState enum | 12 | #region WorkItemState enum |
58 | 13 | ||
@@ -61,33 +16,57 @@ namespace Amib.Threading.Internal | |||
61 | /// </summary> | 16 | /// </summary> |
62 | private enum WorkItemState | 17 | private enum WorkItemState |
63 | { | 18 | { |
64 | InQueue, | 19 | InQueue = 0, // Nexts: InProgress, Canceled |
65 | InProgress, | 20 | InProgress = 1, // Nexts: Completed, Canceled |
66 | Completed, | 21 | Completed = 2, // Stays Completed |
67 | Canceled, | 22 | Canceled = 3, // Stays Canceled |
68 | } | 23 | } |
69 | 24 | ||
70 | #endregion | 25 | private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState) |
26 | { | ||
27 | bool valid = false; | ||
28 | |||
29 | switch (currentState) | ||
30 | { | ||
31 | case WorkItemState.InQueue: | ||
32 | valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState); | ||
33 | break; | ||
34 | case WorkItemState.InProgress: | ||
35 | valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState); | ||
36 | break; | ||
37 | case WorkItemState.Completed: | ||
38 | case WorkItemState.Canceled: | ||
39 | // Cannot be changed | ||
40 | break; | ||
41 | default: | ||
42 | // Unknown state | ||
43 | Debug.Assert(false); | ||
44 | break; | ||
45 | } | ||
71 | 46 | ||
72 | #region Member Variables | 47 | return valid; |
48 | } | ||
73 | 49 | ||
74 | public Thread currentThread; | 50 | #endregion |
51 | |||
52 | #region Fields | ||
75 | 53 | ||
76 | /// <summary> | 54 | /// <summary> |
77 | /// Callback delegate for the callback. | 55 | /// Callback delegate for the callback. |
78 | /// </summary> | 56 | /// </summary> |
79 | private WorkItemCallback _callback; | 57 | private readonly WorkItemCallback _callback; |
80 | 58 | ||
81 | /// <summary> | 59 | /// <summary> |
82 | /// State with which to call the callback delegate. | 60 | /// State with which to call the callback delegate. |
83 | /// </summary> | 61 | /// </summary> |
84 | private object _state; | 62 | private object _state; |
85 | 63 | ||
64 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) | ||
86 | /// <summary> | 65 | /// <summary> |
87 | /// Stores the caller's context | 66 | /// Stores the caller's context |
88 | /// </summary> | 67 | /// </summary> |
89 | private CallerThreadContext _callerContext; | 68 | private readonly CallerThreadContext _callerContext; |
90 | 69 | #endif | |
91 | /// <summary> | 70 | /// <summary> |
92 | /// Holds the result of the mehtod | 71 | /// Holds the result of the mehtod |
93 | /// </summary> | 72 | /// </summary> |
@@ -117,12 +96,12 @@ namespace Amib.Threading.Internal | |||
117 | /// <summary> | 96 | /// <summary> |
118 | /// Represents the result state of the work item | 97 | /// Represents the result state of the work item |
119 | /// </summary> | 98 | /// </summary> |
120 | private WorkItemResult _workItemResult; | 99 | private readonly WorkItemResult _workItemResult; |
121 | 100 | ||
122 | /// <summary> | 101 | /// <summary> |
123 | /// Work item info | 102 | /// Work item info |
124 | /// </summary> | 103 | /// </summary> |
125 | private WorkItemInfo _workItemInfo; | 104 | private readonly WorkItemInfo _workItemInfo; |
126 | 105 | ||
127 | /// <summary> | 106 | /// <summary> |
128 | /// Called when the WorkItem starts | 107 | /// Called when the WorkItem starts |
@@ -141,30 +120,41 @@ namespace Amib.Threading.Internal | |||
141 | private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; | 120 | private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; |
142 | 121 | ||
143 | /// <summary> | 122 | /// <summary> |
123 | /// A reference to an object that indicates whatever the | ||
124 | /// SmartThreadPool has been canceled | ||
125 | /// </summary> | ||
126 | private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; | ||
127 | |||
128 | /// <summary> | ||
144 | /// The work item group this work item belong to. | 129 | /// The work item group this work item belong to. |
145 | /// | ||
146 | /// </summary> | 130 | /// </summary> |
147 | private IWorkItemsGroup _workItemsGroup; | 131 | private readonly IWorkItemsGroup _workItemsGroup; |
148 | 132 | ||
149 | #region Performance Counter fields | 133 | /// <summary> |
134 | /// The thread that executes this workitem. | ||
135 | /// This field is available for the period when the work item is executed, before and after it is null. | ||
136 | /// </summary> | ||
137 | private Thread _executingThread; | ||
150 | 138 | ||
151 | /// <summary> | 139 | /// <summary> |
152 | /// The time when the work items is queued. | 140 | /// The absulote time when the work item will be timeout |
153 | /// Used with the performance counter. | ||
154 | /// </summary> | 141 | /// </summary> |
155 | private DateTime _queuedTime; | 142 | private long _expirationTime; |
143 | |||
144 | #region Performance Counter fields | ||
145 | |||
146 | |||
147 | |||
156 | 148 | ||
157 | /// <summary> | 149 | /// <summary> |
158 | /// The time when the work items starts its execution. | 150 | /// Stores how long the work item waited on the stp queue |
159 | /// Used with the performance counter. | ||
160 | /// </summary> | 151 | /// </summary> |
161 | private DateTime _beginProcessTime; | 152 | private Stopwatch _waitingOnQueueStopwatch; |
162 | 153 | ||
163 | /// <summary> | 154 | /// <summary> |
164 | /// The time when the work items ends its execution. | 155 | /// Stores how much time it took the work item to execute after it went out of the queue |
165 | /// Used with the performance counter. | ||
166 | /// </summary> | 156 | /// </summary> |
167 | private DateTime _endProcessTime; | 157 | private Stopwatch _processingStopwatch; |
168 | 158 | ||
169 | #endregion | 159 | #endregion |
170 | 160 | ||
@@ -174,17 +164,25 @@ namespace Amib.Threading.Internal | |||
174 | 164 | ||
175 | public TimeSpan WaitingTime | 165 | public TimeSpan WaitingTime |
176 | { | 166 | { |
177 | get | 167 | get |
178 | { | 168 | { |
179 | return (_beginProcessTime - _queuedTime); | 169 | return _waitingOnQueueStopwatch.Elapsed; |
180 | } | 170 | } |
181 | } | 171 | } |
182 | 172 | ||
183 | public TimeSpan ProcessTime | 173 | public TimeSpan ProcessTime |
184 | { | 174 | { |
185 | get | 175 | get |
176 | { | ||
177 | return _processingStopwatch.Elapsed; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | internal WorkItemInfo WorkItemInfo | ||
182 | { | ||
183 | get | ||
186 | { | 184 | { |
187 | return (_endProcessTime - _beginProcessTime); | 185 | return _workItemInfo; |
188 | } | 186 | } |
189 | } | 187 | } |
190 | 188 | ||
@@ -195,6 +193,8 @@ namespace Amib.Threading.Internal | |||
195 | /// <summary> | 193 | /// <summary> |
196 | /// Initialize the callback holding object. | 194 | /// Initialize the callback holding object. |
197 | /// </summary> | 195 | /// </summary> |
196 | /// <param name="workItemsGroup">The workItemGroup of the workitem</param> | ||
197 | /// <param name="workItemInfo">The WorkItemInfo of te workitem</param> | ||
198 | /// <param name="callback">Callback delegate for the callback.</param> | 198 | /// <param name="callback">Callback delegate for the callback.</param> |
199 | /// <param name="state">State with which to call the callback delegate.</param> | 199 | /// <param name="state">State with which to call the callback delegate.</param> |
200 | /// | 200 | /// |
@@ -203,16 +203,18 @@ namespace Amib.Threading.Internal | |||
203 | public WorkItem( | 203 | public WorkItem( |
204 | IWorkItemsGroup workItemsGroup, | 204 | IWorkItemsGroup workItemsGroup, |
205 | WorkItemInfo workItemInfo, | 205 | WorkItemInfo workItemInfo, |
206 | WorkItemCallback callback, | 206 | WorkItemCallback callback, |
207 | object state) | 207 | object state) |
208 | { | 208 | { |
209 | _workItemsGroup = workItemsGroup; | 209 | _workItemsGroup = workItemsGroup; |
210 | _workItemInfo = workItemInfo; | 210 | _workItemInfo = workItemInfo; |
211 | 211 | ||
212 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) | ||
212 | if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) | 213 | if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) |
213 | { | 214 | { |
214 | _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); | 215 | _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); |
215 | } | 216 | } |
217 | #endif | ||
216 | 218 | ||
217 | _callback = callback; | 219 | _callback = callback; |
218 | _state = state; | 220 | _state = state; |
@@ -222,9 +224,18 @@ namespace Amib.Threading.Internal | |||
222 | 224 | ||
223 | internal void Initialize() | 225 | internal void Initialize() |
224 | { | 226 | { |
227 | // The _workItemState is changed directly instead of using the SetWorkItemState | ||
228 | // method since we don't want to go throught IsValidStateTransition. | ||
225 | _workItemState = WorkItemState.InQueue; | 229 | _workItemState = WorkItemState.InQueue; |
230 | |||
226 | _workItemCompleted = null; | 231 | _workItemCompleted = null; |
227 | _workItemCompletedRefCount = 0; | 232 | _workItemCompletedRefCount = 0; |
233 | _waitingOnQueueStopwatch = new Stopwatch(); | ||
234 | _processingStopwatch = new Stopwatch(); | ||
235 | _expirationTime = | ||
236 | _workItemInfo.Timeout > 0 ? | ||
237 | DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond : | ||
238 | long.MaxValue; | ||
228 | } | 239 | } |
229 | 240 | ||
230 | internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) | 241 | internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) |
@@ -237,17 +248,16 @@ namespace Amib.Threading.Internal | |||
237 | 248 | ||
238 | #region Methods | 249 | #region Methods |
239 | 250 | ||
240 | public CanceledWorkItemsGroup CanceledWorkItemsGroup | 251 | internal CanceledWorkItemsGroup CanceledWorkItemsGroup |
241 | { | 252 | { |
242 | get | 253 | get { return _canceledWorkItemsGroup; } |
243 | { | 254 | set { _canceledWorkItemsGroup = value; } |
244 | return _canceledWorkItemsGroup; | 255 | } |
245 | } | ||
246 | 256 | ||
247 | set | 257 | internal CanceledWorkItemsGroup CanceledSmartThreadPool |
248 | { | 258 | { |
249 | _canceledWorkItemsGroup = value; | 259 | get { return _canceledSmartThreadPool; } |
250 | } | 260 | set { _canceledSmartThreadPool = value; } |
251 | } | 261 | } |
252 | 262 | ||
253 | /// <summary> | 263 | /// <summary> |
@@ -259,9 +269,10 @@ namespace Amib.Threading.Internal | |||
259 | /// </returns> | 269 | /// </returns> |
260 | public bool StartingWorkItem() | 270 | public bool StartingWorkItem() |
261 | { | 271 | { |
262 | _beginProcessTime = DateTime.Now; | 272 | _waitingOnQueueStopwatch.Stop(); |
273 | _processingStopwatch.Start(); | ||
263 | 274 | ||
264 | lock(this) | 275 | lock (this) |
265 | { | 276 | { |
266 | if (IsCanceled) | 277 | if (IsCanceled) |
267 | { | 278 | { |
@@ -277,6 +288,9 @@ namespace Amib.Threading.Internal | |||
277 | 288 | ||
278 | Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); | 289 | Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); |
279 | 290 | ||
291 | // No need for a lock yet, only after the state has changed to InProgress | ||
292 | _executingThread = Thread.CurrentThread; | ||
293 | |||
280 | SetWorkItemState(WorkItemState.InProgress); | 294 | SetWorkItemState(WorkItemState.InProgress); |
281 | } | 295 | } |
282 | 296 | ||
@@ -291,7 +305,7 @@ namespace Amib.Threading.Internal | |||
291 | CallToPostExecute currentCallToPostExecute = 0; | 305 | CallToPostExecute currentCallToPostExecute = 0; |
292 | 306 | ||
293 | // Execute the work item if we are in the correct state | 307 | // Execute the work item if we are in the correct state |
294 | switch(GetWorkItemState()) | 308 | switch (GetWorkItemState()) |
295 | { | 309 | { |
296 | case WorkItemState.InProgress: | 310 | case WorkItemState.InProgress: |
297 | currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled; | 311 | currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled; |
@@ -311,7 +325,7 @@ namespace Amib.Threading.Internal | |||
311 | PostExecute(); | 325 | PostExecute(); |
312 | } | 326 | } |
313 | 327 | ||
314 | _endProcessTime = DateTime.Now; | 328 | _processingStopwatch.Stop(); |
315 | } | 329 | } |
316 | 330 | ||
317 | internal void FireWorkItemCompleted() | 331 | internal void FireWorkItemCompleted() |
@@ -323,8 +337,21 @@ namespace Amib.Threading.Internal | |||
323 | _workItemCompletedEvent(this); | 337 | _workItemCompletedEvent(this); |
324 | } | 338 | } |
325 | } | 339 | } |
326 | catch // Ignore exceptions | 340 | catch // Suppress exceptions |
327 | {} | 341 | { } |
342 | } | ||
343 | |||
344 | internal void FireWorkItemStarted() | ||
345 | { | ||
346 | try | ||
347 | { | ||
348 | if (null != _workItemStartedEvent) | ||
349 | { | ||
350 | _workItemStartedEvent(this); | ||
351 | } | ||
352 | } | ||
353 | catch // Suppress exceptions | ||
354 | { } | ||
328 | } | 355 | } |
329 | 356 | ||
330 | /// <summary> | 357 | /// <summary> |
@@ -332,32 +359,70 @@ namespace Amib.Threading.Internal | |||
332 | /// </summary> | 359 | /// </summary> |
333 | private void ExecuteWorkItem() | 360 | private void ExecuteWorkItem() |
334 | { | 361 | { |
362 | |||
363 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) | ||
335 | CallerThreadContext ctc = null; | 364 | CallerThreadContext ctc = null; |
336 | if (null != _callerContext) | 365 | if (null != _callerContext) |
337 | { | 366 | { |
338 | ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); | 367 | ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); |
339 | CallerThreadContext.Apply(_callerContext); | 368 | CallerThreadContext.Apply(_callerContext); |
340 | } | 369 | } |
370 | #endif | ||
341 | 371 | ||
342 | Exception exception = null; | 372 | Exception exception = null; |
343 | object result = null; | 373 | object result = null; |
344 | 374 | ||
345 | try | 375 | try |
346 | { | 376 | { |
347 | result = _callback(_state); | 377 | try |
378 | { | ||
379 | result = _callback(_state); | ||
380 | } | ||
381 | catch (Exception e) | ||
382 | { | ||
383 | // Save the exception so we can rethrow it later | ||
384 | exception = e; | ||
385 | } | ||
386 | |||
387 | // Remove the value of the execution thread, so it will be impossible to cancel the work item, | ||
388 | // since it is already completed. | ||
389 | // Cancelling a work item that already completed may cause the abortion of the next work item!!! | ||
390 | Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); | ||
391 | |||
392 | if (null == executionThread) | ||
393 | { | ||
394 | // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException | ||
395 | Thread.Sleep(60 * 1000); | ||
396 | |||
397 | // If after 1 minute this thread was not aborted then let it continue working. | ||
398 | } | ||
348 | } | 399 | } |
349 | catch (Exception e) | 400 | // We must treat the ThreadAbortException or else it will be stored in the exception variable |
401 | catch (ThreadAbortException tae) | ||
350 | { | 402 | { |
351 | // Save the exception so we can rethrow it later | 403 | tae.GetHashCode(); |
352 | exception = e; | 404 | // Check if the work item was cancelled |
405 | // If we got a ThreadAbortException and the STP is not shutting down, it means the | ||
406 | // work items was cancelled. | ||
407 | if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown) | ||
408 | { | ||
409 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) | ||
410 | Thread.ResetAbort(); | ||
411 | #endif | ||
412 | } | ||
353 | } | 413 | } |
354 | 414 | ||
415 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) | ||
355 | if (null != _callerContext) | 416 | if (null != _callerContext) |
356 | { | 417 | { |
357 | CallerThreadContext.Apply(ctc); | 418 | CallerThreadContext.Apply(ctc); |
358 | } | 419 | } |
420 | #endif | ||
359 | 421 | ||
360 | SetResult(result, exception); | 422 | if (!SmartThreadPool.IsWorkItemCanceled) |
423 | { | ||
424 | SetResult(result, exception); | ||
425 | } | ||
361 | } | 426 | } |
362 | 427 | ||
363 | /// <summary> | 428 | /// <summary> |
@@ -369,9 +434,9 @@ namespace Amib.Threading.Internal | |||
369 | { | 434 | { |
370 | try | 435 | try |
371 | { | 436 | { |
372 | _workItemInfo.PostExecuteWorkItemCallback(this._workItemResult); | 437 | _workItemInfo.PostExecuteWorkItemCallback(_workItemResult); |
373 | } | 438 | } |
374 | catch (Exception e) | 439 | catch (Exception e) |
375 | { | 440 | { |
376 | Debug.Assert(null != e); | 441 | Debug.Assert(null != e); |
377 | } | 442 | } |
@@ -382,6 +447,8 @@ namespace Amib.Threading.Internal | |||
382 | /// Set the result of the work item to return | 447 | /// Set the result of the work item to return |
383 | /// </summary> | 448 | /// </summary> |
384 | /// <param name="result">The result of the work item</param> | 449 | /// <param name="result">The result of the work item</param> |
450 | /// <param name="exception">The exception that was throw while the workitem executed, null | ||
451 | /// if there was no exception.</param> | ||
385 | internal void SetResult(object result, Exception exception) | 452 | internal void SetResult(object result, Exception exception) |
386 | { | 453 | { |
387 | _result = result; | 454 | _result = result; |
@@ -401,48 +468,48 @@ namespace Amib.Threading.Internal | |||
401 | /// <summary> | 468 | /// <summary> |
402 | /// Wait for all work items to complete | 469 | /// Wait for all work items to complete |
403 | /// </summary> | 470 | /// </summary> |
404 | /// <param name="workItemResults">Array of work item result objects</param> | 471 | /// <param name="waitableResults">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> | 472 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
406 | /// <param name="exitContext"> | 473 | /// <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. | 474 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
408 | /// </param> | 475 | /// </param> |
409 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> | 476 | /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> |
410 | /// <returns> | 477 | /// <returns> |
411 | /// true when every work item in workItemResults has completed; otherwise false. | 478 | /// true when every work item in waitableResults has completed; otherwise false. |
412 | /// </returns> | 479 | /// </returns> |
413 | internal static bool WaitAll( | 480 | internal static bool WaitAll( |
414 | IWorkItemResult [] workItemResults, | 481 | IWaitableResult[] waitableResults, |
415 | int millisecondsTimeout, | 482 | int millisecondsTimeout, |
416 | bool exitContext, | 483 | bool exitContext, |
417 | WaitHandle cancelWaitHandle) | 484 | WaitHandle cancelWaitHandle) |
418 | { | 485 | { |
419 | if (0 == workItemResults.Length) | 486 | if (0 == waitableResults.Length) |
420 | { | 487 | { |
421 | return true; | 488 | return true; |
422 | } | 489 | } |
423 | 490 | ||
424 | bool success; | 491 | bool success; |
425 | WaitHandle [] waitHandles = new WaitHandle[workItemResults.Length];; | 492 | WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; |
426 | GetWaitHandles(workItemResults, waitHandles); | 493 | GetWaitHandles(waitableResults, waitHandles); |
427 | 494 | ||
428 | if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) | 495 | if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) |
429 | { | 496 | { |
430 | success = WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); | 497 | success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); |
431 | } | 498 | } |
432 | else | 499 | else |
433 | { | 500 | { |
434 | success = true; | 501 | success = true; |
435 | int millisecondsLeft = millisecondsTimeout; | 502 | int millisecondsLeft = millisecondsTimeout; |
436 | DateTime start = DateTime.Now; | 503 | Stopwatch stopwatch = Stopwatch.StartNew(); |
437 | 504 | ||
438 | WaitHandle [] whs; | 505 | WaitHandle[] whs; |
439 | if (null != cancelWaitHandle) | 506 | if (null != cancelWaitHandle) |
440 | { | 507 | { |
441 | whs = new WaitHandle [] { null, cancelWaitHandle }; | 508 | whs = new WaitHandle[] { null, cancelWaitHandle }; |
442 | } | 509 | } |
443 | else | 510 | else |
444 | { | 511 | { |
445 | whs = new WaitHandle [] { null }; | 512 | whs = new WaitHandle[] { null }; |
446 | } | 513 | } |
447 | 514 | ||
448 | bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); | 515 | bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); |
@@ -450,7 +517,7 @@ namespace Amib.Threading.Internal | |||
450 | // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle | 517 | // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle |
451 | // won't affect it. | 518 | // won't affect it. |
452 | // Each iteration we update the time left for the timeout. | 519 | // Each iteration we update the time left for the timeout. |
453 | for(int i = 0; i < workItemResults.Length; ++i) | 520 | for (int i = 0; i < waitableResults.Length; ++i) |
454 | { | 521 | { |
455 | // WaitAny don't work with negative numbers | 522 | // WaitAny don't work with negative numbers |
456 | if (!waitInfinitely && (millisecondsLeft < 0)) | 523 | if (!waitInfinitely && (millisecondsLeft < 0)) |
@@ -460,23 +527,22 @@ namespace Amib.Threading.Internal | |||
460 | } | 527 | } |
461 | 528 | ||
462 | whs[0] = waitHandles[i]; | 529 | whs[0] = waitHandles[i]; |
463 | int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext); | 530 | int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); |
464 | if((result > 0) || (WaitHandle.WaitTimeout == result)) | 531 | if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) |
465 | { | 532 | { |
466 | success = false; | 533 | success = false; |
467 | break; | 534 | break; |
468 | } | 535 | } |
469 | 536 | ||
470 | if(!waitInfinitely) | 537 | if (!waitInfinitely) |
471 | { | 538 | { |
472 | // Update the time left to wait | 539 | // Update the time left to wait |
473 | TimeSpan ts = DateTime.Now - start; | 540 | millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; |
474 | millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds; | ||
475 | } | 541 | } |
476 | } | 542 | } |
477 | } | 543 | } |
478 | // Release the wait handles | 544 | // Release the wait handles |
479 | ReleaseWaitHandles(workItemResults); | 545 | ReleaseWaitHandles(waitableResults); |
480 | 546 | ||
481 | return success; | 547 | return success; |
482 | } | 548 | } |
@@ -484,7 +550,7 @@ namespace Amib.Threading.Internal | |||
484 | /// <summary> | 550 | /// <summary> |
485 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout | 551 | /// Waits for any of the work items in the specified array to complete, cancel, or timeout |
486 | /// </summary> | 552 | /// </summary> |
487 | /// <param name="workItemResults">Array of work item result objects</param> | 553 | /// <param name="waitableResults">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> | 554 | /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> |
489 | /// <param name="exitContext"> | 555 | /// <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. | 556 | /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. |
@@ -493,38 +559,38 @@ namespace Amib.Threading.Internal | |||
493 | /// <returns> | 559 | /// <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. | 560 | /// 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> | 561 | /// </returns> |
496 | internal static int WaitAny( | 562 | internal static int WaitAny( |
497 | IWorkItemResult [] workItemResults, | 563 | IWaitableResult[] waitableResults, |
498 | int millisecondsTimeout, | 564 | int millisecondsTimeout, |
499 | bool exitContext, | 565 | bool exitContext, |
500 | WaitHandle cancelWaitHandle) | 566 | WaitHandle cancelWaitHandle) |
501 | { | 567 | { |
502 | WaitHandle [] waitHandles = null; | 568 | WaitHandle[] waitHandles; |
503 | 569 | ||
504 | if (null != cancelWaitHandle) | 570 | if (null != cancelWaitHandle) |
505 | { | 571 | { |
506 | waitHandles = new WaitHandle[workItemResults.Length+1]; | 572 | waitHandles = new WaitHandle[waitableResults.Length + 1]; |
507 | GetWaitHandles(workItemResults, waitHandles); | 573 | GetWaitHandles(waitableResults, waitHandles); |
508 | waitHandles[workItemResults.Length] = cancelWaitHandle; | 574 | waitHandles[waitableResults.Length] = cancelWaitHandle; |
509 | } | 575 | } |
510 | else | 576 | else |
511 | { | 577 | { |
512 | waitHandles = new WaitHandle[workItemResults.Length]; | 578 | waitHandles = new WaitHandle[waitableResults.Length]; |
513 | GetWaitHandles(workItemResults, waitHandles); | 579 | GetWaitHandles(waitableResults, waitHandles); |
514 | } | 580 | } |
515 | 581 | ||
516 | int result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); | 582 | int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); |
517 | 583 | ||
518 | // Treat cancel as timeout | 584 | // Treat cancel as timeout |
519 | if (null != cancelWaitHandle) | 585 | if (null != cancelWaitHandle) |
520 | { | 586 | { |
521 | if (result == workItemResults.Length) | 587 | if (result == waitableResults.Length) |
522 | { | 588 | { |
523 | result = WaitHandle.WaitTimeout; | 589 | result = STPEventWaitHandle.WaitTimeout; |
524 | } | 590 | } |
525 | } | 591 | } |
526 | 592 | ||
527 | ReleaseWaitHandles(workItemResults); | 593 | ReleaseWaitHandles(waitableResults); |
528 | 594 | ||
529 | return result; | 595 | return result; |
530 | } | 596 | } |
@@ -532,16 +598,16 @@ namespace Amib.Threading.Internal | |||
532 | /// <summary> | 598 | /// <summary> |
533 | /// Fill an array of wait handles with the work items wait handles. | 599 | /// Fill an array of wait handles with the work items wait handles. |
534 | /// </summary> | 600 | /// </summary> |
535 | /// <param name="workItemResults">An array of work item results</param> | 601 | /// <param name="waitableResults">An array of work item results</param> |
536 | /// <param name="waitHandles">An array of wait handles to fill</param> | 602 | /// <param name="waitHandles">An array of wait handles to fill</param> |
537 | private static void GetWaitHandles( | 603 | private static void GetWaitHandles( |
538 | IWorkItemResult [] workItemResults, | 604 | IWaitableResult[] waitableResults, |
539 | WaitHandle [] waitHandles) | 605 | WaitHandle[] waitHandles) |
540 | { | 606 | { |
541 | for(int i = 0; i < workItemResults.Length; ++i) | 607 | for (int i = 0; i < waitableResults.Length; ++i) |
542 | { | 608 | { |
543 | WorkItemResult wir = workItemResults[i] as WorkItemResult; | 609 | WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult; |
544 | Debug.Assert(null != wir, "All workItemResults must be WorkItemResult objects"); | 610 | Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects"); |
545 | 611 | ||
546 | waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); | 612 | waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); |
547 | } | 613 | } |
@@ -550,40 +616,64 @@ namespace Amib.Threading.Internal | |||
550 | /// <summary> | 616 | /// <summary> |
551 | /// Release the work items' wait handles | 617 | /// Release the work items' wait handles |
552 | /// </summary> | 618 | /// </summary> |
553 | /// <param name="workItemResults">An array of work item results</param> | 619 | /// <param name="waitableResults">An array of work item results</param> |
554 | private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults) | 620 | private static void ReleaseWaitHandles(IWaitableResult[] waitableResults) |
555 | { | 621 | { |
556 | for(int i = 0; i < workItemResults.Length; ++i) | 622 | for (int i = 0; i < waitableResults.Length; ++i) |
557 | { | 623 | { |
558 | WorkItemResult wir = workItemResults[i] as WorkItemResult; | 624 | WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult(); |
559 | 625 | ||
560 | wir.GetWorkItem().ReleaseWaitHandle(); | 626 | wir.GetWorkItem().ReleaseWaitHandle(); |
561 | } | 627 | } |
562 | } | 628 | } |
563 | 629 | ||
564 | |||
565 | #endregion | 630 | #endregion |
566 | 631 | ||
567 | #region Private Members | 632 | #region Private Members |
568 | 633 | ||
569 | private WorkItemState GetWorkItemState() | 634 | private WorkItemState GetWorkItemState() |
570 | { | 635 | { |
571 | if (_canceledWorkItemsGroup.IsCanceled) | 636 | lock (this) |
572 | { | 637 | { |
573 | return WorkItemState.Canceled; | 638 | if (WorkItemState.Completed == _workItemState) |
574 | } | 639 | { |
575 | return _workItemState; | 640 | return _workItemState; |
641 | } | ||
642 | |||
643 | long nowTicks = DateTime.UtcNow.Ticks; | ||
576 | 644 | ||
645 | if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime) | ||
646 | { | ||
647 | _workItemState = WorkItemState.Canceled; | ||
648 | } | ||
649 | |||
650 | if (WorkItemState.InProgress == _workItemState) | ||
651 | { | ||
652 | return _workItemState; | ||
653 | } | ||
654 | |||
655 | if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled) | ||
656 | { | ||
657 | return WorkItemState.Canceled; | ||
658 | } | ||
659 | |||
660 | return _workItemState; | ||
661 | } | ||
577 | } | 662 | } |
663 | |||
664 | |||
578 | /// <summary> | 665 | /// <summary> |
579 | /// Sets the work item's state | 666 | /// Sets the work item's state |
580 | /// </summary> | 667 | /// </summary> |
581 | /// <param name="workItemState">The state to set the work item to</param> | 668 | /// <param name="workItemState">The state to set the work item to</param> |
582 | private void SetWorkItemState(WorkItemState workItemState) | 669 | private void SetWorkItemState(WorkItemState workItemState) |
583 | { | 670 | { |
584 | lock(this) | 671 | lock (this) |
585 | { | 672 | { |
586 | _workItemState = workItemState; | 673 | if (IsValidStatesTransition(_workItemState, workItemState)) |
674 | { | ||
675 | _workItemState = workItemState; | ||
676 | } | ||
587 | } | 677 | } |
588 | } | 678 | } |
589 | 679 | ||
@@ -594,7 +684,7 @@ namespace Amib.Threading.Internal | |||
594 | private void SignalComplete(bool canceled) | 684 | private void SignalComplete(bool canceled) |
595 | { | 685 | { |
596 | SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed); | 686 | SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed); |
597 | lock(this) | 687 | lock (this) |
598 | { | 688 | { |
599 | // If someone is waiting then signal. | 689 | // If someone is waiting then signal. |
600 | if (null != _workItemCompleted) | 690 | if (null != _workItemCompleted) |
@@ -606,40 +696,83 @@ namespace Amib.Threading.Internal | |||
606 | 696 | ||
607 | internal void WorkItemIsQueued() | 697 | internal void WorkItemIsQueued() |
608 | { | 698 | { |
609 | _queuedTime = DateTime.Now; | 699 | _waitingOnQueueStopwatch.Start(); |
610 | } | 700 | } |
611 | 701 | ||
612 | #endregion | 702 | #endregion |
613 | 703 | ||
614 | #region Members exposed by WorkItemResult | 704 | #region Members exposed by WorkItemResult |
615 | 705 | ||
616 | /// <summary> | 706 | /// <summary> |
617 | /// Cancel the work item if it didn't start running yet. | 707 | /// Cancel the work item if it didn't start running yet. |
618 | /// </summary> | 708 | /// </summary> |
619 | /// <returns>Returns true on success or false if the work item is in progress or already completed</returns> | 709 | /// <returns>Returns true on success or false if the work item is in progress or already completed</returns> |
620 | private bool Cancel() | 710 | private bool Cancel(bool abortExecution) |
621 | { | 711 | { |
622 | lock(this) | 712 | #if (_WINDOWS_CE) |
713 | if(abortExecution) | ||
714 | { | ||
715 | throw new ArgumentOutOfRangeException("abortExecution", "WindowsCE doesn't support this feature"); | ||
716 | } | ||
717 | #endif | ||
718 | bool success = false; | ||
719 | bool signalComplete = false; | ||
720 | |||
721 | lock (this) | ||
623 | { | 722 | { |
624 | switch(GetWorkItemState()) | 723 | switch (GetWorkItemState()) |
625 | { | 724 | { |
626 | case WorkItemState.Canceled: | 725 | case WorkItemState.Canceled: |
627 | //Debug.WriteLine("Work item already canceled"); | 726 | //Debug.WriteLine("Work item already canceled"); |
628 | return true; | 727 | if (abortExecution) |
728 | { | ||
729 | Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); | ||
730 | if (null != executionThread) | ||
731 | { | ||
732 | executionThread.Abort(); // "Cancel" | ||
733 | // No need to signalComplete, because we already cancelled this work item | ||
734 | // so it already signaled its completion. | ||
735 | //signalComplete = true; | ||
736 | } | ||
737 | } | ||
738 | success = true; | ||
739 | break; | ||
629 | case WorkItemState.Completed: | 740 | case WorkItemState.Completed: |
630 | case WorkItemState.InProgress: | ||
631 | //Debug.WriteLine("Work item cannot be canceled"); | 741 | //Debug.WriteLine("Work item cannot be canceled"); |
632 | return false; | 742 | break; |
743 | case WorkItemState.InProgress: | ||
744 | if (abortExecution) | ||
745 | { | ||
746 | Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); | ||
747 | if (null != executionThread) | ||
748 | { | ||
749 | executionThread.Abort(); // "Cancel" | ||
750 | success = true; | ||
751 | signalComplete = true; | ||
752 | } | ||
753 | } | ||
754 | else | ||
755 | { | ||
756 | success = false; | ||
757 | signalComplete = false; | ||
758 | } | ||
759 | break; | ||
633 | case WorkItemState.InQueue: | 760 | case WorkItemState.InQueue: |
634 | // Signal to the wait for completion that the work | 761 | // Signal to the wait for completion that the work |
635 | // item has been completed (canceled). There is no | 762 | // item has been completed (canceled). There is no |
636 | // reason to wait for it to get out of the queue | 763 | // reason to wait for it to get out of the queue |
637 | SignalComplete(true); | 764 | signalComplete = true; |
638 | //Debug.WriteLine("Work item canceled"); | 765 | //Debug.WriteLine("Work item canceled"); |
639 | return true; | 766 | success = true; |
767 | break; | ||
768 | } | ||
769 | |||
770 | if (signalComplete) | ||
771 | { | ||
772 | SignalComplete(true); | ||
640 | } | 773 | } |
641 | } | 774 | } |
642 | return false; | 775 | return success; |
643 | } | 776 | } |
644 | 777 | ||
645 | /// <summary> | 778 | /// <summary> |
@@ -653,7 +786,7 @@ namespace Amib.Threading.Internal | |||
653 | bool exitContext, | 786 | bool exitContext, |
654 | WaitHandle cancelWaitHandle) | 787 | WaitHandle cancelWaitHandle) |
655 | { | 788 | { |
656 | Exception e = null; | 789 | Exception e; |
657 | object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); | 790 | object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); |
658 | if (null != e) | 791 | if (null != e) |
659 | { | 792 | { |
@@ -694,7 +827,7 @@ namespace Amib.Threading.Internal | |||
694 | { | 827 | { |
695 | WaitHandle wh = GetWaitHandle(); | 828 | WaitHandle wh = GetWaitHandle(); |
696 | 829 | ||
697 | bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext); | 830 | bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); |
698 | 831 | ||
699 | ReleaseWaitHandle(); | 832 | ReleaseWaitHandle(); |
700 | 833 | ||
@@ -706,10 +839,10 @@ namespace Amib.Threading.Internal | |||
706 | else | 839 | else |
707 | { | 840 | { |
708 | WaitHandle wh = GetWaitHandle(); | 841 | WaitHandle wh = GetWaitHandle(); |
709 | int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); | 842 | int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); |
710 | ReleaseWaitHandle(); | 843 | ReleaseWaitHandle(); |
711 | 844 | ||
712 | switch(result) | 845 | switch (result) |
713 | { | 846 | { |
714 | case 0: | 847 | case 0: |
715 | // The work item signaled | 848 | // The work item signaled |
@@ -717,7 +850,7 @@ namespace Amib.Threading.Internal | |||
717 | // work item (not the get result) | 850 | // work item (not the get result) |
718 | break; | 851 | break; |
719 | case 1: | 852 | case 1: |
720 | case WaitHandle.WaitTimeout: | 853 | case STPEventWaitHandle.WaitTimeout: |
721 | throw new WorkItemTimeoutException("Work item timeout"); | 854 | throw new WorkItemTimeoutException("Work item timeout"); |
722 | default: | 855 | default: |
723 | Debug.Assert(false); | 856 | Debug.Assert(false); |
@@ -745,11 +878,11 @@ namespace Amib.Threading.Internal | |||
745 | /// </summary> | 878 | /// </summary> |
746 | private WaitHandle GetWaitHandle() | 879 | private WaitHandle GetWaitHandle() |
747 | { | 880 | { |
748 | lock(this) | 881 | lock (this) |
749 | { | 882 | { |
750 | if (null == _workItemCompleted) | 883 | if (null == _workItemCompleted) |
751 | { | 884 | { |
752 | _workItemCompleted = new ManualResetEvent(IsCompleted); | 885 | _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted); |
753 | } | 886 | } |
754 | ++_workItemCompletedRefCount; | 887 | ++_workItemCompletedRefCount; |
755 | } | 888 | } |
@@ -758,7 +891,7 @@ namespace Amib.Threading.Internal | |||
758 | 891 | ||
759 | private void ReleaseWaitHandle() | 892 | private void ReleaseWaitHandle() |
760 | { | 893 | { |
761 | lock(this) | 894 | lock (this) |
762 | { | 895 | { |
763 | if (null != _workItemCompleted) | 896 | if (null != _workItemCompleted) |
764 | { | 897 | { |
@@ -779,10 +912,10 @@ namespace Amib.Threading.Internal | |||
779 | { | 912 | { |
780 | get | 913 | get |
781 | { | 914 | { |
782 | lock(this) | 915 | lock (this) |
783 | { | 916 | { |
784 | WorkItemState workItemState = GetWorkItemState(); | 917 | WorkItemState workItemState = GetWorkItemState(); |
785 | return ((workItemState == WorkItemState.Completed) || | 918 | return ((workItemState == WorkItemState.Completed) || |
786 | (workItemState == WorkItemState.Canceled)); | 919 | (workItemState == WorkItemState.Canceled)); |
787 | } | 920 | } |
788 | } | 921 | } |
@@ -795,7 +928,7 @@ namespace Amib.Threading.Internal | |||
795 | { | 928 | { |
796 | get | 929 | get |
797 | { | 930 | { |
798 | lock(this) | 931 | lock (this) |
799 | { | 932 | { |
800 | return (GetWorkItemState() == WorkItemState.Canceled); | 933 | return (GetWorkItemState() == WorkItemState.Canceled); |
801 | } | 934 | } |
@@ -843,172 +976,6 @@ namespace Amib.Threading.Internal | |||
843 | } | 976 | } |
844 | } | 977 | } |
845 | 978 | ||
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() | 979 | public void DisposeOfState() |
1013 | { | 980 | { |
1014 | if (_workItemInfo.DisposeOfStateObjects) | 981 | if (_workItemInfo.DisposeOfStateObjects) |
@@ -1021,15 +988,5 @@ namespace Amib.Threading.Internal | |||
1021 | } | 988 | } |
1022 | } | 989 | } |
1023 | } | 990 | } |
1024 | |||
1025 | public void Abort() | ||
1026 | { | ||
1027 | lock (this) | ||
1028 | { | ||
1029 | if(currentThread != null) | ||
1030 | currentThread.Abort(); | ||
1031 | } | ||
1032 | } | ||
1033 | } | 991 | } |
1034 | #endregion | ||
1035 | } | 992 | } |
diff --git a/ThirdParty/SmartThreadPool/WorkItemFactory.cs b/ThirdParty/SmartThreadPool/WorkItemFactory.cs index dfcb54f..2d6601e 100644 --- a/ThirdParty/SmartThreadPool/WorkItemFactory.cs +++ b/ThirdParty/SmartThreadPool/WorkItemFactory.cs | |||
@@ -1,333 +1,343 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | |
3 | 3 | namespace Amib.Threading.Internal | |
4 | using System; | 4 | { |
5 | 5 | #region WorkItemFactory class | |
6 | namespace Amib.Threading.Internal | 6 | |
7 | { | 7 | public class WorkItemFactory |
8 | #region WorkItemFactory class | 8 | { |
9 | 9 | /// <summary> | |
10 | public class WorkItemFactory | 10 | /// Create a new work item |
11 | { | 11 | /// </summary> |
12 | /// <summary> | 12 | /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param> |
13 | /// Create a new work item | 13 | /// <param name="wigStartInfo">Work item group start information</param> |
14 | /// </summary> | 14 | /// <param name="callback">A callback to execute</param> |
15 | /// <param name="wigStartInfo">Work item group start information</param> | 15 | /// <returns>Returns a work item</returns> |
16 | /// <param name="callback">A callback to execute</param> | 16 | public static WorkItem CreateWorkItem( |
17 | /// <returns>Returns a work item</returns> | 17 | IWorkItemsGroup workItemsGroup, |
18 | public static WorkItem CreateWorkItem( | 18 | WIGStartInfo wigStartInfo, |
19 | IWorkItemsGroup workItemsGroup, | 19 | WorkItemCallback callback) |
20 | WIGStartInfo wigStartInfo, | 20 | { |
21 | WorkItemCallback callback) | 21 | return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); |
22 | { | 22 | } |
23 | return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); | 23 | |
24 | } | 24 | /// <summary> |
25 | 25 | /// Create a new work item | |
26 | /// <summary> | 26 | /// </summary> |
27 | /// Create a new work item | 27 | /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param> |
28 | /// </summary> | 28 | /// <param name="wigStartInfo">Work item group start information</param> |
29 | /// <param name="wigStartInfo">Work item group start information</param> | 29 | /// <param name="callback">A callback to execute</param> |
30 | /// <param name="callback">A callback to execute</param> | 30 | /// <param name="workItemPriority">The priority of the work item</param> |
31 | /// <param name="workItemPriority">The priority of the work item</param> | 31 | /// <returns>Returns a work item</returns> |
32 | /// <returns>Returns a work item</returns> | 32 | public static WorkItem CreateWorkItem( |
33 | public static WorkItem CreateWorkItem( | 33 | IWorkItemsGroup workItemsGroup, |
34 | IWorkItemsGroup workItemsGroup, | 34 | WIGStartInfo wigStartInfo, |
35 | WIGStartInfo wigStartInfo, | 35 | WorkItemCallback callback, |
36 | WorkItemCallback callback, | 36 | WorkItemPriority workItemPriority) |
37 | WorkItemPriority workItemPriority) | 37 | { |
38 | { | 38 | return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); |
39 | return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); | 39 | } |
40 | } | 40 | |
41 | 41 | /// <summary> | |
42 | /// <summary> | 42 | /// Create a new work item |
43 | /// Create a new work item | 43 | /// </summary> |
44 | /// </summary> | 44 | /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param> |
45 | /// <param name="wigStartInfo">Work item group start information</param> | 45 | /// <param name="wigStartInfo">Work item group start information</param> |
46 | /// <param name="workItemInfo">Work item info</param> | 46 | /// <param name="workItemInfo">Work item info</param> |
47 | /// <param name="callback">A callback to execute</param> | 47 | /// <param name="callback">A callback to execute</param> |
48 | /// <returns>Returns a work item</returns> | 48 | /// <returns>Returns a work item</returns> |
49 | public static WorkItem CreateWorkItem( | 49 | public static WorkItem CreateWorkItem( |
50 | IWorkItemsGroup workItemsGroup, | 50 | IWorkItemsGroup workItemsGroup, |
51 | WIGStartInfo wigStartInfo, | 51 | WIGStartInfo wigStartInfo, |
52 | WorkItemInfo workItemInfo, | 52 | WorkItemInfo workItemInfo, |
53 | WorkItemCallback callback) | 53 | WorkItemCallback callback) |
54 | { | 54 | { |
55 | return CreateWorkItem( | 55 | return CreateWorkItem( |
56 | workItemsGroup, | 56 | workItemsGroup, |
57 | wigStartInfo, | 57 | wigStartInfo, |
58 | workItemInfo, | 58 | workItemInfo, |
59 | callback, | 59 | callback, |
60 | null); | 60 | null); |
61 | } | 61 | } |
62 | 62 | ||
63 | /// <summary> | 63 | /// <summary> |
64 | /// Create a new work item | 64 | /// Create a new work item |
65 | /// </summary> | 65 | /// </summary> |
66 | /// <param name="wigStartInfo">Work item group start information</param> | 66 | /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param> |
67 | /// <param name="callback">A callback to execute</param> | 67 | /// <param name="wigStartInfo">Work item group start information</param> |
68 | /// <param name="state"> | 68 | /// <param name="callback">A callback to execute</param> |
69 | /// The context object of the work item. Used for passing arguments to the work item. | 69 | /// <param name="state"> |
70 | /// </param> | 70 | /// The context object of the work item. Used for passing arguments to the work item. |
71 | /// <returns>Returns a work item</returns> | 71 | /// </param> |
72 | public static WorkItem CreateWorkItem( | 72 | /// <returns>Returns a work item</returns> |
73 | IWorkItemsGroup workItemsGroup, | 73 | public static WorkItem CreateWorkItem( |
74 | WIGStartInfo wigStartInfo, | 74 | IWorkItemsGroup workItemsGroup, |
75 | WorkItemCallback callback, | 75 | WIGStartInfo wigStartInfo, |
76 | object state) | 76 | WorkItemCallback callback, |
77 | { | 77 | object state) |
78 | ValidateCallback(callback); | 78 | { |
79 | 79 | ValidateCallback(callback); | |
80 | WorkItemInfo workItemInfo = new WorkItemInfo(); | 80 | |
81 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; | 81 | WorkItemInfo workItemInfo = new WorkItemInfo(); |
82 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; | 82 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; |
83 | workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; | 83 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; |
84 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; | 84 | workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; |
85 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | 85 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; |
86 | 86 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | |
87 | WorkItem workItem = new WorkItem( | 87 | workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; |
88 | workItemsGroup, | 88 | |
89 | workItemInfo, | 89 | WorkItem workItem = new WorkItem( |
90 | callback, | 90 | workItemsGroup, |
91 | state); | 91 | workItemInfo, |
92 | return workItem; | 92 | callback, |
93 | } | 93 | state); |
94 | 94 | return workItem; | |
95 | /// <summary> | 95 | } |
96 | /// Create a new work item | 96 | |
97 | /// </summary> | 97 | /// <summary> |
98 | /// <param name="wigStartInfo">Work item group start information</param> | 98 | /// Create a new work item |
99 | /// <param name="callback">A callback to execute</param> | 99 | /// </summary> |
100 | /// <param name="state"> | 100 | /// <param name="workItemsGroup">The work items group</param> |
101 | /// The context object of the work item. Used for passing arguments to the work item. | 101 | /// <param name="wigStartInfo">Work item group start information</param> |
102 | /// </param> | 102 | /// <param name="callback">A callback to execute</param> |
103 | /// <param name="workItemPriority">The work item priority</param> | 103 | /// <param name="state"> |
104 | /// <returns>Returns a work item</returns> | 104 | /// The context object of the work item. Used for passing arguments to the work item. |
105 | public static WorkItem CreateWorkItem( | 105 | /// </param> |
106 | IWorkItemsGroup workItemsGroup, | 106 | /// <param name="workItemPriority">The work item priority</param> |
107 | WIGStartInfo wigStartInfo, | 107 | /// <returns>Returns a work item</returns> |
108 | WorkItemCallback callback, | 108 | public static WorkItem CreateWorkItem( |
109 | object state, | 109 | IWorkItemsGroup workItemsGroup, |
110 | WorkItemPriority workItemPriority) | 110 | WIGStartInfo wigStartInfo, |
111 | { | 111 | WorkItemCallback callback, |
112 | ValidateCallback(callback); | 112 | object state, |
113 | 113 | WorkItemPriority workItemPriority) | |
114 | WorkItemInfo workItemInfo = new WorkItemInfo(); | 114 | { |
115 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; | 115 | ValidateCallback(callback); |
116 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; | 116 | |
117 | workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; | 117 | WorkItemInfo workItemInfo = new WorkItemInfo(); |
118 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; | 118 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; |
119 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | 119 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; |
120 | workItemInfo.WorkItemPriority = workItemPriority; | 120 | workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; |
121 | 121 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; | |
122 | WorkItem workItem = new WorkItem( | 122 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; |
123 | workItemsGroup, | 123 | workItemInfo.WorkItemPriority = workItemPriority; |
124 | workItemInfo, | 124 | |
125 | callback, | 125 | WorkItem workItem = new WorkItem( |
126 | state); | 126 | workItemsGroup, |
127 | 127 | workItemInfo, | |
128 | return workItem; | 128 | callback, |
129 | } | 129 | state); |
130 | 130 | ||
131 | /// <summary> | 131 | return workItem; |
132 | /// Create a new work item | 132 | } |
133 | /// </summary> | 133 | |
134 | /// <param name="wigStartInfo">Work item group start information</param> | 134 | /// <summary> |
135 | /// <param name="workItemInfo">Work item information</param> | 135 | /// Create a new work item |
136 | /// <param name="callback">A callback to execute</param> | 136 | /// </summary> |
137 | /// <param name="state"> | 137 | /// <param name="workItemsGroup">The work items group</param> |
138 | /// The context object of the work item. Used for passing arguments to the work item. | 138 | /// <param name="wigStartInfo">Work item group start information</param> |
139 | /// </param> | 139 | /// <param name="workItemInfo">Work item information</param> |
140 | /// <returns>Returns a work item</returns> | 140 | /// <param name="callback">A callback to execute</param> |
141 | public static WorkItem CreateWorkItem( | 141 | /// <param name="state"> |
142 | IWorkItemsGroup workItemsGroup, | 142 | /// The context object of the work item. Used for passing arguments to the work item. |
143 | WIGStartInfo wigStartInfo, | 143 | /// </param> |
144 | WorkItemInfo workItemInfo, | 144 | /// <returns>Returns a work item</returns> |
145 | WorkItemCallback callback, | 145 | public static WorkItem CreateWorkItem( |
146 | object state) | 146 | IWorkItemsGroup workItemsGroup, |
147 | { | 147 | WIGStartInfo wigStartInfo, |
148 | ValidateCallback(callback); | 148 | WorkItemInfo workItemInfo, |
149 | ValidateCallback(workItemInfo.PostExecuteWorkItemCallback); | 149 | WorkItemCallback callback, |
150 | 150 | object state) | |
151 | WorkItem workItem = new WorkItem( | 151 | { |
152 | workItemsGroup, | 152 | ValidateCallback(callback); |
153 | new WorkItemInfo(workItemInfo), | 153 | ValidateCallback(workItemInfo.PostExecuteWorkItemCallback); |
154 | callback, | 154 | |
155 | state); | 155 | WorkItem workItem = new WorkItem( |
156 | 156 | workItemsGroup, | |
157 | return workItem; | 157 | new WorkItemInfo(workItemInfo), |
158 | } | 158 | callback, |
159 | 159 | state); | |
160 | /// <summary> | 160 | |
161 | /// Create a new work item | 161 | return workItem; |
162 | /// </summary> | 162 | } |
163 | /// <param name="wigStartInfo">Work item group start information</param> | 163 | |
164 | /// <param name="callback">A callback to execute</param> | 164 | /// <summary> |
165 | /// <param name="state"> | 165 | /// Create a new work item |
166 | /// The context object of the work item. Used for passing arguments to the work item. | 166 | /// </summary> |
167 | /// </param> | 167 | /// <param name="workItemsGroup">The work items group</param> |
168 | /// <param name="postExecuteWorkItemCallback"> | 168 | /// <param name="wigStartInfo">Work item group start information</param> |
169 | /// A delegate to call after the callback completion | 169 | /// <param name="callback">A callback to execute</param> |
170 | /// </param> | 170 | /// <param name="state"> |
171 | /// <returns>Returns a work item</returns> | 171 | /// The context object of the work item. Used for passing arguments to the work item. |
172 | public static WorkItem CreateWorkItem( | 172 | /// </param> |
173 | IWorkItemsGroup workItemsGroup, | 173 | /// <param name="postExecuteWorkItemCallback"> |
174 | WIGStartInfo wigStartInfo, | 174 | /// A delegate to call after the callback completion |
175 | WorkItemCallback callback, | 175 | /// </param> |
176 | object state, | 176 | /// <returns>Returns a work item</returns> |
177 | PostExecuteWorkItemCallback postExecuteWorkItemCallback) | 177 | public static WorkItem CreateWorkItem( |
178 | { | 178 | IWorkItemsGroup workItemsGroup, |
179 | ValidateCallback(callback); | 179 | WIGStartInfo wigStartInfo, |
180 | ValidateCallback(postExecuteWorkItemCallback); | 180 | WorkItemCallback callback, |
181 | 181 | object state, | |
182 | WorkItemInfo workItemInfo = new WorkItemInfo(); | 182 | PostExecuteWorkItemCallback postExecuteWorkItemCallback) |
183 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; | 183 | { |
184 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; | 184 | ValidateCallback(callback); |
185 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; | 185 | ValidateCallback(postExecuteWorkItemCallback); |
186 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; | 186 | |
187 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | 187 | WorkItemInfo workItemInfo = new WorkItemInfo(); |
188 | 188 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; | |
189 | WorkItem workItem = new WorkItem( | 189 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; |
190 | workItemsGroup, | 190 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; |
191 | workItemInfo, | 191 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; |
192 | callback, | 192 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; |
193 | state); | 193 | workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; |
194 | 194 | ||
195 | return workItem; | 195 | WorkItem workItem = new WorkItem( |
196 | } | 196 | workItemsGroup, |
197 | 197 | workItemInfo, | |
198 | /// <summary> | 198 | callback, |
199 | /// Create a new work item | 199 | state); |
200 | /// </summary> | 200 | |
201 | /// <param name="wigStartInfo">Work item group start information</param> | 201 | return workItem; |
202 | /// <param name="callback">A callback to execute</param> | 202 | } |
203 | /// <param name="state"> | 203 | |
204 | /// The context object of the work item. Used for passing arguments to the work item. | 204 | /// <summary> |
205 | /// </param> | 205 | /// Create a new work item |
206 | /// <param name="postExecuteWorkItemCallback"> | 206 | /// </summary> |
207 | /// A delegate to call after the callback completion | 207 | /// <param name="workItemsGroup">The work items group</param> |
208 | /// </param> | 208 | /// <param name="wigStartInfo">Work item group start information</param> |
209 | /// <param name="workItemPriority">The work item priority</param> | 209 | /// <param name="callback">A callback to execute</param> |
210 | /// <returns>Returns a work item</returns> | 210 | /// <param name="state"> |
211 | public static WorkItem CreateWorkItem( | 211 | /// The context object of the work item. Used for passing arguments to the work item. |
212 | IWorkItemsGroup workItemsGroup, | 212 | /// </param> |
213 | WIGStartInfo wigStartInfo, | 213 | /// <param name="postExecuteWorkItemCallback"> |
214 | WorkItemCallback callback, | 214 | /// A delegate to call after the callback completion |
215 | object state, | 215 | /// </param> |
216 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 216 | /// <param name="workItemPriority">The work item priority</param> |
217 | WorkItemPriority workItemPriority) | 217 | /// <returns>Returns a work item</returns> |
218 | { | 218 | public static WorkItem CreateWorkItem( |
219 | ValidateCallback(callback); | 219 | IWorkItemsGroup workItemsGroup, |
220 | ValidateCallback(postExecuteWorkItemCallback); | 220 | WIGStartInfo wigStartInfo, |
221 | 221 | WorkItemCallback callback, | |
222 | WorkItemInfo workItemInfo = new WorkItemInfo(); | 222 | object state, |
223 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; | 223 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, |
224 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; | 224 | WorkItemPriority workItemPriority) |
225 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; | 225 | { |
226 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; | 226 | ValidateCallback(callback); |
227 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | 227 | ValidateCallback(postExecuteWorkItemCallback); |
228 | workItemInfo.WorkItemPriority = workItemPriority; | 228 | |
229 | 229 | WorkItemInfo workItemInfo = new WorkItemInfo(); | |
230 | WorkItem workItem = new WorkItem( | 230 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; |
231 | workItemsGroup, | 231 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; |
232 | workItemInfo, | 232 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; |
233 | callback, | 233 | workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; |
234 | state); | 234 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; |
235 | 235 | workItemInfo.WorkItemPriority = workItemPriority; | |
236 | return workItem; | 236 | |
237 | } | 237 | WorkItem workItem = new WorkItem( |
238 | 238 | workItemsGroup, | |
239 | /// <summary> | 239 | workItemInfo, |
240 | /// Create a new work item | 240 | callback, |
241 | /// </summary> | 241 | state); |
242 | /// <param name="wigStartInfo">Work item group start information</param> | 242 | |
243 | /// <param name="callback">A callback to execute</param> | 243 | return workItem; |
244 | /// <param name="state"> | 244 | } |
245 | /// The context object of the work item. Used for passing arguments to the work item. | 245 | |
246 | /// </param> | 246 | /// <summary> |
247 | /// <param name="postExecuteWorkItemCallback"> | 247 | /// Create a new work item |
248 | /// A delegate to call after the callback completion | 248 | /// </summary> |
249 | /// </param> | 249 | /// <param name="workItemsGroup">The work items group</param> |
250 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 250 | /// <param name="wigStartInfo">Work item group start information</param> |
251 | /// <returns>Returns a work item</returns> | 251 | /// <param name="callback">A callback to execute</param> |
252 | public static WorkItem CreateWorkItem( | 252 | /// <param name="state"> |
253 | IWorkItemsGroup workItemsGroup, | 253 | /// The context object of the work item. Used for passing arguments to the work item. |
254 | WIGStartInfo wigStartInfo, | 254 | /// </param> |
255 | WorkItemCallback callback, | 255 | /// <param name="postExecuteWorkItemCallback"> |
256 | object state, | 256 | /// A delegate to call after the callback completion |
257 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 257 | /// </param> |
258 | CallToPostExecute callToPostExecute) | 258 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> |
259 | { | 259 | /// <returns>Returns a work item</returns> |
260 | ValidateCallback(callback); | 260 | public static WorkItem CreateWorkItem( |
261 | ValidateCallback(postExecuteWorkItemCallback); | 261 | IWorkItemsGroup workItemsGroup, |
262 | 262 | WIGStartInfo wigStartInfo, | |
263 | WorkItemInfo workItemInfo = new WorkItemInfo(); | 263 | WorkItemCallback callback, |
264 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; | 264 | object state, |
265 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; | 265 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, |
266 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; | 266 | CallToPostExecute callToPostExecute) |
267 | workItemInfo.CallToPostExecute = callToPostExecute; | 267 | { |
268 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | 268 | ValidateCallback(callback); |
269 | 269 | ValidateCallback(postExecuteWorkItemCallback); | |
270 | WorkItem workItem = new WorkItem( | 270 | |
271 | workItemsGroup, | 271 | WorkItemInfo workItemInfo = new WorkItemInfo(); |
272 | workItemInfo, | 272 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; |
273 | callback, | 273 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; |
274 | state); | 274 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; |
275 | 275 | workItemInfo.CallToPostExecute = callToPostExecute; | |
276 | return workItem; | 276 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; |
277 | } | 277 | workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; |
278 | 278 | ||
279 | /// <summary> | 279 | WorkItem workItem = new WorkItem( |
280 | /// Create a new work item | 280 | workItemsGroup, |
281 | /// </summary> | 281 | workItemInfo, |
282 | /// <param name="wigStartInfo">Work item group start information</param> | 282 | callback, |
283 | /// <param name="callback">A callback to execute</param> | 283 | state); |
284 | /// <param name="state"> | 284 | |
285 | /// The context object of the work item. Used for passing arguments to the work item. | 285 | return workItem; |
286 | /// </param> | 286 | } |
287 | /// <param name="postExecuteWorkItemCallback"> | 287 | |
288 | /// A delegate to call after the callback completion | 288 | /// <summary> |
289 | /// </param> | 289 | /// Create a new work item |
290 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 290 | /// </summary> |
291 | /// <param name="workItemPriority">The work item priority</param> | 291 | /// <param name="workItemsGroup">The work items group</param> |
292 | /// <returns>Returns a work item</returns> | 292 | /// <param name="wigStartInfo">Work item group start information</param> |
293 | public static WorkItem CreateWorkItem( | 293 | /// <param name="callback">A callback to execute</param> |
294 | IWorkItemsGroup workItemsGroup, | 294 | /// <param name="state"> |
295 | WIGStartInfo wigStartInfo, | 295 | /// The context object of the work item. Used for passing arguments to the work item. |
296 | WorkItemCallback callback, | 296 | /// </param> |
297 | object state, | 297 | /// <param name="postExecuteWorkItemCallback"> |
298 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 298 | /// A delegate to call after the callback completion |
299 | CallToPostExecute callToPostExecute, | 299 | /// </param> |
300 | WorkItemPriority workItemPriority) | 300 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> |
301 | { | 301 | /// <param name="workItemPriority">The work item priority</param> |
302 | 302 | /// <returns>Returns a work item</returns> | |
303 | ValidateCallback(callback); | 303 | public static WorkItem CreateWorkItem( |
304 | ValidateCallback(postExecuteWorkItemCallback); | 304 | IWorkItemsGroup workItemsGroup, |
305 | 305 | WIGStartInfo wigStartInfo, | |
306 | WorkItemInfo workItemInfo = new WorkItemInfo(); | 306 | WorkItemCallback callback, |
307 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; | 307 | object state, |
308 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; | 308 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, |
309 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; | 309 | CallToPostExecute callToPostExecute, |
310 | workItemInfo.CallToPostExecute = callToPostExecute; | 310 | WorkItemPriority workItemPriority) |
311 | workItemInfo.WorkItemPriority = workItemPriority; | 311 | { |
312 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | 312 | |
313 | 313 | ValidateCallback(callback); | |
314 | WorkItem workItem = new WorkItem( | 314 | ValidateCallback(postExecuteWorkItemCallback); |
315 | workItemsGroup, | 315 | |
316 | workItemInfo, | 316 | WorkItemInfo workItemInfo = new WorkItemInfo(); |
317 | callback, | 317 | workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; |
318 | state); | 318 | workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; |
319 | 319 | workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; | |
320 | return workItem; | 320 | workItemInfo.CallToPostExecute = callToPostExecute; |
321 | } | 321 | workItemInfo.WorkItemPriority = workItemPriority; |
322 | 322 | workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; | |
323 | private static void ValidateCallback(Delegate callback) | 323 | |
324 | { | 324 | WorkItem workItem = new WorkItem( |
325 | if(callback.GetInvocationList().Length > 1) | 325 | workItemsGroup, |
326 | { | 326 | workItemInfo, |
327 | throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); | 327 | callback, |
328 | } | 328 | state); |
329 | } | 329 | |
330 | } | 330 | return workItem; |
331 | 331 | } | |
332 | #endregion | 332 | |
333 | } | 333 | private static void ValidateCallback(Delegate callback) |
334 | { | ||
335 | if (callback != null && callback.GetInvocationList().Length > 1) | ||
336 | { | ||
337 | throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | |||
342 | #endregion | ||
343 | } | ||
diff --git a/ThirdParty/SmartThreadPool/WorkItemInfo.cs b/ThirdParty/SmartThreadPool/WorkItemInfo.cs index c259339..5fbceb8 100644 --- a/ThirdParty/SmartThreadPool/WorkItemInfo.cs +++ b/ThirdParty/SmartThreadPool/WorkItemInfo.cs | |||
@@ -1,102 +1,69 @@ | |||
1 | // Ami Bar | 1 | namespace Amib.Threading |
2 | // amibar@gmail.com | 2 | { |
3 | 3 | #region WorkItemInfo class | |
4 | namespace Amib.Threading | 4 | |
5 | { | 5 | /// <summary> |
6 | #region WorkItemInfo class | 6 | /// Summary description for WorkItemInfo. |
7 | 7 | /// </summary> | |
8 | /// <summary> | 8 | public class WorkItemInfo |
9 | /// Summary description for WorkItemInfo. | 9 | { |
10 | /// </summary> | 10 | public WorkItemInfo() |
11 | public class WorkItemInfo | 11 | { |
12 | { | 12 | UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; |
13 | /// <summary> | 13 | UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; |
14 | /// Use the caller's security context | 14 | DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; |
15 | /// </summary> | 15 | CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute; |
16 | private bool _useCallerCallContext; | 16 | PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; |
17 | 17 | WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority; | |
18 | /// <summary> | 18 | } |
19 | /// Use the caller's security context | 19 | |
20 | /// </summary> | 20 | public WorkItemInfo(WorkItemInfo workItemInfo) |
21 | private bool _useCallerHttpContext; | 21 | { |
22 | 22 | UseCallerCallContext = workItemInfo.UseCallerCallContext; | |
23 | /// <summary> | 23 | UseCallerHttpContext = workItemInfo.UseCallerHttpContext; |
24 | /// Dispose of the state object of a work item | 24 | DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects; |
25 | /// </summary> | 25 | CallToPostExecute = workItemInfo.CallToPostExecute; |
26 | private bool _disposeOfStateObjects; | 26 | PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback; |
27 | 27 | WorkItemPriority = workItemInfo.WorkItemPriority; | |
28 | /// <summary> | 28 | Timeout = workItemInfo.Timeout; |
29 | /// The option to run the post execute | 29 | } |
30 | /// </summary> | 30 | |
31 | private CallToPostExecute _callToPostExecute; | 31 | /// <summary> |
32 | 32 | /// Get/Set if to use the caller's security context | |
33 | /// <summary> | 33 | /// </summary> |
34 | /// A post execute callback to call when none is provided in | 34 | public bool UseCallerCallContext { get; set; } |
35 | /// the QueueWorkItem method. | 35 | |
36 | /// </summary> | 36 | /// <summary> |
37 | private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; | 37 | /// Get/Set if to use the caller's HTTP context |
38 | 38 | /// </summary> | |
39 | /// <summary> | 39 | public bool UseCallerHttpContext { get; set; } |
40 | /// The priority of the work item | 40 | |
41 | /// </summary> | 41 | /// <summary> |
42 | private WorkItemPriority _workItemPriority; | 42 | /// Get/Set if to dispose of the state object of a work item |
43 | 43 | /// </summary> | |
44 | public WorkItemInfo() | 44 | public bool DisposeOfStateObjects { get; set; } |
45 | { | 45 | |
46 | _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; | 46 | /// <summary> |
47 | _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; | 47 | /// Get/Set the run the post execute options |
48 | _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; | 48 | /// </summary> |
49 | _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; | 49 | public CallToPostExecute CallToPostExecute { get; set; } |
50 | _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; | 50 | |
51 | _workItemPriority = SmartThreadPool.DefaultWorkItemPriority; | 51 | /// <summary> |
52 | } | 52 | /// Get/Set the post execute callback |
53 | 53 | /// </summary> | |
54 | public WorkItemInfo(WorkItemInfo workItemInfo) | 54 | public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; } |
55 | { | 55 | |
56 | _useCallerCallContext = workItemInfo._useCallerCallContext; | 56 | /// <summary> |
57 | _useCallerHttpContext = workItemInfo._useCallerHttpContext; | 57 | /// Get/Set the work item's priority |
58 | _disposeOfStateObjects = workItemInfo._disposeOfStateObjects; | 58 | /// </summary> |
59 | _callToPostExecute = workItemInfo._callToPostExecute; | 59 | public WorkItemPriority WorkItemPriority { get; set; } |
60 | _postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback; | 60 | |
61 | _workItemPriority = workItemInfo._workItemPriority; | 61 | /// <summary> |
62 | } | 62 | /// Get/Set the work item's timout in milliseconds. |
63 | 63 | /// This is a passive timout. When the timout expires the work item won't be actively aborted! | |
64 | public bool UseCallerCallContext | 64 | /// </summary> |
65 | { | 65 | public long Timeout { get; set; } |
66 | get { return _useCallerCallContext; } | 66 | } |
67 | set { _useCallerCallContext = value; } | 67 | |
68 | } | 68 | #endregion |
69 | 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/WorkItemResultTWrapper.cs b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs new file mode 100644 index 0000000..a0bf8b8 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs | |||
@@ -0,0 +1,128 @@ | |||
1 | using System; | ||
2 | using System.Threading; | ||
3 | |||
4 | namespace Amib.Threading.Internal | ||
5 | { | ||
6 | #region WorkItemResultTWrapper class | ||
7 | |||
8 | internal class WorkItemResultTWrapper<TResult> : IWorkItemResult<TResult>, IInternalWaitableResult | ||
9 | { | ||
10 | private readonly IWorkItemResult _workItemResult; | ||
11 | |||
12 | public WorkItemResultTWrapper(IWorkItemResult workItemResult) | ||
13 | { | ||
14 | _workItemResult = workItemResult; | ||
15 | } | ||
16 | |||
17 | #region IWorkItemResult<TResult> Members | ||
18 | |||
19 | public TResult GetResult() | ||
20 | { | ||
21 | return (TResult)_workItemResult.GetResult(); | ||
22 | } | ||
23 | |||
24 | public TResult GetResult(int millisecondsTimeout, bool exitContext) | ||
25 | { | ||
26 | return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); | ||
27 | } | ||
28 | |||
29 | public TResult GetResult(TimeSpan timeout, bool exitContext) | ||
30 | { | ||
31 | return (TResult)_workItemResult.GetResult(timeout, exitContext); | ||
32 | } | ||
33 | |||
34 | public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) | ||
35 | { | ||
36 | return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); | ||
37 | } | ||
38 | |||
39 | public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) | ||
40 | { | ||
41 | return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); | ||
42 | } | ||
43 | |||
44 | public TResult GetResult(out Exception e) | ||
45 | { | ||
46 | return (TResult)_workItemResult.GetResult(out e); | ||
47 | } | ||
48 | |||
49 | public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) | ||
50 | { | ||
51 | return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); | ||
52 | } | ||
53 | |||
54 | public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) | ||
55 | { | ||
56 | return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); | ||
57 | } | ||
58 | |||
59 | public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) | ||
60 | { | ||
61 | return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); | ||
62 | } | ||
63 | |||
64 | public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) | ||
65 | { | ||
66 | return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); | ||
67 | } | ||
68 | |||
69 | public bool IsCompleted | ||
70 | { | ||
71 | get { return _workItemResult.IsCompleted; } | ||
72 | } | ||
73 | |||
74 | public bool IsCanceled | ||
75 | { | ||
76 | get { return _workItemResult.IsCanceled; } | ||
77 | } | ||
78 | |||
79 | public object State | ||
80 | { | ||
81 | get { return _workItemResult.State; } | ||
82 | } | ||
83 | |||
84 | public bool Cancel() | ||
85 | { | ||
86 | return _workItemResult.Cancel(); | ||
87 | } | ||
88 | |||
89 | public bool Cancel(bool abortExecution) | ||
90 | { | ||
91 | return _workItemResult.Cancel(abortExecution); | ||
92 | } | ||
93 | |||
94 | public WorkItemPriority WorkItemPriority | ||
95 | { | ||
96 | get { return _workItemResult.WorkItemPriority; } | ||
97 | } | ||
98 | |||
99 | public TResult Result | ||
100 | { | ||
101 | get { return (TResult)_workItemResult.Result; } | ||
102 | } | ||
103 | |||
104 | public object Exception | ||
105 | { | ||
106 | get { return (TResult)_workItemResult.Exception; } | ||
107 | } | ||
108 | |||
109 | #region IInternalWorkItemResult Members | ||
110 | |||
111 | public IWorkItemResult GetWorkItemResult() | ||
112 | { | ||
113 | return _workItemResult.GetWorkItemResult(); | ||
114 | } | ||
115 | |||
116 | public IWorkItemResult<TRes> GetWorkItemResultT<TRes>() | ||
117 | { | ||
118 | return (IWorkItemResult<TRes>)this; | ||
119 | } | ||
120 | |||
121 | #endregion | ||
122 | |||
123 | #endregion | ||
124 | } | ||
125 | |||
126 | #endregion | ||
127 | |||
128 | } | ||
diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs index 01ac8dd..67dcbdd 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs | |||
@@ -1,512 +1,361 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | using System.Threading; |
3 | 3 | using System.Runtime.CompilerServices; | |
4 | using System; | 4 | using System.Diagnostics; |
5 | using System.Threading; | 5 | |
6 | using System.Runtime.CompilerServices; | 6 | namespace Amib.Threading.Internal |
7 | using System.Diagnostics; | 7 | { |
8 | 8 | ||
9 | namespace Amib.Threading.Internal | 9 | #region WorkItemsGroup class |
10 | { | 10 | |
11 | #region WorkItemsGroup class | 11 | /// <summary> |
12 | 12 | /// Summary description for WorkItemsGroup. | |
13 | /// <summary> | 13 | /// </summary> |
14 | /// Summary description for WorkItemsGroup. | 14 | public class WorkItemsGroup : WorkItemsGroupBase |
15 | /// </summary> | 15 | { |
16 | public class WorkItemsGroup : IWorkItemsGroup | 16 | #region Private members |
17 | { | 17 | |
18 | #region Private members | 18 | private readonly object _lock = new object(); |
19 | 19 | ||
20 | private object _lock = new object(); | 20 | /// <summary> |
21 | /// <summary> | 21 | /// A reference to the SmartThreadPool instance that created this |
22 | /// Contains the name of this instance of SmartThreadPool. | 22 | /// WorkItemsGroup. |
23 | /// Can be changed by the user. | 23 | /// </summary> |
24 | /// </summary> | 24 | private readonly SmartThreadPool _stp; |
25 | private string _name = "WorkItemsGroup"; | 25 | |
26 | 26 | /// <summary> | |
27 | /// <summary> | 27 | /// The OnIdle event |
28 | /// A reference to the SmartThreadPool instance that created this | 28 | /// </summary> |
29 | /// WorkItemsGroup. | 29 | private event WorkItemsGroupIdleHandler _onIdle; |
30 | /// </summary> | 30 | |
31 | private SmartThreadPool _stp; | 31 | /// <summary> |
32 | 32 | /// A flag to indicate if the Work Items Group is now suspended. | |
33 | /// <summary> | 33 | /// </summary> |
34 | /// The OnIdle event | 34 | private bool _isSuspended; |
35 | /// </summary> | 35 | |
36 | private event WorkItemsGroupIdleHandler _onIdle; | 36 | /// <summary> |
37 | 37 | /// Defines how many work items of this WorkItemsGroup can run at once. | |
38 | /// <summary> | 38 | /// </summary> |
39 | /// Defines how many work items of this WorkItemsGroup can run at once. | 39 | private int _concurrency; |
40 | /// </summary> | 40 | |
41 | private int _concurrency; | 41 | /// <summary> |
42 | 42 | /// Priority queue to hold work items before they are passed | |
43 | /// <summary> | 43 | /// to the SmartThreadPool. |
44 | /// Priority queue to hold work items before they are passed | 44 | /// </summary> |
45 | /// to the SmartThreadPool. | 45 | private readonly PriorityQueue _workItemsQueue; |
46 | /// </summary> | 46 | |
47 | private PriorityQueue _workItemsQueue; | 47 | /// <summary> |
48 | 48 | /// Indicate how many work items are waiting in the SmartThreadPool | |
49 | /// <summary> | 49 | /// queue. |
50 | /// Indicate how many work items are waiting in the SmartThreadPool | 50 | /// This value is used to apply the concurrency. |
51 | /// queue. | 51 | /// </summary> |
52 | /// This value is used to apply the concurrency. | 52 | private int _workItemsInStpQueue; |
53 | /// </summary> | 53 | |
54 | private int _workItemsInStpQueue; | 54 | /// <summary> |
55 | 55 | /// Indicate how many work items are currently running in the SmartThreadPool. | |
56 | /// <summary> | 56 | /// This value is used with the Cancel, to calculate if we can send new |
57 | /// Indicate how many work items are currently running in the SmartThreadPool. | 57 | /// work items to the STP. |
58 | /// This value is used with the Cancel, to calculate if we can send new | 58 | /// </summary> |
59 | /// work items to the STP. | 59 | private int _workItemsExecutingInStp = 0; |
60 | /// </summary> | 60 | |
61 | private int _workItemsExecutingInStp = 0; | 61 | /// <summary> |
62 | 62 | /// WorkItemsGroup start information | |
63 | /// <summary> | 63 | /// </summary> |
64 | /// WorkItemsGroup start information | 64 | private readonly WIGStartInfo _workItemsGroupStartInfo; |
65 | /// </summary> | 65 | |
66 | private WIGStartInfo _workItemsGroupStartInfo; | 66 | /// <summary> |
67 | 67 | /// Signaled when all of the WorkItemsGroup's work item completed. | |
68 | /// <summary> | 68 | /// </summary> |
69 | /// Signaled when all of the WorkItemsGroup's work item completed. | 69 | //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); |
70 | /// </summary> | 70 | private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); |
71 | private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); | 71 | |
72 | 72 | /// <summary> | |
73 | /// <summary> | 73 | /// A common object for all the work items that this work items group |
74 | /// A common object for all the work items that this work items group | 74 | /// generate so we can mark them to cancel in O(1) |
75 | /// generate so we can mark them to cancel in O(1) | 75 | /// </summary> |
76 | /// </summary> | 76 | private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); |
77 | private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); | 77 | |
78 | 78 | #endregion | |
79 | #endregion | 79 | |
80 | 80 | #region Construction | |
81 | #region Construction | 81 | |
82 | 82 | public WorkItemsGroup( | |
83 | public WorkItemsGroup( | 83 | SmartThreadPool stp, |
84 | SmartThreadPool stp, | 84 | int concurrency, |
85 | int concurrency, | 85 | WIGStartInfo wigStartInfo) |
86 | WIGStartInfo wigStartInfo) | 86 | { |
87 | { | 87 | if (concurrency <= 0) |
88 | if (concurrency <= 0) | 88 | { |
89 | { | 89 | throw new ArgumentOutOfRangeException( |
90 | throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero"); | 90 | "concurrency", |
91 | } | 91 | #if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) |
92 | _stp = stp; | 92 | concurrency, |
93 | _concurrency = concurrency; | 93 | #endif |
94 | _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo); | 94 | "concurrency must be greater than zero"); |
95 | _workItemsQueue = new PriorityQueue(); | 95 | } |
96 | 96 | _stp = stp; | |
97 | // The _workItemsInStpQueue gets the number of currently executing work items, | 97 | _concurrency = concurrency; |
98 | // because once a work item is executing, it cannot be cancelled. | 98 | _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly(); |
99 | _workItemsInStpQueue = _workItemsExecutingInStp; | 99 | _workItemsQueue = new PriorityQueue(); |
100 | } | 100 | Name = "WorkItemsGroup"; |
101 | 101 | ||
102 | #endregion | 102 | // The _workItemsInStpQueue gets the number of currently executing work items, |
103 | 103 | // because once a work item is executing, it cannot be cancelled. | |
104 | #region IWorkItemsGroup implementation | 104 | _workItemsInStpQueue = _workItemsExecutingInStp; |
105 | 105 | ||
106 | /// <summary> | 106 | _isSuspended = _workItemsGroupStartInfo.StartSuspended; |
107 | /// Get/Set the name of the SmartThreadPool instance | 107 | } |
108 | /// </summary> | 108 | |
109 | public string Name | 109 | #endregion |
110 | { | 110 | |
111 | get | 111 | #region WorkItemsGroupBase Overrides |
112 | { | 112 | |
113 | return _name; | 113 | public override int Concurrency |
114 | } | 114 | { |
115 | 115 | get { return _concurrency; } | |
116 | set | 116 | set |
117 | { | 117 | { |
118 | _name = value; | 118 | Debug.Assert(value > 0); |
119 | } | 119 | |
120 | } | 120 | int diff = value - _concurrency; |
121 | 121 | _concurrency = value; | |
122 | /// <summary> | 122 | if (diff > 0) |
123 | /// Queue a work item | 123 | { |
124 | /// </summary> | 124 | EnqueueToSTPNextNWorkItem(diff); |
125 | /// <param name="callback">A callback to execute</param> | 125 | } |
126 | /// <returns>Returns a work item result</returns> | 126 | } |
127 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback) | 127 | } |
128 | { | 128 | |
129 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback); | 129 | public override int WaitingCallbacks |
130 | EnqueueToSTPNextWorkItem(workItem); | 130 | { |
131 | return workItem.GetWorkItemResult(); | 131 | get { return _workItemsQueue.Count; } |
132 | } | 132 | } |
133 | 133 | ||
134 | /// <summary> | 134 | public override object[] GetStates() |
135 | /// Queue a work item | 135 | { |
136 | /// </summary> | 136 | lock (_lock) |
137 | /// <param name="callback">A callback to execute</param> | 137 | { |
138 | /// <param name="workItemPriority">The priority of the work item</param> | 138 | object[] states = new object[_workItemsQueue.Count]; |
139 | /// <returns>Returns a work item result</returns> | 139 | int i = 0; |
140 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) | 140 | foreach (WorkItem workItem in _workItemsQueue) |
141 | { | 141 | { |
142 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority); | 142 | states[i] = workItem.GetWorkItemResult().State; |
143 | EnqueueToSTPNextWorkItem(workItem); | 143 | ++i; |
144 | return workItem.GetWorkItemResult(); | 144 | } |
145 | } | 145 | return states; |
146 | 146 | } | |
147 | /// <summary> | 147 | } |
148 | /// Queue a work item | 148 | |
149 | /// </summary> | 149 | /// <summary> |
150 | /// <param name="workItemInfo">Work item info</param> | 150 | /// WorkItemsGroup start information |
151 | /// <param name="callback">A callback to execute</param> | 151 | /// </summary> |
152 | /// <returns>Returns a work item result</returns> | 152 | public override WIGStartInfo WIGStartInfo |
153 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) | 153 | { |
154 | { | 154 | get { return _workItemsGroupStartInfo; } |
155 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback); | 155 | } |
156 | EnqueueToSTPNextWorkItem(workItem); | 156 | |
157 | return workItem.GetWorkItemResult(); | 157 | /// <summary> |
158 | } | 158 | /// Start the Work Items Group if it was started suspended |
159 | 159 | /// </summary> | |
160 | /// <summary> | 160 | public override void Start() |
161 | /// Queue a work item | 161 | { |
162 | /// </summary> | 162 | // If the Work Items Group already started then quit |
163 | /// <param name="callback">A callback to execute</param> | 163 | if (!_isSuspended) |
164 | /// <param name="state"> | 164 | { |
165 | /// The context object of the work item. Used for passing arguments to the work item. | 165 | return; |
166 | /// </param> | 166 | } |
167 | /// <returns>Returns a work item result</returns> | 167 | _isSuspended = false; |
168 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) | 168 | |
169 | { | 169 | EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); |
170 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state); | 170 | } |
171 | EnqueueToSTPNextWorkItem(workItem); | 171 | |
172 | return workItem.GetWorkItemResult(); | 172 | public override void Cancel(bool abortExecution) |
173 | } | 173 | { |
174 | 174 | lock (_lock) | |
175 | /// <summary> | 175 | { |
176 | /// Queue a work item | 176 | _canceledWorkItemsGroup.IsCanceled = true; |
177 | /// </summary> | 177 | _workItemsQueue.Clear(); |
178 | /// <param name="callback">A callback to execute</param> | 178 | _workItemsInStpQueue = 0; |
179 | /// <param name="state"> | 179 | _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); |
180 | /// The context object of the work item. Used for passing arguments to the work item. | 180 | } |
181 | /// </param> | 181 | |
182 | /// <param name="workItemPriority">The work item priority</param> | 182 | if (abortExecution) |
183 | /// <returns>Returns a work item result</returns> | 183 | { |
184 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) | 184 | _stp.CancelAbortWorkItemsGroup(this); |
185 | { | 185 | } |
186 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority); | 186 | } |
187 | EnqueueToSTPNextWorkItem(workItem); | 187 | |
188 | return workItem.GetWorkItemResult(); | 188 | /// <summary> |
189 | } | 189 | /// Wait for the thread pool to be idle |
190 | 190 | /// </summary> | |
191 | /// <summary> | 191 | public override bool WaitForIdle(int millisecondsTimeout) |
192 | /// Queue a work item | 192 | { |
193 | /// </summary> | 193 | SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this); |
194 | /// <param name="workItemInfo">Work item information</param> | 194 | return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); |
195 | /// <param name="callback">A callback to execute</param> | 195 | } |
196 | /// <param name="state"> | 196 | |
197 | /// The context object of the work item. Used for passing arguments to the work item. | 197 | public override event WorkItemsGroupIdleHandler OnIdle |
198 | /// </param> | 198 | { |
199 | /// <returns>Returns a work item result</returns> | 199 | add { _onIdle += value; } |
200 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) | 200 | remove { _onIdle -= value; } |
201 | { | 201 | } |
202 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state); | 202 | |
203 | EnqueueToSTPNextWorkItem(workItem); | 203 | #endregion |
204 | return workItem.GetWorkItemResult(); | 204 | |
205 | } | 205 | #region Private methods |
206 | 206 | ||
207 | /// <summary> | 207 | private void RegisterToWorkItemCompletion(IWorkItemResult wir) |
208 | /// Queue a work item | 208 | { |
209 | /// </summary> | 209 | IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir; |
210 | /// <param name="callback">A callback to execute</param> | 210 | iwir.OnWorkItemStarted += OnWorkItemStartedCallback; |
211 | /// <param name="state"> | 211 | iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback; |
212 | /// The context object of the work item. Used for passing arguments to the work item. | 212 | } |
213 | /// </param> | 213 | |
214 | /// <param name="postExecuteWorkItemCallback"> | 214 | public void OnSTPIsStarting() |
215 | /// A delegate to call after the callback completion | 215 | { |
216 | /// </param> | 216 | if (_isSuspended) |
217 | /// <returns>Returns a work item result</returns> | 217 | { |
218 | public IWorkItemResult QueueWorkItem( | 218 | return; |
219 | WorkItemCallback callback, | 219 | } |
220 | object state, | 220 | |
221 | PostExecuteWorkItemCallback postExecuteWorkItemCallback) | 221 | EnqueueToSTPNextNWorkItem(_concurrency); |
222 | { | 222 | } |
223 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback); | 223 | |
224 | EnqueueToSTPNextWorkItem(workItem); | 224 | public void EnqueueToSTPNextNWorkItem(int count) |
225 | return workItem.GetWorkItemResult(); | 225 | { |
226 | } | 226 | for (int i = 0; i < count; ++i) |
227 | 227 | { | |
228 | /// <summary> | 228 | EnqueueToSTPNextWorkItem(null, false); |
229 | /// Queue a work item | 229 | } |
230 | /// </summary> | 230 | } |
231 | /// <param name="callback">A callback to execute</param> | 231 | |
232 | /// <param name="state"> | 232 | private object FireOnIdle(object state) |
233 | /// The context object of the work item. Used for passing arguments to the work item. | 233 | { |
234 | /// </param> | 234 | FireOnIdleImpl(_onIdle); |
235 | /// <param name="postExecuteWorkItemCallback"> | 235 | return null; |
236 | /// A delegate to call after the callback completion | 236 | } |
237 | /// </param> | 237 | |
238 | /// <param name="workItemPriority">The work item priority</param> | 238 | [MethodImpl(MethodImplOptions.NoInlining)] |
239 | /// <returns>Returns a work item result</returns> | 239 | private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle) |
240 | public IWorkItemResult QueueWorkItem( | 240 | { |
241 | WorkItemCallback callback, | 241 | if(null == onIdle) |
242 | object state, | 242 | { |
243 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 243 | return; |
244 | WorkItemPriority workItemPriority) | 244 | } |
245 | { | 245 | |
246 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); | 246 | Delegate[] delegates = onIdle.GetInvocationList(); |
247 | EnqueueToSTPNextWorkItem(workItem); | 247 | foreach(WorkItemsGroupIdleHandler eh in delegates) |
248 | return workItem.GetWorkItemResult(); | 248 | { |
249 | } | 249 | try |
250 | 250 | { | |
251 | /// <summary> | 251 | eh(this); |
252 | /// Queue a work item | 252 | } |
253 | /// </summary> | 253 | catch { } // Suppress exceptions |
254 | /// <param name="callback">A callback to execute</param> | 254 | } |
255 | /// <param name="state"> | 255 | } |
256 | /// The context object of the work item. Used for passing arguments to the work item. | 256 | |
257 | /// </param> | 257 | private void OnWorkItemStartedCallback(WorkItem workItem) |
258 | /// <param name="postExecuteWorkItemCallback"> | 258 | { |
259 | /// A delegate to call after the callback completion | 259 | lock(_lock) |
260 | /// </param> | 260 | { |
261 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 261 | ++_workItemsExecutingInStp; |
262 | /// <returns>Returns a work item result</returns> | 262 | } |
263 | public IWorkItemResult QueueWorkItem( | 263 | } |
264 | WorkItemCallback callback, | 264 | |
265 | object state, | 265 | private void OnWorkItemCompletedCallback(WorkItem workItem) |
266 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 266 | { |
267 | CallToPostExecute callToPostExecute) | 267 | EnqueueToSTPNextWorkItem(null, true); |
268 | { | 268 | } |
269 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); | 269 | |
270 | EnqueueToSTPNextWorkItem(workItem); | 270 | internal override void Enqueue(WorkItem workItem) |
271 | return workItem.GetWorkItemResult(); | 271 | { |
272 | } | 272 | EnqueueToSTPNextWorkItem(workItem); |
273 | 273 | } | |
274 | /// <summary> | 274 | |
275 | /// Queue a work item | 275 | private void EnqueueToSTPNextWorkItem(WorkItem workItem) |
276 | /// </summary> | 276 | { |
277 | /// <param name="callback">A callback to execute</param> | 277 | EnqueueToSTPNextWorkItem(workItem, false); |
278 | /// <param name="state"> | 278 | } |
279 | /// The context object of the work item. Used for passing arguments to the work item. | 279 | |
280 | /// </param> | 280 | private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue) |
281 | /// <param name="postExecuteWorkItemCallback"> | 281 | { |
282 | /// A delegate to call after the callback completion | 282 | lock(_lock) |
283 | /// </param> | 283 | { |
284 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | 284 | // Got here from OnWorkItemCompletedCallback() |
285 | /// <param name="workItemPriority">The work item priority</param> | 285 | if (decrementWorkItemsInStpQueue) |
286 | /// <returns>Returns a work item result</returns> | 286 | { |
287 | public IWorkItemResult QueueWorkItem( | 287 | --_workItemsInStpQueue; |
288 | WorkItemCallback callback, | 288 | |
289 | object state, | 289 | if(_workItemsInStpQueue < 0) |
290 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | 290 | { |
291 | CallToPostExecute callToPostExecute, | 291 | _workItemsInStpQueue = 0; |
292 | WorkItemPriority workItemPriority) | 292 | } |
293 | { | 293 | |
294 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); | 294 | --_workItemsExecutingInStp; |
295 | EnqueueToSTPNextWorkItem(workItem); | 295 | |
296 | return workItem.GetWorkItemResult(); | 296 | if(_workItemsExecutingInStp < 0) |
297 | } | 297 | { |
298 | 298 | _workItemsExecutingInStp = 0; | |
299 | /// <summary> | 299 | } |
300 | /// Wait for the thread pool to be idle | 300 | } |
301 | /// </summary> | 301 | |
302 | public void WaitForIdle() | 302 | // If the work item is not null then enqueue it |
303 | { | 303 | if (null != workItem) |
304 | WaitForIdle(Timeout.Infinite); | 304 | { |
305 | } | 305 | workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup; |
306 | 306 | ||
307 | /// <summary> | 307 | RegisterToWorkItemCompletion(workItem.GetWorkItemResult()); |
308 | /// Wait for the thread pool to be idle | 308 | _workItemsQueue.Enqueue(workItem); |
309 | /// </summary> | 309 | //_stp.IncrementWorkItemsCount(); |
310 | public bool WaitForIdle(TimeSpan timeout) | 310 | |
311 | { | 311 | if ((1 == _workItemsQueue.Count) && |
312 | return WaitForIdle((int)timeout.TotalMilliseconds); | 312 | (0 == _workItemsInStpQueue)) |
313 | } | 313 | { |
314 | 314 | _stp.RegisterWorkItemsGroup(this); | |
315 | /// <summary> | 315 | IsIdle = false; |
316 | /// Wait for the thread pool to be idle | 316 | _isIdleWaitHandle.Reset(); |
317 | /// </summary> | 317 | } |
318 | public bool WaitForIdle(int millisecondsTimeout) | 318 | } |
319 | { | 319 | |
320 | _stp.ValidateWorkItemsGroupWaitForIdle(this); | 320 | // If the work items queue of the group is empty than quit |
321 | return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); | 321 | if (0 == _workItemsQueue.Count) |
322 | } | 322 | { |
323 | 323 | if (0 == _workItemsInStpQueue) | |
324 | public int WaitingCallbacks | 324 | { |
325 | { | 325 | _stp.UnregisterWorkItemsGroup(this); |
326 | get | 326 | IsIdle = true; |
327 | { | 327 | _isIdleWaitHandle.Set(); |
328 | return _workItemsQueue.Count; | 328 | if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0) |
329 | } | 329 | { |
330 | } | 330 | _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle)); |
331 | 331 | } | |
332 | public event WorkItemsGroupIdleHandler OnIdle | 332 | } |
333 | { | 333 | return; |
334 | add | 334 | } |
335 | { | 335 | |
336 | _onIdle += value; | 336 | if (!_isSuspended) |
337 | } | 337 | { |
338 | remove | 338 | if (_workItemsInStpQueue < _concurrency) |
339 | { | 339 | { |
340 | _onIdle -= value; | 340 | WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; |
341 | } | 341 | try |
342 | } | 342 | { |
343 | 343 | _stp.Enqueue(nextWorkItem); | |
344 | public void Cancel() | 344 | } |
345 | { | 345 | catch (ObjectDisposedException e) |
346 | lock(_lock) | 346 | { |
347 | { | 347 | e.GetHashCode(); |
348 | _canceledWorkItemsGroup.IsCanceled = true; | 348 | // The STP has been shutdown |
349 | _workItemsQueue.Clear(); | 349 | } |
350 | _workItemsInStpQueue = 0; | 350 | |
351 | _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); | 351 | ++_workItemsInStpQueue; |
352 | } | 352 | } |
353 | } | 353 | } |
354 | 354 | } | |
355 | public void Start() | 355 | } |
356 | { | 356 | |
357 | lock (this) | 357 | #endregion |
358 | { | 358 | } |
359 | if (!_workItemsGroupStartInfo.StartSuspended) | 359 | |
360 | { | 360 | #endregion |
361 | return; | 361 | } |
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/WorkItemsGroupBase.cs b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs new file mode 100644 index 0000000..429de12 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs | |||
@@ -0,0 +1,471 @@ | |||
1 | using System; | ||
2 | using System.Threading; | ||
3 | |||
4 | namespace Amib.Threading.Internal | ||
5 | { | ||
6 | public abstract class WorkItemsGroupBase : IWorkItemsGroup | ||
7 | { | ||
8 | #region Private Fields | ||
9 | |||
10 | /// <summary> | ||
11 | /// Contains the name of this instance of SmartThreadPool. | ||
12 | /// Can be changed by the user. | ||
13 | /// </summary> | ||
14 | private string _name = "WorkItemsGroupBase"; | ||
15 | |||
16 | public WorkItemsGroupBase() | ||
17 | { | ||
18 | IsIdle = true; | ||
19 | } | ||
20 | |||
21 | #endregion | ||
22 | |||
23 | #region IWorkItemsGroup Members | ||
24 | |||
25 | #region Public Methods | ||
26 | |||
27 | /// <summary> | ||
28 | /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance | ||
29 | /// </summary> | ||
30 | public string Name | ||
31 | { | ||
32 | get { return _name; } | ||
33 | set { _name = value; } | ||
34 | } | ||
35 | |||
36 | #endregion | ||
37 | |||
38 | #region Abstract Methods | ||
39 | |||
40 | public abstract int Concurrency { get; set; } | ||
41 | public abstract int WaitingCallbacks { get; } | ||
42 | public abstract object[] GetStates(); | ||
43 | public abstract WIGStartInfo WIGStartInfo { get; } | ||
44 | public abstract void Start(); | ||
45 | public abstract void Cancel(bool abortExecution); | ||
46 | public abstract bool WaitForIdle(int millisecondsTimeout); | ||
47 | public abstract event WorkItemsGroupIdleHandler OnIdle; | ||
48 | |||
49 | internal abstract void Enqueue(WorkItem workItem); | ||
50 | internal virtual void PreQueueWorkItem() { } | ||
51 | |||
52 | #endregion | ||
53 | |||
54 | #region Common Base Methods | ||
55 | |||
56 | /// <summary> | ||
57 | /// Cancel all the work items. | ||
58 | /// Same as Cancel(false) | ||
59 | /// </summary> | ||
60 | public virtual void Cancel() | ||
61 | { | ||
62 | Cancel(false); | ||
63 | } | ||
64 | |||
65 | /// <summary> | ||
66 | /// Wait for the SmartThreadPool/WorkItemsGroup to be idle | ||
67 | /// </summary> | ||
68 | public void WaitForIdle() | ||
69 | { | ||
70 | WaitForIdle(Timeout.Infinite); | ||
71 | } | ||
72 | |||
73 | /// <summary> | ||
74 | /// Wait for the SmartThreadPool/WorkItemsGroup to be idle | ||
75 | /// </summary> | ||
76 | public bool WaitForIdle(TimeSpan timeout) | ||
77 | { | ||
78 | return WaitForIdle((int)timeout.TotalMilliseconds); | ||
79 | } | ||
80 | |||
81 | /// <summary> | ||
82 | /// IsIdle is true when there are no work items running or queued. | ||
83 | /// </summary> | ||
84 | public bool IsIdle { get; protected set; } | ||
85 | |||
86 | #endregion | ||
87 | |||
88 | #region QueueWorkItem | ||
89 | |||
90 | /// <summary> | ||
91 | /// Queue a work item | ||
92 | /// </summary> | ||
93 | /// <param name="callback">A callback to execute</param> | ||
94 | /// <returns>Returns a work item result</returns> | ||
95 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback) | ||
96 | { | ||
97 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); | ||
98 | Enqueue(workItem); | ||
99 | return workItem.GetWorkItemResult(); | ||
100 | } | ||
101 | |||
102 | /// <summary> | ||
103 | /// Queue a work item | ||
104 | /// </summary> | ||
105 | /// <param name="callback">A callback to execute</param> | ||
106 | /// <param name="workItemPriority">The priority of the work item</param> | ||
107 | /// <returns>Returns a work item result</returns> | ||
108 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) | ||
109 | { | ||
110 | PreQueueWorkItem(); | ||
111 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); | ||
112 | Enqueue(workItem); | ||
113 | return workItem.GetWorkItemResult(); | ||
114 | } | ||
115 | |||
116 | /// <summary> | ||
117 | /// Queue a work item | ||
118 | /// </summary> | ||
119 | /// <param name="workItemInfo">Work item info</param> | ||
120 | /// <param name="callback">A callback to execute</param> | ||
121 | /// <returns>Returns a work item result</returns> | ||
122 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) | ||
123 | { | ||
124 | PreQueueWorkItem(); | ||
125 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); | ||
126 | Enqueue(workItem); | ||
127 | return workItem.GetWorkItemResult(); | ||
128 | } | ||
129 | |||
130 | /// <summary> | ||
131 | /// Queue a work item | ||
132 | /// </summary> | ||
133 | /// <param name="callback">A callback to execute</param> | ||
134 | /// <param name="state"> | ||
135 | /// The context object of the work item. Used for passing arguments to the work item. | ||
136 | /// </param> | ||
137 | /// <returns>Returns a work item result</returns> | ||
138 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) | ||
139 | { | ||
140 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); | ||
141 | Enqueue(workItem); | ||
142 | return workItem.GetWorkItemResult(); | ||
143 | } | ||
144 | |||
145 | /// <summary> | ||
146 | /// Queue a work item | ||
147 | /// </summary> | ||
148 | /// <param name="callback">A callback to execute</param> | ||
149 | /// <param name="state"> | ||
150 | /// The context object of the work item. Used for passing arguments to the work item. | ||
151 | /// </param> | ||
152 | /// <param name="workItemPriority">The work item priority</param> | ||
153 | /// <returns>Returns a work item result</returns> | ||
154 | public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) | ||
155 | { | ||
156 | PreQueueWorkItem(); | ||
157 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); | ||
158 | Enqueue(workItem); | ||
159 | return workItem.GetWorkItemResult(); | ||
160 | } | ||
161 | |||
162 | /// <summary> | ||
163 | /// Queue a work item | ||
164 | /// </summary> | ||
165 | /// <param name="workItemInfo">Work item information</param> | ||
166 | /// <param name="callback">A callback to execute</param> | ||
167 | /// <param name="state"> | ||
168 | /// The context object of the work item. Used for passing arguments to the work item. | ||
169 | /// </param> | ||
170 | /// <returns>Returns a work item result</returns> | ||
171 | public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) | ||
172 | { | ||
173 | PreQueueWorkItem(); | ||
174 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); | ||
175 | Enqueue(workItem); | ||
176 | return workItem.GetWorkItemResult(); | ||
177 | } | ||
178 | |||
179 | /// <summary> | ||
180 | /// Queue a work item | ||
181 | /// </summary> | ||
182 | /// <param name="callback">A callback to execute</param> | ||
183 | /// <param name="state"> | ||
184 | /// The context object of the work item. Used for passing arguments to the work item. | ||
185 | /// </param> | ||
186 | /// <param name="postExecuteWorkItemCallback"> | ||
187 | /// A delegate to call after the callback completion | ||
188 | /// </param> | ||
189 | /// <returns>Returns a work item result</returns> | ||
190 | public IWorkItemResult QueueWorkItem( | ||
191 | WorkItemCallback callback, | ||
192 | object state, | ||
193 | PostExecuteWorkItemCallback postExecuteWorkItemCallback) | ||
194 | { | ||
195 | PreQueueWorkItem(); | ||
196 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); | ||
197 | Enqueue(workItem); | ||
198 | return workItem.GetWorkItemResult(); | ||
199 | } | ||
200 | |||
201 | /// <summary> | ||
202 | /// Queue a work item | ||
203 | /// </summary> | ||
204 | /// <param name="callback">A callback to execute</param> | ||
205 | /// <param name="state"> | ||
206 | /// The context object of the work item. Used for passing arguments to the work item. | ||
207 | /// </param> | ||
208 | /// <param name="postExecuteWorkItemCallback"> | ||
209 | /// A delegate to call after the callback completion | ||
210 | /// </param> | ||
211 | /// <param name="workItemPriority">The work item priority</param> | ||
212 | /// <returns>Returns a work item result</returns> | ||
213 | public IWorkItemResult QueueWorkItem( | ||
214 | WorkItemCallback callback, | ||
215 | object state, | ||
216 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | ||
217 | WorkItemPriority workItemPriority) | ||
218 | { | ||
219 | PreQueueWorkItem(); | ||
220 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); | ||
221 | Enqueue(workItem); | ||
222 | return workItem.GetWorkItemResult(); | ||
223 | } | ||
224 | |||
225 | /// <summary> | ||
226 | /// Queue a work item | ||
227 | /// </summary> | ||
228 | /// <param name="callback">A callback to execute</param> | ||
229 | /// <param name="state"> | ||
230 | /// The context object of the work item. Used for passing arguments to the work item. | ||
231 | /// </param> | ||
232 | /// <param name="postExecuteWorkItemCallback"> | ||
233 | /// A delegate to call after the callback completion | ||
234 | /// </param> | ||
235 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | ||
236 | /// <returns>Returns a work item result</returns> | ||
237 | public IWorkItemResult QueueWorkItem( | ||
238 | WorkItemCallback callback, | ||
239 | object state, | ||
240 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | ||
241 | CallToPostExecute callToPostExecute) | ||
242 | { | ||
243 | PreQueueWorkItem(); | ||
244 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); | ||
245 | Enqueue(workItem); | ||
246 | return workItem.GetWorkItemResult(); | ||
247 | } | ||
248 | |||
249 | /// <summary> | ||
250 | /// Queue a work item | ||
251 | /// </summary> | ||
252 | /// <param name="callback">A callback to execute</param> | ||
253 | /// <param name="state"> | ||
254 | /// The context object of the work item. Used for passing arguments to the work item. | ||
255 | /// </param> | ||
256 | /// <param name="postExecuteWorkItemCallback"> | ||
257 | /// A delegate to call after the callback completion | ||
258 | /// </param> | ||
259 | /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> | ||
260 | /// <param name="workItemPriority">The work item priority</param> | ||
261 | /// <returns>Returns a work item result</returns> | ||
262 | public IWorkItemResult QueueWorkItem( | ||
263 | WorkItemCallback callback, | ||
264 | object state, | ||
265 | PostExecuteWorkItemCallback postExecuteWorkItemCallback, | ||
266 | CallToPostExecute callToPostExecute, | ||
267 | WorkItemPriority workItemPriority) | ||
268 | { | ||
269 | PreQueueWorkItem(); | ||
270 | WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); | ||
271 | Enqueue(workItem); | ||
272 | return workItem.GetWorkItemResult(); | ||
273 | } | ||
274 | |||
275 | #endregion | ||
276 | |||
277 | #region QueueWorkItem(Action<...>) | ||
278 | |||
279 | public IWorkItemResult QueueWorkItem(Action action) | ||
280 | { | ||
281 | return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority); | ||
282 | } | ||
283 | |||
284 | public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority) | ||
285 | { | ||
286 | PreQueueWorkItem (); | ||
287 | WorkItem workItem = WorkItemFactory.CreateWorkItem ( | ||
288 | this, | ||
289 | WIGStartInfo, | ||
290 | delegate | ||
291 | { | ||
292 | action.Invoke (); | ||
293 | return null; | ||
294 | }, priority); | ||
295 | Enqueue (workItem); | ||
296 | return workItem.GetWorkItemResult (); | ||
297 | } | ||
298 | |||
299 | public IWorkItemResult QueueWorkItem<T>(Action<T> action, T arg) | ||
300 | { | ||
301 | return QueueWorkItem<T> (action, arg, SmartThreadPool.DefaultWorkItemPriority); | ||
302 | } | ||
303 | |||
304 | public IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority) | ||
305 | { | ||
306 | PreQueueWorkItem (); | ||
307 | WorkItem workItem = WorkItemFactory.CreateWorkItem ( | ||
308 | this, | ||
309 | WIGStartInfo, | ||
310 | state => | ||
311 | { | ||
312 | action.Invoke (arg); | ||
313 | return null; | ||
314 | }, | ||
315 | WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority); | ||
316 | Enqueue (workItem); | ||
317 | return workItem.GetWorkItemResult (); | ||
318 | } | ||
319 | |||
320 | public IWorkItemResult QueueWorkItem<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2) | ||
321 | { | ||
322 | return QueueWorkItem<T1, T2> (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority); | ||
323 | } | ||
324 | |||
325 | public IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority) | ||
326 | { | ||
327 | PreQueueWorkItem (); | ||
328 | WorkItem workItem = WorkItemFactory.CreateWorkItem ( | ||
329 | this, | ||
330 | WIGStartInfo, | ||
331 | state => | ||
332 | { | ||
333 | action.Invoke (arg1, arg2); | ||
334 | return null; | ||
335 | }, | ||
336 | WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); | ||
337 | Enqueue (workItem); | ||
338 | return workItem.GetWorkItemResult (); | ||
339 | } | ||
340 | |||
341 | public IWorkItemResult QueueWorkItem<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3) | ||
342 | { | ||
343 | return QueueWorkItem<T1, T2, T3> (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority); | ||
344 | ; | ||
345 | } | ||
346 | |||
347 | public IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority) | ||
348 | { | ||
349 | PreQueueWorkItem (); | ||
350 | WorkItem workItem = WorkItemFactory.CreateWorkItem ( | ||
351 | this, | ||
352 | WIGStartInfo, | ||
353 | state => | ||
354 | { | ||
355 | action.Invoke (arg1, arg2, arg3); | ||
356 | return null; | ||
357 | }, | ||
358 | WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority); | ||
359 | Enqueue (workItem); | ||
360 | return workItem.GetWorkItemResult (); | ||
361 | } | ||
362 | |||
363 | public IWorkItemResult QueueWorkItem<T1, T2, T3, T4>( | ||
364 | Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) | ||
365 | { | ||
366 | return QueueWorkItem<T1, T2, T3, T4> (action, arg1, arg2, arg3, arg4, | ||
367 | SmartThreadPool.DefaultWorkItemPriority); | ||
368 | } | ||
369 | |||
370 | public IWorkItemResult QueueWorkItem<T1, T2, T3, T4> ( | ||
371 | Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority) | ||
372 | { | ||
373 | PreQueueWorkItem (); | ||
374 | WorkItem workItem = WorkItemFactory.CreateWorkItem ( | ||
375 | this, | ||
376 | WIGStartInfo, | ||
377 | state => | ||
378 | { | ||
379 | action.Invoke (arg1, arg2, arg3, arg4); | ||
380 | return null; | ||
381 | }, | ||
382 | WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority); | ||
383 | Enqueue (workItem); | ||
384 | return workItem.GetWorkItemResult (); | ||
385 | } | ||
386 | |||
387 | #endregion | ||
388 | |||
389 | #region QueueWorkItem(Func<...>) | ||
390 | |||
391 | public IWorkItemResult<TResult> QueueWorkItem<TResult>(Func<TResult> func) | ||
392 | { | ||
393 | PreQueueWorkItem(); | ||
394 | WorkItem workItem = WorkItemFactory.CreateWorkItem( | ||
395 | this, | ||
396 | WIGStartInfo, | ||
397 | state => | ||
398 | { | ||
399 | return func.Invoke(); | ||
400 | }); | ||
401 | Enqueue(workItem); | ||
402 | return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult()); | ||
403 | } | ||
404 | |||
405 | public IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> func, T arg) | ||
406 | { | ||
407 | PreQueueWorkItem(); | ||
408 | WorkItem workItem = WorkItemFactory.CreateWorkItem( | ||
409 | this, | ||
410 | WIGStartInfo, | ||
411 | state => | ||
412 | { | ||
413 | return func.Invoke(arg); | ||
414 | }, | ||
415 | WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null); | ||
416 | Enqueue(workItem); | ||
417 | return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult()); | ||
418 | } | ||
419 | |||
420 | public IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 arg1, T2 arg2) | ||
421 | { | ||
422 | PreQueueWorkItem(); | ||
423 | WorkItem workItem = WorkItemFactory.CreateWorkItem( | ||
424 | this, | ||
425 | WIGStartInfo, | ||
426 | state => | ||
427 | { | ||
428 | return func.Invoke(arg1, arg2); | ||
429 | }, | ||
430 | WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null); | ||
431 | Enqueue(workItem); | ||
432 | return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult()); | ||
433 | } | ||
434 | |||
435 | public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>( | ||
436 | Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3) | ||
437 | { | ||
438 | PreQueueWorkItem(); | ||
439 | WorkItem workItem = WorkItemFactory.CreateWorkItem( | ||
440 | this, | ||
441 | WIGStartInfo, | ||
442 | state => | ||
443 | { | ||
444 | return func.Invoke(arg1, arg2, arg3); | ||
445 | }, | ||
446 | WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null); | ||
447 | Enqueue(workItem); | ||
448 | return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult()); | ||
449 | } | ||
450 | |||
451 | public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult>( | ||
452 | Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4) | ||
453 | { | ||
454 | PreQueueWorkItem(); | ||
455 | WorkItem workItem = WorkItemFactory.CreateWorkItem( | ||
456 | this, | ||
457 | WIGStartInfo, | ||
458 | state => | ||
459 | { | ||
460 | return func.Invoke(arg1, arg2, arg3, arg4); | ||
461 | }, | ||
462 | WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null); | ||
463 | Enqueue(workItem); | ||
464 | return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult()); | ||
465 | } | ||
466 | |||
467 | #endregion | ||
468 | |||
469 | #endregion | ||
470 | } | ||
471 | } \ No newline at end of file | ||
diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs index af5af07..156a131 100644 --- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs +++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs | |||
@@ -1,600 +1,645 @@ | |||
1 | // Ami Bar | 1 | using System; |
2 | // amibar@gmail.com | 2 | using System.Collections.Generic; |
3 | 3 | using System.Threading; | |
4 | using System; | 4 | |
5 | using System.Threading; | 5 | namespace Amib.Threading.Internal |
6 | 6 | { | |
7 | namespace Amib.Threading.Internal | 7 | #region WorkItemsQueue class |
8 | { | 8 | |
9 | #region WorkItemsQueue class | 9 | /// <summary> |
10 | 10 | /// WorkItemsQueue class. | |
11 | /// <summary> | 11 | /// </summary> |
12 | /// WorkItemsQueue class. | 12 | public class WorkItemsQueue : IDisposable |
13 | /// </summary> | 13 | { |
14 | public class WorkItemsQueue : IDisposable | 14 | #region Member variables |
15 | { | 15 | |
16 | #region Member variables | 16 | /// <summary> |
17 | 17 | /// Waiters queue (implemented as stack). | |
18 | /// <summary> | 18 | /// </summary> |
19 | /// Waiters queue (implemented as stack). | 19 | private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); |
20 | /// </summary> | 20 | |
21 | private WaiterEntry _headWaiterEntry = new WaiterEntry(); | 21 | /// <summary> |
22 | 22 | /// Waiters count | |
23 | /// <summary> | 23 | /// </summary> |
24 | /// Waiters count | 24 | private int _waitersCount = 0; |
25 | /// </summary> | 25 | |
26 | private int _waitersCount = 0; | 26 | /// <summary> |
27 | 27 | /// Work items queue | |
28 | /// <summary> | 28 | /// </summary> |
29 | /// Work items queue | 29 | private readonly PriorityQueue _workItems = new PriorityQueue(); |
30 | /// </summary> | 30 | |
31 | private PriorityQueue _workItems = new PriorityQueue(); | 31 | /// <summary> |
32 | 32 | /// Indicate that work items are allowed to be queued | |
33 | /// <summary> | 33 | /// </summary> |
34 | /// Indicate that work items are allowed to be queued | 34 | private bool _isWorkItemsQueueActive = true; |
35 | /// </summary> | 35 | |
36 | private bool _isWorkItemsQueueActive = true; | 36 | |
37 | 37 | #if (WINDOWS_PHONE) | |
38 | /// <summary> | 38 | private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>(); |
39 | /// Each thread in the thread pool keeps its own waiter entry. | 39 | #elif (_WINDOWS_CE) |
40 | /// </summary> | 40 | private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); |
41 | [ThreadStatic] | 41 | #else |
42 | private static WaiterEntry _waiterEntry; | 42 | |
43 | 43 | [ThreadStatic] | |
44 | /// <summary> | 44 | private static WaiterEntry _waiterEntry; |
45 | /// A flag that indicates if the WorkItemsQueue has been disposed. | 45 | #endif |
46 | /// </summary> | 46 | |
47 | private bool _isDisposed = false; | 47 | |
48 | 48 | /// <summary> | |
49 | #endregion | 49 | /// Each thread in the thread pool keeps its own waiter entry. |
50 | 50 | /// </summary> | |
51 | #region Public properties | 51 | private static WaiterEntry CurrentWaiterEntry |
52 | 52 | { | |
53 | /// <summary> | 53 | #if (WINDOWS_PHONE) |
54 | /// Returns the current number of work items in the queue | 54 | get |
55 | /// </summary> | 55 | { |
56 | public int Count | 56 | lock (_waiterEntries) |
57 | { | 57 | { |
58 | get | 58 | WaiterEntry waiterEntry; |
59 | { | 59 | if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry)) |
60 | lock(this) | 60 | { |
61 | { | 61 | return waiterEntry; |
62 | ValidateNotDisposed(); | 62 | } |
63 | return _workItems.Count; | 63 | } |
64 | } | 64 | return null; |
65 | } | 65 | } |
66 | } | 66 | set |
67 | 67 | { | |
68 | /// <summary> | 68 | lock (_waiterEntries) |
69 | /// Returns the current number of waiters | 69 | { |
70 | /// </summary> | 70 | _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value; |
71 | public int WaitersCount | 71 | } |
72 | { | 72 | } |
73 | get | 73 | #elif (_WINDOWS_CE) |
74 | { | 74 | get |
75 | lock(this) | 75 | { |
76 | { | 76 | return Thread.GetData(_waiterEntrySlot) as WaiterEntry; |
77 | ValidateNotDisposed(); | 77 | } |
78 | return _waitersCount; | 78 | set |
79 | } | 79 | { |
80 | } | 80 | Thread.SetData(_waiterEntrySlot, value); |
81 | } | 81 | } |
82 | 82 | #else | |
83 | 83 | get | |
84 | #endregion | 84 | { |
85 | 85 | return _waiterEntry; | |
86 | #region Public methods | 86 | } |
87 | 87 | set | |
88 | /// <summary> | 88 | { |
89 | /// Enqueue a work item to the queue. | 89 | _waiterEntry = value; |
90 | /// </summary> | 90 | } |
91 | public bool EnqueueWorkItem(WorkItem workItem) | 91 | #endif |
92 | { | 92 | } |
93 | // A work item cannot be null, since null is used in the | 93 | |
94 | // WaitForWorkItem() method to indicate timeout or cancel | 94 | /// <summary> |
95 | if (null == workItem) | 95 | /// A flag that indicates if the WorkItemsQueue has been disposed. |
96 | { | 96 | /// </summary> |
97 | throw new ArgumentNullException("workItem" , "workItem cannot be null"); | 97 | private bool _isDisposed = false; |
98 | } | 98 | |
99 | 99 | #endregion | |
100 | bool enqueue = true; | 100 | |
101 | 101 | #region Public properties | |
102 | // First check if there is a waiter waiting for work item. During | 102 | |
103 | // the check, timed out waiters are ignored. If there is no | 103 | /// <summary> |
104 | // waiter then the work item is queued. | 104 | /// Returns the current number of work items in the queue |
105 | lock(this) | 105 | /// </summary> |
106 | { | 106 | public int Count |
107 | ValidateNotDisposed(); | 107 | { |
108 | 108 | get | |
109 | if (!_isWorkItemsQueueActive) | 109 | { |
110 | { | 110 | return _workItems.Count; |
111 | return false; | 111 | } |
112 | } | 112 | } |
113 | 113 | ||
114 | while(_waitersCount > 0) | 114 | /// <summary> |
115 | { | 115 | /// Returns the current number of waiters |
116 | // Dequeue a waiter. | 116 | /// </summary> |
117 | WaiterEntry waiterEntry = PopWaiter(); | 117 | public int WaitersCount |
118 | 118 | { | |
119 | // Signal the waiter. On success break the loop | 119 | get |
120 | if (waiterEntry.Signal(workItem)) | 120 | { |
121 | { | 121 | return _waitersCount; |
122 | enqueue = false; | 122 | } |
123 | break; | 123 | } |
124 | } | 124 | |
125 | } | 125 | |
126 | 126 | #endregion | |
127 | if (enqueue) | 127 | |
128 | { | 128 | #region Public methods |
129 | // Enqueue the work item | 129 | |
130 | _workItems.Enqueue(workItem); | 130 | /// <summary> |
131 | } | 131 | /// Enqueue a work item to the queue. |
132 | } | 132 | /// </summary> |
133 | return true; | 133 | public bool EnqueueWorkItem(WorkItem workItem) |
134 | } | 134 | { |
135 | 135 | // A work item cannot be null, since null is used in the | |
136 | 136 | // WaitForWorkItem() method to indicate timeout or cancel | |
137 | /// <summary> | 137 | if (null == workItem) |
138 | /// Waits for a work item or exits on timeout or cancel | 138 | { |
139 | /// </summary> | 139 | throw new ArgumentNullException("workItem" , "workItem cannot be null"); |
140 | /// <param name="millisecondsTimeout">Timeout in milliseconds</param> | 140 | } |
141 | /// <param name="cancelEvent">Cancel wait handle</param> | 141 | |
142 | /// <returns>Returns true if the resource was granted</returns> | 142 | bool enqueue = true; |
143 | public WorkItem DequeueWorkItem( | 143 | |
144 | int millisecondsTimeout, | 144 | // First check if there is a waiter waiting for work item. During |
145 | WaitHandle cancelEvent) | 145 | // the check, timed out waiters are ignored. If there is no |
146 | { | 146 | // waiter then the work item is queued. |
147 | /// This method cause the caller to wait for a work item. | 147 | lock(this) |
148 | /// If there is at least one waiting work item then the | 148 | { |
149 | /// method returns immidiately with true. | 149 | ValidateNotDisposed(); |
150 | /// | 150 | |
151 | /// If there are no waiting work items then the caller | 151 | if (!_isWorkItemsQueueActive) |
152 | /// is queued between other waiters for a work item to arrive. | 152 | { |
153 | /// | 153 | return false; |
154 | /// If a work item didn't come within millisecondsTimeout or | 154 | } |
155 | /// the user canceled the wait by signaling the cancelEvent | 155 | |
156 | /// then the method returns false to indicate that the caller | 156 | while(_waitersCount > 0) |
157 | /// didn't get a work item. | 157 | { |
158 | 158 | // Dequeue a waiter. | |
159 | WaiterEntry waiterEntry = null; | 159 | WaiterEntry waiterEntry = PopWaiter(); |
160 | WorkItem workItem = null; | 160 | |
161 | 161 | // Signal the waiter. On success break the loop | |
162 | lock(this) | 162 | if (waiterEntry.Signal(workItem)) |
163 | { | 163 | { |
164 | ValidateNotDisposed(); | 164 | enqueue = false; |
165 | 165 | break; | |
166 | // If there are waiting work items then take one and return. | 166 | } |
167 | if (_workItems.Count > 0) | 167 | } |
168 | { | 168 | |
169 | workItem = _workItems.Dequeue() as WorkItem; | 169 | if (enqueue) |
170 | return workItem; | 170 | { |
171 | } | 171 | // Enqueue the work item |
172 | // No waiting work items ... | 172 | _workItems.Enqueue(workItem); |
173 | else | 173 | } |
174 | { | 174 | } |
175 | // Get the wait entry for the waiters queue | 175 | return true; |
176 | waiterEntry = GetThreadWaiterEntry(); | 176 | } |
177 | 177 | ||
178 | // Put the waiter with the other waiters | 178 | |
179 | PushWaiter(waiterEntry); | 179 | /// <summary> |
180 | } | 180 | /// Waits for a work item or exits on timeout or cancel |
181 | } | 181 | /// </summary> |
182 | 182 | /// <param name="millisecondsTimeout">Timeout in milliseconds</param> | |
183 | // Prepare array of wait handle for the WaitHandle.WaitAny() | 183 | /// <param name="cancelEvent">Cancel wait handle</param> |
184 | WaitHandle [] waitHandles = new WaitHandle [] { | 184 | /// <returns>Returns true if the resource was granted</returns> |
185 | waiterEntry.WaitHandle, | 185 | public WorkItem DequeueWorkItem( |
186 | cancelEvent }; | 186 | int millisecondsTimeout, |
187 | 187 | WaitHandle cancelEvent) | |
188 | // Wait for an available resource, cancel event, or timeout. | 188 | { |
189 | 189 | // This method cause the caller to wait for a work item. | |
190 | // During the wait we are supposes to exit the synchronization | 190 | // If there is at least one waiting work item then the |
191 | // domain. (Placing true as the third argument of the WaitAny()) | 191 | // method returns immidiately with it. |
192 | // It just doesn't work, I don't know why, so I have lock(this) | 192 | // |
193 | // statments insted of one. | 193 | // If there are no waiting work items then the caller |
194 | 194 | // is queued between other waiters for a work item to arrive. | |
195 | int index = WaitHandle.WaitAny( | 195 | // |
196 | waitHandles, | 196 | // If a work item didn't come within millisecondsTimeout or |
197 | millisecondsTimeout, | 197 | // the user canceled the wait by signaling the cancelEvent |
198 | true); | 198 | // then the method returns null to indicate that the caller |
199 | 199 | // didn't get a work item. | |
200 | lock(this) | 200 | |
201 | { | 201 | WaiterEntry waiterEntry; |
202 | // success is true if it got a work item. | 202 | WorkItem workItem = null; |
203 | bool success = (0 == index); | 203 | lock (this) |
204 | 204 | { | |
205 | // The timeout variable is used only for readability. | 205 | ValidateNotDisposed(); |
206 | // (We treat cancel as timeout) | 206 | |
207 | bool timeout = !success; | 207 | // If there are waiting work items then take one and return. |
208 | 208 | if (_workItems.Count > 0) | |
209 | // On timeout update the waiterEntry that it is timed out | 209 | { |
210 | if (timeout) | 210 | workItem = _workItems.Dequeue() as WorkItem; |
211 | { | 211 | return workItem; |
212 | // The Timeout() fails if the waiter has already been signaled | 212 | } |
213 | timeout = waiterEntry.Timeout(); | 213 | |
214 | 214 | // No waiting work items ... | |
215 | // On timeout remove the waiter from the queue. | 215 | |
216 | // Note that the complexity is O(1). | 216 | // Get the waiter entry for the waiters queue |
217 | if(timeout) | 217 | waiterEntry = GetThreadWaiterEntry(); |
218 | { | 218 | |
219 | RemoveWaiter(waiterEntry, false); | 219 | // Put the waiter with the other waiters |
220 | } | 220 | PushWaiter(waiterEntry); |
221 | 221 | } | |
222 | // Again readability | 222 | |
223 | success = !timeout; | 223 | // Prepare array of wait handle for the WaitHandle.WaitAny() |
224 | } | 224 | WaitHandle [] waitHandles = new WaitHandle[] { |
225 | 225 | waiterEntry.WaitHandle, | |
226 | // On success return the work item | 226 | cancelEvent }; |
227 | if (success) | 227 | |
228 | { | 228 | // Wait for an available resource, cancel event, or timeout. |
229 | workItem = waiterEntry.WorkItem; | 229 | |
230 | 230 | // During the wait we are supposes to exit the synchronization | |
231 | if (null == workItem) | 231 | // domain. (Placing true as the third argument of the WaitAny()) |
232 | { | 232 | // It just doesn't work, I don't know why, so I have two lock(this) |
233 | workItem = _workItems.Dequeue() as WorkItem; | 233 | // statments instead of one. |
234 | } | 234 | |
235 | } | 235 | int index = STPEventWaitHandle.WaitAny( |
236 | } | 236 | waitHandles, |
237 | // On failure return null. | 237 | millisecondsTimeout, |
238 | return workItem; | 238 | true); |
239 | } | 239 | |
240 | 240 | lock(this) | |
241 | /// <summary> | 241 | { |
242 | /// Cleanup the work items queue, hence no more work | 242 | // success is true if it got a work item. |
243 | /// items are allowed to be queue | 243 | bool success = (0 == index); |
244 | /// </summary> | 244 | |
245 | protected virtual void Cleanup() | 245 | // The timeout variable is used only for readability. |
246 | { | 246 | // (We treat cancel as timeout) |
247 | lock(this) | 247 | bool timeout = !success; |
248 | { | 248 | |
249 | // Deactivate only once | 249 | // On timeout update the waiterEntry that it is timed out |
250 | if (!_isWorkItemsQueueActive) | 250 | if (timeout) |
251 | { | 251 | { |
252 | return; | 252 | // The Timeout() fails if the waiter has already been signaled |
253 | } | 253 | timeout = waiterEntry.Timeout(); |
254 | 254 | ||
255 | // Don't queue more work items | 255 | // On timeout remove the waiter from the queue. |
256 | _isWorkItemsQueueActive = false; | 256 | // Note that the complexity is O(1). |
257 | 257 | if(timeout) | |
258 | foreach(WorkItem workItem in _workItems) | 258 | { |
259 | { | 259 | RemoveWaiter(waiterEntry, false); |
260 | workItem.DisposeOfState(); | 260 | } |
261 | } | 261 | |
262 | 262 | // Again readability | |
263 | // Clear the work items that are already queued | 263 | success = !timeout; |
264 | _workItems.Clear(); | 264 | } |
265 | 265 | ||
266 | // Note: | 266 | // On success return the work item |
267 | // I don't iterate over the queue and dispose of work items's states, | 267 | if (success) |
268 | // since if a work item has a state object that is still in use in the | 268 | { |
269 | // application then I must not dispose it. | 269 | workItem = waiterEntry.WorkItem; |
270 | 270 | ||
271 | // Tell the waiters that they were timed out. | 271 | if (null == workItem) |
272 | // It won't signal them to exit, but to ignore their | 272 | { |
273 | // next work item. | 273 | workItem = _workItems.Dequeue() as WorkItem; |
274 | while(_waitersCount > 0) | 274 | } |
275 | { | 275 | } |
276 | WaiterEntry waiterEntry = PopWaiter(); | 276 | } |
277 | waiterEntry.Timeout(); | 277 | // On failure return null. |
278 | } | 278 | return workItem; |
279 | } | 279 | } |
280 | } | 280 | |
281 | 281 | /// <summary> | |
282 | #endregion | 282 | /// Cleanup the work items queue, hence no more work |
283 | 283 | /// items are allowed to be queue | |
284 | #region Private methods | 284 | /// </summary> |
285 | 285 | private void Cleanup() | |
286 | /// <summary> | 286 | { |
287 | /// Returns the WaiterEntry of the current thread | 287 | lock(this) |
288 | /// </summary> | 288 | { |
289 | /// <returns></returns> | 289 | // Deactivate only once |
290 | /// In order to avoid creation and destuction of WaiterEntry | 290 | if (!_isWorkItemsQueueActive) |
291 | /// objects each thread has its own WaiterEntry object. | 291 | { |
292 | private WaiterEntry GetThreadWaiterEntry() | 292 | return; |
293 | { | 293 | } |
294 | if (null == _waiterEntry) | 294 | |
295 | { | 295 | // Don't queue more work items |
296 | _waiterEntry = new WaiterEntry(); | 296 | _isWorkItemsQueueActive = false; |
297 | } | 297 | |
298 | _waiterEntry.Reset(); | 298 | foreach(WorkItem workItem in _workItems) |
299 | return _waiterEntry; | 299 | { |
300 | } | 300 | workItem.DisposeOfState(); |
301 | 301 | } | |
302 | #region Waiters stack methods | 302 | |
303 | 303 | // Clear the work items that are already queued | |
304 | /// <summary> | 304 | _workItems.Clear(); |
305 | /// Push a new waiter into the waiter's stack | 305 | |
306 | /// </summary> | 306 | // Note: |
307 | /// <param name="newWaiterEntry">A waiter to put in the stack</param> | 307 | // I don't iterate over the queue and dispose of work items's states, |
308 | public void PushWaiter(WaiterEntry newWaiterEntry) | 308 | // since if a work item has a state object that is still in use in the |
309 | { | 309 | // application then I must not dispose it. |
310 | // Remove the waiter if it is already in the stack and | 310 | |
311 | // update waiter's count as needed | 311 | // Tell the waiters that they were timed out. |
312 | RemoveWaiter(newWaiterEntry, false); | 312 | // It won't signal them to exit, but to ignore their |
313 | 313 | // next work item. | |
314 | // If the stack is empty then newWaiterEntry is the new head of the stack | 314 | while(_waitersCount > 0) |
315 | if (null == _headWaiterEntry._nextWaiterEntry) | 315 | { |
316 | { | 316 | WaiterEntry waiterEntry = PopWaiter(); |
317 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; | 317 | waiterEntry.Timeout(); |
318 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; | 318 | } |
319 | 319 | } | |
320 | } | 320 | } |
321 | // If the stack is not empty then put newWaiterEntry as the new head | 321 | |
322 | // of the stack. | 322 | public object[] GetStates() |
323 | else | 323 | { |
324 | { | 324 | lock (this) |
325 | // Save the old first waiter entry | 325 | { |
326 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; | 326 | object[] states = new object[_workItems.Count]; |
327 | 327 | int i = 0; | |
328 | // Update the links | 328 | foreach (WorkItem workItem in _workItems) |
329 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; | 329 | { |
330 | newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; | 330 | states[i] = workItem.GetWorkItemResult().State; |
331 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; | 331 | ++i; |
332 | oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; | 332 | } |
333 | } | 333 | return states; |
334 | 334 | } | |
335 | // Increment the number of waiters | 335 | } |
336 | ++_waitersCount; | 336 | |
337 | } | 337 | #endregion |
338 | 338 | ||
339 | /// <summary> | 339 | #region Private methods |
340 | /// Pop a waiter from the waiter's stack | 340 | |
341 | /// </summary> | 341 | /// <summary> |
342 | /// <returns>Returns the first waiter in the stack</returns> | 342 | /// Returns the WaiterEntry of the current thread |
343 | private WaiterEntry PopWaiter() | 343 | /// </summary> |
344 | { | 344 | /// <returns></returns> |
345 | // Store the current stack head | 345 | /// In order to avoid creation and destuction of WaiterEntry |
346 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; | 346 | /// objects each thread has its own WaiterEntry object. |
347 | 347 | private static WaiterEntry GetThreadWaiterEntry() | |
348 | // Store the new stack head | 348 | { |
349 | WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; | 349 | if (null == CurrentWaiterEntry) |
350 | 350 | { | |
351 | // Update the old stack head list links and decrement the number | 351 | CurrentWaiterEntry = new WaiterEntry(); |
352 | // waiters. | 352 | } |
353 | RemoveWaiter(oldFirstWaiterEntry, true); | 353 | CurrentWaiterEntry.Reset(); |
354 | 354 | return CurrentWaiterEntry; | |
355 | // Update the new stack head | 355 | } |
356 | _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; | 356 | |
357 | if (null != newHeadWaiterEntry) | 357 | #region Waiters stack methods |
358 | { | 358 | |
359 | newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; | 359 | /// <summary> |
360 | } | 360 | /// Push a new waiter into the waiter's stack |
361 | 361 | /// </summary> | |
362 | // Return the old stack head | 362 | /// <param name="newWaiterEntry">A waiter to put in the stack</param> |
363 | return oldFirstWaiterEntry; | 363 | public void PushWaiter(WaiterEntry newWaiterEntry) |
364 | } | 364 | { |
365 | 365 | // Remove the waiter if it is already in the stack and | |
366 | /// <summary> | 366 | // update waiter's count as needed |
367 | /// Remove a waiter from the stack | 367 | RemoveWaiter(newWaiterEntry, false); |
368 | /// </summary> | 368 | |
369 | /// <param name="waiterEntry">A waiter entry to remove</param> | 369 | // If the stack is empty then newWaiterEntry is the new head of the stack |
370 | /// <param name="popDecrement">If true the waiter count is always decremented</param> | 370 | if (null == _headWaiterEntry._nextWaiterEntry) |
371 | private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) | 371 | { |
372 | { | 372 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; |
373 | // Store the prev entry in the list | 373 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; |
374 | WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; | 374 | |
375 | 375 | } | |
376 | // Store the next entry in the list | 376 | // If the stack is not empty then put newWaiterEntry as the new head |
377 | WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; | 377 | // of the stack. |
378 | 378 | else | |
379 | // A flag to indicate if we need to decrement the waiters count. | 379 | { |
380 | // If we got here from PopWaiter then we must decrement. | 380 | // Save the old first waiter entry |
381 | // If we got here from PushWaiter then we decrement only if | 381 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; |
382 | // the waiter was already in the stack. | 382 | |
383 | bool decrementCounter = popDecrement; | 383 | // Update the links |
384 | 384 | _headWaiterEntry._nextWaiterEntry = newWaiterEntry; | |
385 | // Null the waiter's entry links | 385 | newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; |
386 | waiterEntry._prevWaiterEntry = null; | 386 | newWaiterEntry._prevWaiterEntry = _headWaiterEntry; |
387 | waiterEntry._nextWaiterEntry = null; | 387 | oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; |
388 | 388 | } | |
389 | // If the waiter entry had a prev link then update it. | 389 | |
390 | // It also means that the waiter is already in the list and we | 390 | // Increment the number of waiters |
391 | // need to decrement the waiters count. | 391 | ++_waitersCount; |
392 | if (null != prevWaiterEntry) | 392 | } |
393 | { | 393 | |
394 | prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; | 394 | /// <summary> |
395 | decrementCounter = true; | 395 | /// Pop a waiter from the waiter's stack |
396 | } | 396 | /// </summary> |
397 | 397 | /// <returns>Returns the first waiter in the stack</returns> | |
398 | // If the waiter entry had a next link then update it. | 398 | private WaiterEntry PopWaiter() |
399 | // It also means that the waiter is already in the list and we | 399 | { |
400 | // need to decrement the waiters count. | 400 | // Store the current stack head |
401 | if (null != nextWaiterEntry) | 401 | WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; |
402 | { | 402 | |
403 | nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; | 403 | // Store the new stack head |
404 | decrementCounter = true; | 404 | WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; |
405 | } | 405 | |
406 | 406 | // Update the old stack head list links and decrement the number | |
407 | // Decrement the waiters count if needed | 407 | // waiters. |
408 | if (decrementCounter) | 408 | RemoveWaiter(oldFirstWaiterEntry, true); |
409 | { | 409 | |
410 | --_waitersCount; | 410 | // Update the new stack head |
411 | } | 411 | _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; |
412 | } | 412 | if (null != newHeadWaiterEntry) |
413 | 413 | { | |
414 | #endregion | 414 | newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; |
415 | 415 | } | |
416 | #endregion | 416 | |
417 | 417 | // Return the old stack head | |
418 | #region WaiterEntry class | 418 | return oldFirstWaiterEntry; |
419 | 419 | } | |
420 | // A waiter entry in the _waiters queue. | 420 | |
421 | public class WaiterEntry : IDisposable | 421 | /// <summary> |
422 | { | 422 | /// Remove a waiter from the stack |
423 | #region Member variables | 423 | /// </summary> |
424 | 424 | /// <param name="waiterEntry">A waiter entry to remove</param> | |
425 | /// <summary> | 425 | /// <param name="popDecrement">If true the waiter count is always decremented</param> |
426 | /// Event to signal the waiter that it got the work item. | 426 | private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) |
427 | /// </summary> | 427 | { |
428 | private AutoResetEvent _waitHandle = new AutoResetEvent(false); | 428 | // Store the prev entry in the list |
429 | 429 | WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; | |
430 | /// <summary> | 430 | |
431 | /// Flag to know if this waiter already quited from the queue | 431 | // Store the next entry in the list |
432 | /// because of a timeout. | 432 | WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; |
433 | /// </summary> | 433 | |
434 | private bool _isTimedout = false; | 434 | // A flag to indicate if we need to decrement the waiters count. |
435 | 435 | // If we got here from PopWaiter then we must decrement. | |
436 | /// <summary> | 436 | // If we got here from PushWaiter then we decrement only if |
437 | /// Flag to know if the waiter was signaled and got a work item. | 437 | // the waiter was already in the stack. |
438 | /// </summary> | 438 | bool decrementCounter = popDecrement; |
439 | private bool _isSignaled = false; | 439 | |
440 | 440 | // Null the waiter's entry links | |
441 | /// <summary> | 441 | waiterEntry._prevWaiterEntry = null; |
442 | /// A work item that passed directly to the waiter withou going | 442 | waiterEntry._nextWaiterEntry = null; |
443 | /// through the queue | 443 | |
444 | /// </summary> | 444 | // If the waiter entry had a prev link then update it. |
445 | private WorkItem _workItem = null; | 445 | // It also means that the waiter is already in the list and we |
446 | 446 | // need to decrement the waiters count. | |
447 | private bool _isDisposed = false; | 447 | if (null != prevWaiterEntry) |
448 | 448 | { | |
449 | // Linked list members | 449 | prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; |
450 | internal WaiterEntry _nextWaiterEntry = null; | 450 | decrementCounter = true; |
451 | internal WaiterEntry _prevWaiterEntry = null; | 451 | } |
452 | 452 | ||
453 | #endregion | 453 | // If the waiter entry had a next link then update it. |
454 | 454 | // It also means that the waiter is already in the list and we | |
455 | #region Construction | 455 | // need to decrement the waiters count. |
456 | 456 | if (null != nextWaiterEntry) | |
457 | public WaiterEntry() | 457 | { |
458 | { | 458 | nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; |
459 | Reset(); | 459 | decrementCounter = true; |
460 | } | 460 | } |
461 | 461 | ||
462 | #endregion | 462 | // Decrement the waiters count if needed |
463 | 463 | if (decrementCounter) | |
464 | #region Public methods | 464 | { |
465 | 465 | --_waitersCount; | |
466 | public WaitHandle WaitHandle | 466 | } |
467 | { | 467 | } |
468 | get { return _waitHandle; } | 468 | |
469 | } | 469 | #endregion |
470 | 470 | ||
471 | public WorkItem WorkItem | 471 | #endregion |
472 | { | 472 | |
473 | get | 473 | #region WaiterEntry class |
474 | { | 474 | |
475 | lock(this) | 475 | // A waiter entry in the _waiters queue. |
476 | { | 476 | public sealed class WaiterEntry : IDisposable |
477 | return _workItem; | 477 | { |
478 | } | 478 | #region Member variables |
479 | } | 479 | |
480 | } | 480 | /// <summary> |
481 | 481 | /// Event to signal the waiter that it got the work item. | |
482 | /// <summary> | 482 | /// </summary> |
483 | /// Signal the waiter that it got a work item. | 483 | //private AutoResetEvent _waitHandle = new AutoResetEvent(false); |
484 | /// </summary> | 484 | private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); |
485 | /// <returns>Return true on success</returns> | 485 | |
486 | /// The method fails if Timeout() preceded its call | 486 | /// <summary> |
487 | public bool Signal(WorkItem workItem) | 487 | /// Flag to know if this waiter already quited from the queue |
488 | { | 488 | /// because of a timeout. |
489 | lock(this) | 489 | /// </summary> |
490 | { | 490 | private bool _isTimedout = false; |
491 | if (!_isTimedout) | 491 | |
492 | { | 492 | /// <summary> |
493 | _workItem = workItem; | 493 | /// Flag to know if the waiter was signaled and got a work item. |
494 | _isSignaled = true; | 494 | /// </summary> |
495 | _waitHandle.Set(); | 495 | private bool _isSignaled = false; |
496 | return true; | 496 | |
497 | } | 497 | /// <summary> |
498 | } | 498 | /// A work item that passed directly to the waiter withou going |
499 | return false; | 499 | /// through the queue |
500 | } | 500 | /// </summary> |
501 | 501 | private WorkItem _workItem = null; | |
502 | /// <summary> | 502 | |
503 | /// Mark the wait entry that it has been timed out | 503 | private bool _isDisposed = false; |
504 | /// </summary> | 504 | |
505 | /// <returns>Return true on success</returns> | 505 | // Linked list members |
506 | /// The method fails if Signal() preceded its call | 506 | internal WaiterEntry _nextWaiterEntry = null; |
507 | public bool Timeout() | 507 | internal WaiterEntry _prevWaiterEntry = null; |
508 | { | 508 | |
509 | lock(this) | 509 | #endregion |
510 | { | 510 | |
511 | // Time out can happen only if the waiter wasn't marked as | 511 | #region Construction |
512 | // signaled | 512 | |
513 | if (!_isSignaled) | 513 | public WaiterEntry() |
514 | { | 514 | { |
515 | // We don't remove the waiter from the queue, the DequeueWorkItem | 515 | Reset(); |
516 | // method skips _waiters that were timed out. | 516 | } |
517 | _isTimedout = true; | 517 | |
518 | return true; | 518 | #endregion |
519 | } | 519 | |
520 | } | 520 | #region Public methods |
521 | return false; | 521 | |
522 | } | 522 | public WaitHandle WaitHandle |
523 | 523 | { | |
524 | /// <summary> | 524 | get { return _waitHandle; } |
525 | /// Reset the wait entry so it can be used again | 525 | } |
526 | /// </summary> | 526 | |
527 | public void Reset() | 527 | public WorkItem WorkItem |
528 | { | 528 | { |
529 | _workItem = null; | 529 | get |
530 | _isTimedout = false; | 530 | { |
531 | _isSignaled = false; | 531 | return _workItem; |
532 | _waitHandle.Reset(); | 532 | } |
533 | } | 533 | } |
534 | 534 | ||
535 | /// <summary> | 535 | /// <summary> |
536 | /// Free resources | 536 | /// Signal the waiter that it got a work item. |
537 | /// </summary> | 537 | /// </summary> |
538 | public void Close() | 538 | /// <returns>Return true on success</returns> |
539 | { | 539 | /// The method fails if Timeout() preceded its call |
540 | if (null != _waitHandle) | 540 | public bool Signal(WorkItem workItem) |
541 | { | 541 | { |
542 | _waitHandle.Close(); | 542 | lock(this) |
543 | _waitHandle = null; | 543 | { |
544 | } | 544 | if (!_isTimedout) |
545 | } | 545 | { |
546 | 546 | _workItem = workItem; | |
547 | #endregion | 547 | _isSignaled = true; |
548 | 548 | _waitHandle.Set(); | |
549 | #region IDisposable Members | 549 | return true; |
550 | 550 | } | |
551 | public void Dispose() | 551 | } |
552 | { | 552 | return false; |
553 | if (!_isDisposed) | 553 | } |
554 | { | 554 | |
555 | Close(); | 555 | /// <summary> |
556 | _isDisposed = true; | 556 | /// Mark the wait entry that it has been timed out |
557 | } | 557 | /// </summary> |
558 | } | 558 | /// <returns>Return true on success</returns> |
559 | 559 | /// The method fails if Signal() preceded its call | |
560 | ~WaiterEntry() | 560 | public bool Timeout() |
561 | { | 561 | { |
562 | Dispose(); | 562 | lock(this) |
563 | } | 563 | { |
564 | 564 | // Time out can happen only if the waiter wasn't marked as | |
565 | #endregion | 565 | // signaled |
566 | } | 566 | if (!_isSignaled) |
567 | 567 | { | |
568 | #endregion | 568 | // We don't remove the waiter from the queue, the DequeueWorkItem |
569 | 569 | // method skips _waiters that were timed out. | |
570 | #region IDisposable Members | 570 | _isTimedout = true; |
571 | 571 | return true; | |
572 | public void Dispose() | 572 | } |
573 | { | 573 | } |
574 | if (!_isDisposed) | 574 | return false; |
575 | { | 575 | } |
576 | Cleanup(); | 576 | |
577 | _isDisposed = true; | 577 | /// <summary> |
578 | GC.SuppressFinalize(this); | 578 | /// Reset the wait entry so it can be used again |
579 | } | 579 | /// </summary> |
580 | } | 580 | public void Reset() |
581 | 581 | { | |
582 | ~WorkItemsQueue() | 582 | _workItem = null; |
583 | { | 583 | _isTimedout = false; |
584 | Cleanup(); | 584 | _isSignaled = false; |
585 | } | 585 | _waitHandle.Reset(); |
586 | 586 | } | |
587 | private void ValidateNotDisposed() | 587 | |
588 | { | 588 | /// <summary> |
589 | if(_isDisposed) | 589 | /// Free resources |
590 | { | 590 | /// </summary> |
591 | throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); | 591 | public void Close() |
592 | } | 592 | { |
593 | } | 593 | if (null != _waitHandle) |
594 | 594 | { | |
595 | #endregion | 595 | _waitHandle.Close(); |
596 | } | 596 | _waitHandle = null; |
597 | 597 | } | |
598 | #endregion | 598 | } |
599 | } | 599 | |
600 | 600 | #endregion | |
601 | |||
602 | #region IDisposable Members | ||
603 | |||
604 | public void Dispose() | ||
605 | { | ||
606 | lock (this) | ||
607 | { | ||
608 | if (!_isDisposed) | ||
609 | { | ||
610 | Close(); | ||
611 | } | ||
612 | _isDisposed = true; | ||
613 | } | ||
614 | } | ||
615 | |||
616 | #endregion | ||
617 | } | ||
618 | |||
619 | #endregion | ||
620 | |||
621 | #region IDisposable Members | ||
622 | |||
623 | public void Dispose() | ||
624 | { | ||
625 | if (!_isDisposed) | ||
626 | { | ||
627 | Cleanup(); | ||
628 | } | ||
629 | _isDisposed = true; | ||
630 | } | ||
631 | |||
632 | private void ValidateNotDisposed() | ||
633 | { | ||
634 | if(_isDisposed) | ||
635 | { | ||
636 | throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); | ||
637 | } | ||
638 | } | ||
639 | |||
640 | #endregion | ||
641 | } | ||
642 | |||
643 | #endregion | ||
644 | } | ||
645 | |||