aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ThirdParty/SmartThreadPool
diff options
context:
space:
mode:
authorDiva Canto2013-05-06 09:18:17 -0700
committerDiva Canto2013-05-06 09:18:17 -0700
commita81ddf3d7097a2e0959080ae7291357435b0bd5b (patch)
tree688ceb63aa41d8eb9d3af27bd57d39cbd38ca250 /ThirdParty/SmartThreadPool
parentMinor reordering of operations on NewUserConnection. The agent circuit needs ... (diff)
parentMerge branch 'master' into bulletsim4 (diff)
downloadopensim-SC_OLD-a81ddf3d7097a2e0959080ae7291357435b0bd5b.zip
opensim-SC_OLD-a81ddf3d7097a2e0959080ae7291357435b0bd5b.tar.gz
opensim-SC_OLD-a81ddf3d7097a2e0959080ae7291357435b0bd5b.tar.bz2
opensim-SC_OLD-a81ddf3d7097a2e0959080ae7291357435b0bd5b.tar.xz
Merge branch 'master' of ssh://opensimulator.org/var/git/opensim
Diffstat (limited to 'ThirdParty/SmartThreadPool')
-rw-r--r--ThirdParty/SmartThreadPool/AssemblyInfo.cs61
-rw-r--r--ThirdParty/SmartThreadPool/CallerThreadContext.cs309
-rw-r--r--ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs14
-rw-r--r--ThirdParty/SmartThreadPool/EventWaitHandle.cs104
-rw-r--r--ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs82
-rw-r--r--ThirdParty/SmartThreadPool/Exceptions.cs82
-rw-r--r--ThirdParty/SmartThreadPool/Interfaces.cs703
-rw-r--r--ThirdParty/SmartThreadPool/InternalInterfaces.cs27
-rw-r--r--ThirdParty/SmartThreadPool/PriorityQueue.cs465
-rw-r--r--ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs23
-rw-r--r--ThirdParty/SmartThreadPool/SLExt.cs16
-rw-r--r--ThirdParty/SmartThreadPool/STPEventWaitHandle.cs62
-rw-r--r--ThirdParty/SmartThreadPool/STPPerformanceCounter.cs648
-rw-r--r--ThirdParty/SmartThreadPool/STPStartInfo.cs269
-rw-r--r--ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs60
-rw-r--r--ThirdParty/SmartThreadPool/SmartThreadPool.cs2562
-rw-r--r--ThirdParty/SmartThreadPool/SynchronizedDictionary.cs89
-rw-r--r--ThirdParty/SmartThreadPool/WIGStartInfo.cs218
-rw-r--r--ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs190
-rw-r--r--ThirdParty/SmartThreadPool/WorkItem.cs659
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemFactory.cs628
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemInfo.cs145
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs128
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemsGroup.cs759
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs471
-rw-r--r--ThirdParty/SmartThreadPool/WorkItemsQueue.cs973
26 files changed, 5820 insertions, 3927 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 @@
1using System;
2using System.Reflection;
3using System.Runtime.InteropServices;
4
5//
6// General Information about an assembly is controlled through the following
7// set of attributes. Change these attribute values to modify the information
8// associated with an assembly.
9//
10[assembly: AssemblyTitle("")]
11[assembly: AssemblyDescription("")]
12[assembly: AssemblyConfiguration("")]
13[assembly: AssemblyCompany("")]
14[assembly: AssemblyProduct("")]
15[assembly: AssemblyCopyright("")]
16[assembly: AssemblyTrademark("")]
17[assembly: AssemblyCulture("")]
18[assembly: ComVisible(false)]
19[assembly: CLSCompliant(true)]
20
21//
22// Version information for an assembly consists of the following four values:
23//
24// Major Version
25// Minor Version
26// Build Number
27// Revision
28//
29// You can specify all the values or you can default the Revision and Build Numbers
30// by using the '*' as shown below:
31
32[assembly: AssemblyVersion("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..e63add5 100644
--- a/ThirdParty/SmartThreadPool/CallerThreadContext.cs
+++ b/ThirdParty/SmartThreadPool/CallerThreadContext.cs
@@ -1,3 +1,6 @@
1
2#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
3
1using System; 4using System;
2using System.Diagnostics; 5using System.Diagnostics;
3using System.Threading; 6using System.Threading;
@@ -6,218 +9,130 @@ using System.Web;
6using System.Runtime.Remoting.Messaging; 9using System.Runtime.Remoting.Messaging;
7 10
8 11
9namespace Amib.Threading 12namespace Amib.Threading.Internal
10{ 13{
11 #region CallerThreadContext class 14#region CallerThreadContext class
12
13 /// <summary>
14 /// This class stores the caller call context in order to restore
15 /// it when the work item is executed in the thread pool environment.
16 /// </summary>
17 internal class CallerThreadContext
18 {
19 #region Prepare reflection information
20
21 // Cached type information.
22 private static MethodInfo getLogicalCallContextMethodInfo =
23 typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
24
25 private static MethodInfo setLogicalCallContextMethodInfo =
26 typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
27 15
28 private static string HttpContextSlotName = GetHttpContextSlotName(); 16 /// <summary>
17 /// This class stores the caller call context in order to restore
18 /// it when the work item is executed in the thread pool environment.
19 /// </summary>
20 internal class CallerThreadContext
21 {
22#region Prepare reflection information
29 23
30 private static string GetHttpContextSlotName() 24 // Cached type information.
31 { 25 private static readonly MethodInfo getLogicalCallContextMethodInfo =
32 FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); 26 typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
33 27
34 if( fi != null ) 28 private static readonly MethodInfo setLogicalCallContextMethodInfo =
35 return (string)fi.GetValue(null); 29 typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
36 else // Use the default "HttpContext" slot name
37 return "HttpContext";
38 }
39 30
40 #endregion 31 private static string HttpContextSlotName = GetHttpContextSlotName();
41
42 #region Private fields
43
44 private HttpContext _httpContext = null;
45 private LogicalCallContext _callContext = null;
46
47 #endregion
48 32
49 /// <summary> 33 private static string GetHttpContextSlotName()
50 /// Constructor 34 {
51 /// </summary> 35 FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
52 private CallerThreadContext()
53 {
54 }
55 36
56 public bool CapturedCallContext 37 if (fi != null)
57 {
58 get
59 { 38 {
60 return (null != _callContext); 39 return (string) fi.GetValue(null);
61 } 40 }
62 }
63 41
64 public bool CapturedHttpContext 42 return "HttpContext";
65 { 43 }
66 get
67 {
68 return (null != _httpContext);
69 }
70 }
71
72 /// <summary>
73 /// Captures the current thread context
74 /// </summary>
75 /// <returns></returns>
76 public static CallerThreadContext Capture(
77 bool captureCallContext,
78 bool captureHttpContext)
79 {
80 Debug.Assert(captureCallContext || captureHttpContext);
81
82 CallerThreadContext callerThreadContext = new CallerThreadContext();
83
84 // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture()
85 // Capture Call Context
86 if(captureCallContext && (getLogicalCallContextMethodInfo != null))
87 {
88 callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null);
89 if (callerThreadContext._callContext != null)
90 {
91 callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone();
92 }
93 }
94
95 // Capture httpContext
96 if (captureHttpContext && (null != HttpContext.Current))
97 {
98 callerThreadContext._httpContext = HttpContext.Current;
99 }
100
101 return callerThreadContext;
102 }
103 44
104 /// <summary> 45 #endregion
105 /// Applies the thread context stored earlier
106 /// </summary>
107 /// <param name="callerThreadContext"></param>
108 public static void Apply(CallerThreadContext callerThreadContext)
109 {
110 if (null == callerThreadContext)
111 {
112 throw new ArgumentNullException("callerThreadContext");
113 }
114
115 // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run()
116 // Restore call context
117 if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null))
118 {
119 setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
120 }
121
122 // Restore HttpContext
123 if (callerThreadContext._httpContext != null)
124 {
125 CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
126 }
127 }
128 }
129
130 #endregion
131
132}
133 46
47#region Private fields
134 48
135/* 49 private HttpContext _httpContext;
136// Ami Bar 50 private LogicalCallContext _callContext;
137// amibar@gmail.com
138 51
139using System; 52 #endregion
140using System.Threading;
141using System.Globalization;
142using System.Security.Principal;
143using System.Reflection;
144using System.Runtime.Remoting.Contexts;
145 53
146namespace Amib.Threading.Internal 54 /// <summary>
147{ 55 /// Constructor
148 #region CallerThreadContext class 56 /// </summary>
149 57 private CallerThreadContext()
150 /// <summary> 58 {
151 /// This class stores the caller thread context in order to restore 59 }
152 /// it when the work item is executed in the context of the thread 60
153 /// from the pool. 61 public bool CapturedCallContext
154 /// Note that we can't store the thread's CompressedStack, because 62 {
155 /// it throws a security exception 63 get
156 /// </summary> 64 {
157 public class CallerThreadContext 65 return (null != _callContext);
158 { 66 }
159 private CultureInfo _culture = null; 67 }
160 private CultureInfo _cultureUI = null; 68
161 private IPrincipal _principal; 69 public bool CapturedHttpContext
162 private System.Runtime.Remoting.Contexts.Context _context; 70 {
163 71 get
164 private static FieldInfo _fieldInfo = GetFieldInfo(); 72 {
165 73 return (null != _httpContext);
166 private static FieldInfo GetFieldInfo() 74 }
167 { 75 }
168 Type threadType = typeof(Thread); 76
169 return threadType.GetField( 77 /// <summary>
170 "m_Context", 78 /// Captures the current thread context
171 BindingFlags.Instance | BindingFlags.NonPublic); 79 /// </summary>
172 } 80 /// <returns></returns>
173 81 public static CallerThreadContext Capture(
174 /// <summary> 82 bool captureCallContext,
175 /// Constructor 83 bool captureHttpContext)
176 /// </summary> 84 {
177 private CallerThreadContext() 85 Debug.Assert(captureCallContext || captureHttpContext);
178 { 86
179 } 87 CallerThreadContext callerThreadContext = new CallerThreadContext();
180 88
181 /// <summary> 89 // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture()
182 /// Captures the current thread context 90 // Capture Call Context
183 /// </summary> 91 if(captureCallContext && (getLogicalCallContextMethodInfo != null))
184 /// <returns></returns> 92 {
185 public static CallerThreadContext Capture() 93 callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null);
186 { 94 if (callerThreadContext._callContext != null)
187 CallerThreadContext callerThreadContext = new CallerThreadContext(); 95 {
188 96 callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone();
189 Thread thread = Thread.CurrentThread; 97 }
190 callerThreadContext._culture = thread.CurrentCulture; 98 }
191 callerThreadContext._cultureUI = thread.CurrentUICulture; 99
192 callerThreadContext._principal = Thread.CurrentPrincipal; 100 // Capture httpContext
193 callerThreadContext._context = Thread.CurrentContext; 101 if (captureHttpContext && (null != HttpContext.Current))
194 return callerThreadContext; 102 {
195 } 103 callerThreadContext._httpContext = HttpContext.Current;
196 104 }
197 /// <summary> 105
198 /// Applies the thread context stored earlier 106 return callerThreadContext;
199 /// </summary> 107 }
200 /// <param name="callerThreadContext"></param> 108
201 public static void Apply(CallerThreadContext callerThreadContext) 109 /// <summary>
202 { 110 /// Applies the thread context stored earlier
203 Thread thread = Thread.CurrentThread; 111 /// </summary>
204 thread.CurrentCulture = callerThreadContext._culture; 112 /// <param name="callerThreadContext"></param>
205 thread.CurrentUICulture = callerThreadContext._cultureUI; 113 public static void Apply(CallerThreadContext callerThreadContext)
206 Thread.CurrentPrincipal = callerThreadContext._principal; 114 {
207 115 if (null == callerThreadContext)
208 // Uncomment the following block to enable the Thread.CurrentThread 116 {
209/* 117 throw new ArgumentNullException("callerThreadContext");
210 if (null != _fieldInfo) 118 }
211 { 119
212 _fieldInfo.SetValue( 120 // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run()
213 Thread.CurrentThread, 121 // Restore call context
214 callerThreadContext._context); 122 if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null))
215 } 123 {
216* / 124 setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
217 } 125 }
218 } 126
127 // Restore HttpContext
128 if (callerThreadContext._httpContext != null)
129 {
130 HttpContext.Current = callerThreadContext._httpContext;
131 //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
132 }
133 }
134 }
219 135
220 #endregion 136 #endregion
221} 137}
222*/ 138#endif
223
diff --git a/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs
new file mode 100644
index 0000000..5752957
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs
@@ -0,0 +1,14 @@
1namespace 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..25be07a
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/EventWaitHandle.cs
@@ -0,0 +1,104 @@
1#if (_WINDOWS_CE)
2
3using System;
4using System.Runtime.InteropServices;
5using System.Threading;
6
7namespace 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..3c9c849
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs
@@ -0,0 +1,82 @@
1using System.Threading;
2
3#if (_WINDOWS_CE)
4using System;
5using System.Runtime.InteropServices;
6#endif
7
8namespace 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..6c6a88b 100644
--- a/ThirdParty/SmartThreadPool/Exceptions.cs
+++ b/ThirdParty/SmartThreadPool/Exceptions.cs
@@ -1,8 +1,7 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System; 1using System;
2#if !(_WINDOWS_CE)
5using System.Runtime.Serialization; 3using System.Runtime.Serialization;
4#endif
6 5
7namespace Amib.Threading 6namespace Amib.Threading
8{ 7{
@@ -11,22 +10,39 @@ namespace Amib.Threading
11 /// <summary> 10 /// <summary>
12 /// Represents an exception in case IWorkItemResult.GetResult has been canceled 11 /// Represents an exception in case IWorkItemResult.GetResult has been canceled
13 /// </summary> 12 /// </summary>
14 [Serializable] 13 public sealed partial class WorkItemCancelException : Exception
15 public sealed class WorkItemCancelException : ApplicationException
16 { 14 {
17 public WorkItemCancelException() : base() 15 public WorkItemCancelException()
18 { 16 {
19 } 17 }
20 18
21 public WorkItemCancelException(string message) : base(message) 19 public WorkItemCancelException(string message)
20 : base(message)
22 { 21 {
23 } 22 }
24 23
25 public WorkItemCancelException(string message, Exception e) : base(message, e) 24 public WorkItemCancelException(string message, Exception e)
25 : base(message, e)
26 { 26 {
27 } 27 }
28 }
28 29
29 public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc) 30 /// <summary>
31 /// Represents an exception in case IWorkItemResult.GetResult has been timed out
32 /// </summary>
33 public sealed partial class WorkItemTimeoutException : Exception
34 {
35 public WorkItemTimeoutException()
36 {
37 }
38
39 public WorkItemTimeoutException(string message)
40 : base(message)
41 {
42 }
43
44 public WorkItemTimeoutException(string message, Exception e)
45 : base(message, e)
30 { 46 {
31 } 47 }
32 } 48 }
@@ -34,22 +50,33 @@ namespace Amib.Threading
34 /// <summary> 50 /// <summary>
35 /// Represents an exception in case IWorkItemResult.GetResult has been timed out 51 /// Represents an exception in case IWorkItemResult.GetResult has been timed out
36 /// </summary> 52 /// </summary>
37 [Serializable] 53 public sealed partial class WorkItemResultException : Exception
38 public sealed class WorkItemTimeoutException : ApplicationException
39 { 54 {
40 public WorkItemTimeoutException() : base() 55 public WorkItemResultException()
41 { 56 {
42 } 57 }
43 58
44 public WorkItemTimeoutException(string message) : base(message) 59 public WorkItemResultException(string message)
60 : base(message)
45 { 61 {
46 } 62 }
47 63
48 public WorkItemTimeoutException(string message, Exception e) : base(message, e) 64 public WorkItemResultException(string message, Exception e)
65 : base(message, e)
49 { 66 {
50 } 67 }
68 }
51 69
52 public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc) 70
71#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
72 /// <summary>
73 /// Represents an exception in case IWorkItemResult.GetResult has been canceled
74 /// </summary>
75 [Serializable]
76 public sealed partial class WorkItemCancelException
77 {
78 public WorkItemCancelException(SerializationInfo si, StreamingContext sc)
79 : base(si, sc)
53 { 80 {
54 } 81 }
55 } 82 }
@@ -58,24 +85,27 @@ namespace Amib.Threading
58 /// Represents an exception in case IWorkItemResult.GetResult has been timed out 85 /// Represents an exception in case IWorkItemResult.GetResult has been timed out
59 /// </summary> 86 /// </summary>
60 [Serializable] 87 [Serializable]
61 public sealed class WorkItemResultException : ApplicationException 88 public sealed partial class WorkItemTimeoutException
62 { 89 {
63 public WorkItemResultException() : base() 90 public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc)
64 { 91 : base(si, sc)
65 }
66
67 public WorkItemResultException(string message) : base(message)
68 {
69 }
70
71 public WorkItemResultException(string message, Exception e) : base(message, e)
72 { 92 {
73 } 93 }
94 }
74 95
75 public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc) 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)
76 { 104 {
77 } 105 }
78 } 106 }
79 107
108#endif
109
80 #endregion 110 #endregion
81} 111}
diff --git a/ThirdParty/SmartThreadPool/Interfaces.cs b/ThirdParty/SmartThreadPool/Interfaces.cs
index f1c1fcf..513422f 100644
--- a/ThirdParty/SmartThreadPool/Interfaces.cs
+++ b/ThirdParty/SmartThreadPool/Interfaces.cs
@@ -1,271 +1,628 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System; 1using System;
5using System.Threading; 2using System.Threading;
6 3
7namespace Amib.Threading 4namespace Amib.Threading
8{ 5{
9 #region Delegates 6 #region Delegates
7
8 /// <summary>
9 /// A delegate that represents the method to run as the work item
10 /// </summary>
11 /// <param name="state">A state object for the method to run</param>
12 public delegate object WorkItemCallback(object state);
13
14 /// <summary>
15 /// A delegate to call after the WorkItemCallback completed
16 /// </summary>
17 /// <param name="wir">The work item result object</param>
18 public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
10 19
11 /// <summary> 20 /// <summary>
12 /// A delegate that represents the method to run as the work item 21 /// A delegate to call after the WorkItemCallback completed
13 /// </summary> 22 /// </summary>
14 /// <param name="state">A state object for the method to run</param> 23 /// <param name="wir">The work item result object</param>
15 public delegate object WorkItemCallback(object state); 24 public delegate void PostExecuteWorkItemCallback<TResult>(IWorkItemResult<TResult> wir);
25
26 /// <summary>
27 /// A delegate to call when a WorkItemsGroup becomes idle
28 /// </summary>
29 /// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
30 public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
16 31
17 /// <summary> 32 /// <summary>
18 /// A delegate to call after the WorkItemCallback completed 33 /// A delegate to call after a thread is created, but before
34 /// it's first use.
19 /// </summary> 35 /// </summary>
20 /// <param name="wir">The work item result object</param> 36 public delegate void ThreadInitializationHandler();
21 public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
22 37
23 /// <summary> 38 /// <summary>
24 /// A delegate to call when a WorkItemsGroup becomes idle 39 /// A delegate to call when a thread is about to exit, after
40 /// it is no longer belong to the pool.
25 /// </summary> 41 /// </summary>
26 /// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param> 42 public delegate void ThreadTerminationHandler();
27 public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
28 43
29 #endregion 44 #endregion
30 45
31 #region WorkItem Priority 46 #region WorkItem Priority
32 47
33 public enum WorkItemPriority 48 /// <summary>
34 { 49 /// Defines the availeable priorities of a work item.
35 Lowest, 50 /// The higher the priority a work item has, the sooner
36 BelowNormal, 51 /// it will be executed.
37 Normal, 52 /// </summary>
38 AboveNormal, 53 public enum WorkItemPriority
39 Highest, 54 {
40 } 55 Lowest,
56 BelowNormal,
57 Normal,
58 AboveNormal,
59 Highest,
60 }
61
62 #endregion
63
64 #region IWorkItemsGroup interface
65
66 /// <summary>
67 /// IWorkItemsGroup interface
68 /// Created by SmartThreadPool.CreateWorkItemsGroup()
69 /// </summary>
70 public interface IWorkItemsGroup
71 {
72 /// <summary>
73 /// Get/Set the name of the WorkItemsGroup
74 /// </summary>
75 string Name { get; set; }
41 76
42 #endregion 77 /// <summary>
78 /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
79 /// </summary>
80 int Concurrency { get; set; }
81
82 /// <summary>
83 /// Get the number of work items waiting in the queue.
84 /// </summary>
85 int WaitingCallbacks { get; }
43 86
44 #region IHasWorkItemPriority interface 87 /// <summary>
88 /// Get an array with all the state objects of the currently running items.
89 /// The array represents a snap shot and impact performance.
90 /// </summary>
91 object[] GetStates();
45 92
46 public interface IHasWorkItemPriority 93 /// <summary>
47 { 94 /// Get the WorkItemsGroup start information
48 WorkItemPriority WorkItemPriority { get; } 95 /// </summary>
49 } 96 WIGStartInfo WIGStartInfo { get; }
50 97
51 #endregion 98 /// <summary>
99 /// Starts to execute work items
100 /// </summary>
101 void Start();
52 102
53 #region IWorkItemsGroup interface 103 /// <summary>
104 /// Cancel all the work items.
105 /// Same as Cancel(false)
106 /// </summary>
107 void Cancel();
54 108
55 /// <summary>
56 /// IWorkItemsGroup interface
57 /// </summary>
58 public interface IWorkItemsGroup
59 {
60 /// <summary> 109 /// <summary>
61 /// Get/Set the name of the WorkItemsGroup 110 /// Cancel all work items using thread abortion
62 /// </summary> 111 /// </summary>
63 string Name { get; set; } 112 /// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
113 void Cancel(bool abortExecution);
64 114
115 /// <summary>
116 /// Wait for all work item to complete.
117 /// </summary>
118 void WaitForIdle();
119
120 /// <summary>
121 /// Wait for all work item to complete, until timeout expired
122 /// </summary>
123 /// <param name="timeout">How long to wait for the work items to complete</param>
124 /// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
125 bool WaitForIdle(TimeSpan timeout);
126
127 /// <summary>
128 /// Wait for all work item to complete, until timeout expired
129 /// </summary>
130 /// <param name="millisecondsTimeout">How long to wait for the work items to complete in milliseconds</param>
131 /// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
132 bool WaitForIdle(int millisecondsTimeout);
133
134 /// <summary>
135 /// IsIdle is true when there are no work items running or queued.
136 /// </summary>
137 bool IsIdle { get; }
138
139 /// <summary>
140 /// This event is fired when all work items are completed.
141 /// (When IsIdle changes to true)
142 /// This event only work on WorkItemsGroup. On SmartThreadPool
143 /// it throws the NotImplementedException.
144 /// </summary>
145 event WorkItemsGroupIdleHandler OnIdle;
146
147 #region QueueWorkItem
148
149 /// <summary>
150 /// Queue a work item
151 /// </summary>
152 /// <param name="callback">A callback to execute</param>
153 /// <returns>Returns a work item result</returns>
65 IWorkItemResult QueueWorkItem(WorkItemCallback callback); 154 IWorkItemResult QueueWorkItem(WorkItemCallback callback);
155
156 /// <summary>
157 /// Queue a work item
158 /// </summary>
159 /// <param name="callback">A callback to execute</param>
160 /// <param name="workItemPriority">The priority of the work item</param>
161 /// <returns>Returns a work item result</returns>
66 IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); 162 IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
163
164 /// <summary>
165 /// Queue a work item
166 /// </summary>
167 /// <param name="callback">A callback to execute</param>
168 /// <param name="state">
169 /// The context object of the work item. Used for passing arguments to the work item.
170 /// </param>
171 /// <returns>Returns a work item result</returns>
67 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); 172 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
173
174 /// <summary>
175 /// Queue a work item
176 /// </summary>
177 /// <param name="callback">A callback to execute</param>
178 /// <param name="state">
179 /// The context object of the work item. Used for passing arguments to the work item.
180 /// </param>
181 /// <param name="workItemPriority">The work item priority</param>
182 /// <returns>Returns a work item result</returns>
68 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); 183 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
184
185 /// <summary>
186 /// Queue a work item
187 /// </summary>
188 /// <param name="callback">A callback to execute</param>
189 /// <param name="state">
190 /// The context object of the work item. Used for passing arguments to the work item.
191 /// </param>
192 /// <param name="postExecuteWorkItemCallback">
193 /// A delegate to call after the callback completion
194 /// </param>
195 /// <returns>Returns a work item result</returns>
69 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); 196 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
197
198 /// <summary>
199 /// Queue a work item
200 /// </summary>
201 /// <param name="callback">A callback to execute</param>
202 /// <param name="state">
203 /// The context object of the work item. Used for passing arguments to the work item.
204 /// </param>
205 /// <param name="postExecuteWorkItemCallback">
206 /// A delegate to call after the callback completion
207 /// </param>
208 /// <param name="workItemPriority">The work item priority</param>
209 /// <returns>Returns a work item result</returns>
70 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); 210 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
211
212 /// <summary>
213 /// Queue a work item
214 /// </summary>
215 /// <param name="callback">A callback to execute</param>
216 /// <param name="state">
217 /// The context object of the work item. Used for passing arguments to the work item.
218 /// </param>
219 /// <param name="postExecuteWorkItemCallback">
220 /// A delegate to call after the callback completion
221 /// </param>
222 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
223 /// <returns>Returns a work item result</returns>
71 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); 224 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
225
226 /// <summary>
227 /// Queue a work item
228 /// </summary>
229 /// <param name="callback">A callback to execute</param>
230 /// <param name="state">
231 /// The context object of the work item. Used for passing arguments to the work item.
232 /// </param>
233 /// <param name="postExecuteWorkItemCallback">
234 /// A delegate to call after the callback completion
235 /// </param>
236 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
237 /// <param name="workItemPriority">The work item priority</param>
238 /// <returns>Returns a work item result</returns>
72 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); 239 IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
73 240
241 /// <summary>
242 /// Queue a work item
243 /// </summary>
244 /// <param name="workItemInfo">Work item info</param>
245 /// <param name="callback">A callback to execute</param>
246 /// <returns>Returns a work item result</returns>
74 IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); 247 IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
75 IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
76
77 void WaitForIdle();
78 bool WaitForIdle(TimeSpan timeout);
79 bool WaitForIdle(int millisecondsTimeout);
80 248
81 int WaitingCallbacks { get; } 249 /// <summary>
82 event WorkItemsGroupIdleHandler OnIdle; 250 /// Queue a work item
83 251 /// </summary>
84 void Cancel(); 252 /// <param name="workItemInfo">Work item information</param>
85 void Start(); 253 /// <param name="callback">A callback to execute</param>
86 } 254 /// <param name="state">
255 /// The context object of the work item. Used for passing arguments to the work item.
256 /// </param>
257 /// <returns>Returns a work item result</returns>
258 IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
87 259
88 #endregion 260 #endregion
89 261
90 #region CallToPostExecute enumerator 262 #region QueueWorkItem(Action<...>)
91 263
92 [Flags] 264 /// <summary>
93 public enum CallToPostExecute 265 /// Queue a work item.
94 { 266 /// </summary>
95 Never = 0x00, 267 /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
96 WhenWorkItemCanceled = 0x01, 268 IWorkItemResult QueueWorkItem(Action action);
97 WhenWorkItemNotCanceled = 0x02,
98 Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
99 }
100 269
101 #endregion 270 /// <summary>
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);
102 275
103 #region IWorkItemResult interface 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);
104 281
105 /// <summary>
106 /// IWorkItemResult interface
107 /// </summary>
108 public interface IWorkItemResult
109 {
110 /// <summary> 282 /// <summary>
111 /// Get the result of the work item. 283 /// Queue a work item.
112 /// If the work item didn't run yet then the caller waits.
113 /// </summary> 284 /// </summary>
114 /// <returns>The result of the work item</returns> 285 /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
115 object GetResult(); 286 IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg);
116 287
117 /// <summary> 288 /// <summary>
118 /// Get the result of the work item. 289 /// Queue a work item.
119 /// If the work item didn't run yet then the caller waits until timeout.
120 /// </summary> 290 /// </summary>
121 /// <returns>The result of the work item</returns> 291 /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
122 /// On timeout throws WorkItemTimeoutException 292 IWorkItemResult QueueWorkItem<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2);
123 object GetResult(
124 int millisecondsTimeout,
125 bool exitContext);
126 293
127 /// <summary> 294 /// <summary>
128 /// Get the result of the work item. 295 /// Queue a work item.
129 /// If the work item didn't run yet then the caller waits until timeout.
130 /// </summary> 296 /// </summary>
131 /// <returns>The result of the work item</returns> 297 /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
132 /// On timeout throws WorkItemTimeoutException 298 IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority);
133 object GetResult(
134 TimeSpan timeout,
135 bool exitContext);
136 299
137 void Abort(); 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);
138 305
139 /// <summary> 306 /// <summary>
140 /// Get the result of the work item. 307 /// Queue a work item.
141 /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
142 /// </summary> 308 /// </summary>
143 /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param> 309 /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
144 /// <param name="exitContext"> 310 IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);
145 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
146 /// </param>
147 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
148 /// <returns>The result of the work item</returns>
149 /// On timeout throws WorkItemTimeoutException
150 /// On cancel throws WorkItemCancelException
151 object GetResult(
152 int millisecondsTimeout,
153 bool exitContext,
154 WaitHandle cancelWaitHandle);
155 311
156 /// <summary> 312 /// <summary>
157 /// Get the result of the work item. 313 /// 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.
159 /// </summary> 314 /// </summary>
160 /// <returns>The result of the work item</returns> 315 /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
161 /// On timeout throws WorkItemTimeoutException 316 IWorkItemResult QueueWorkItem<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
162 /// On cancel throws WorkItemCancelException
163 object GetResult(
164 TimeSpan timeout,
165 bool exitContext,
166 WaitHandle cancelWaitHandle);
167 317
168 /// <summary> 318 /// <summary>
169 /// Get the result of the work item. 319 /// Queue a work item.
170 /// If the work item didn't run yet then the caller waits.
171 /// </summary> 320 /// </summary>
172 /// <param name="e">Filled with the exception if one was thrown</param> 321 /// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
173 /// <returns>The result of the work item</returns> 322 IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);
174 object GetResult(out Exception e); 323
324 #endregion
325
326 #region QueueWorkItem(Func<...>)
175 327
176 /// <summary> 328 /// <summary>
177 /// Get the result of the work item. 329 /// Queue a work item.
178 /// If the work item didn't run yet then the caller waits until timeout.
179 /// </summary> 330 /// </summary>
180 /// <param name="e">Filled with the exception if one was thrown</param> 331 /// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
181 /// <returns>The result of the work item</returns> 332 /// its GetResult() returns a TResult object</returns>
182 /// On timeout throws WorkItemTimeoutException 333 IWorkItemResult<TResult> QueueWorkItem<TResult>(Func<TResult> func);
183 object GetResult(
184 int millisecondsTimeout,
185 bool exitContext,
186 out Exception e);
187 334
188 /// <summary> 335 /// <summary>
189 /// Get the result of the work item. 336 /// Queue a work item.
190 /// If the work item didn't run yet then the caller waits until timeout.
191 /// </summary> 337 /// </summary>
192 /// <param name="e">Filled with the exception if one was thrown</param> 338 /// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
193 /// <returns>The result of the work item</returns> 339 /// its GetResult() returns a TResult object</returns>
194 /// On timeout throws WorkItemTimeoutException 340 IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> func, T arg);
195 object GetResult(
196 TimeSpan timeout,
197 bool exitContext,
198 out Exception e);
199 341
200 /// <summary> 342 /// <summary>
201 /// Get the result of the work item. 343 /// Queue a work item.
202 /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
203 /// </summary> 344 /// </summary>
204 /// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param> 345 /// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
205 /// <param name="exitContext"> 346 /// its GetResult() returns a TResult object</returns>
206 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 347 IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 arg1, T2 arg2);
207 /// </param>
208 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
209 /// <param name="e">Filled with the exception if one was thrown</param>
210 /// <returns>The result of the work item</returns>
211 /// On timeout throws WorkItemTimeoutException
212 /// On cancel throws WorkItemCancelException
213 object GetResult(
214 int millisecondsTimeout,
215 bool exitContext,
216 WaitHandle cancelWaitHandle,
217 out Exception e);
218 348
219 /// <summary> 349 /// <summary>
220 /// Get the result of the work item. 350 /// Queue a work item.
221 /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
222 /// </summary> 351 /// </summary>
223 /// <returns>The result of the work item</returns> 352 /// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
224 /// <param name="e">Filled with the exception if one was thrown</param> 353 /// its GetResult() returns a TResult object</returns>
225 /// On timeout throws WorkItemTimeoutException 354 IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3);
226 /// On cancel throws WorkItemCancelException
227 object GetResult(
228 TimeSpan timeout,
229 bool exitContext,
230 WaitHandle cancelWaitHandle,
231 out Exception e);
232 355
233 /// <summary> 356 /// <summary>
234 /// Gets an indication whether the asynchronous operation has completed. 357 /// Queue a work item.
235 /// </summary> 358 /// </summary>
236 bool IsCompleted { get; } 359 /// <returns>Returns a IWorkItemResult&lt;TResult&gt; 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
237 367
368 #region CallToPostExecute enumerator
369
370 [Flags]
371 public enum CallToPostExecute
372 {
238 /// <summary> 373 /// <summary>
239 /// Gets an indication whether the asynchronous operation has been canceled. 374 /// Never call to the PostExecute call back
240 /// </summary> 375 /// </summary>
241 bool IsCanceled { get; } 376 Never = 0x00,
242 377
243 /// <summary> 378 /// <summary>
244 /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. 379 /// Call to the PostExecute only when the work item is cancelled
245 /// </summary> 380 /// </summary>
246 object State { get; } 381 WhenWorkItemCanceled = 0x01,
247 382
248 /// <summary> 383 /// <summary>
249 /// Cancel the work item if it didn't start running yet. 384 /// Call to the PostExecute only when the work item is not cancelled
250 /// </summary> 385 /// </summary>
251 /// <returns>Returns true on success or false if the work item is in progress or already completed</returns> 386 WhenWorkItemNotCanceled = 0x02,
252 bool Cancel();
253 387
254 /// <summary> 388 /// <summary>
255 /// Get the work item's priority 389 /// Always call to the PostExecute
256 /// </summary> 390 /// </summary>
257 WorkItemPriority WorkItemPriority { get; } 391 Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
392 }
393
394 #endregion
258 395
396 #region IWorkItemResult interface
397
398 /// <summary>
399 /// The common interface of IWorkItemResult and IWorkItemResult&lt;T&gt;
400 /// </summary>
401 public interface IWaitableResult
402 {
259 /// <summary> 403 /// <summary>
260 /// Return the result, same as GetResult() 404 /// This method intent is for internal use.
261 /// </summary> 405 /// </summary>
262 object Result { get; } 406 /// <returns></returns>
407 IWorkItemResult GetWorkItemResult();
263 408
264 /// <summary> 409 /// <summary>
265 /// Returns the exception if occured otherwise returns null. 410 /// This method intent is for internal use.
266 /// </summary> 411 /// </summary>
267 object Exception { get; } 412 /// <returns></returns>
413 IWorkItemResult<TResult> GetWorkItemResultT<TResult>();
268 } 414 }
269 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&lt;TResult&gt; interface.
426 /// Created when a Func&lt;TResult&gt; 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
270 #endregion 627 #endregion
271} 628}
diff --git a/ThirdParty/SmartThreadPool/InternalInterfaces.cs b/ThirdParty/SmartThreadPool/InternalInterfaces.cs
new file mode 100644
index 0000000..0072e10
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/InternalInterfaces.cs
@@ -0,0 +1,27 @@
1
2namespace 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..409c879 100644
--- a/ThirdParty/SmartThreadPool/PriorityQueue.cs
+++ b/ThirdParty/SmartThreadPool/PriorityQueue.cs
@@ -1,240 +1,239 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System; 1using System;
5using System.Collections; 2using System.Collections;
3using System.Collections.Generic;
6using System.Diagnostics; 4using System.Diagnostics;
7 5
8namespace Amib.Threading.Internal 6namespace Amib.Threading.Internal
9{ 7{
10 #region PriorityQueue class 8 #region PriorityQueue class
11 9
12 /// <summary> 10 /// <summary>
13 /// PriorityQueue class 11 /// PriorityQueue class
14 /// This class is not thread safe because we use external lock 12 /// This class is not thread safe because we use external lock
15 /// </summary> 13 /// </summary>
16 public sealed class PriorityQueue : IEnumerable 14 public sealed class PriorityQueue : IEnumerable
17 { 15 {
18 #region Private members 16 #region Private members
19 17
20 /// <summary> 18 /// <summary>
21 /// The number of queues, there is one for each type of priority 19 /// The number of queues, there is one for each type of priority
22 /// </summary> 20 /// </summary>
23 private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; 21 private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1;
24 22
25 /// <summary> 23 /// <summary>
26 /// Work items queues. There is one for each type of priority 24 /// Work items queues. There is one for each type of priority
27 /// </summary> 25 /// </summary>
28 private Queue [] _queues = new Queue[_queuesCount]; 26 private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];
29 27
30 /// <summary> 28 /// <summary>
31 /// The total number of work items within the queues 29 /// The total number of work items within the queues
32 /// </summary> 30 /// </summary>
33 private int _workItemsCount = 0; 31 private int _workItemsCount;
34 32
35 /// <summary> 33 /// <summary>
36 /// Use with IEnumerable interface 34 /// Use with IEnumerable interface
37 /// </summary> 35 /// </summary>
38 private int _version = 0; 36 private int _version;
39 37
40 #endregion 38 #endregion
41 39
42 #region Contructor 40 #region Contructor
43 41
44 public PriorityQueue() 42 public PriorityQueue()
45 { 43 {
46 for(int i = 0; i < _queues.Length; ++i) 44 for(int i = 0; i < _queues.Length; ++i)
47 { 45 {
48 _queues[i] = new Queue(); 46 _queues[i] = new LinkedList<IHasWorkItemPriority>();
49 } 47 }
50 } 48 }
51 49
52 #endregion 50 #endregion
53 51
54 #region Methods 52 #region Methods
55 53
56 /// <summary> 54 /// <summary>
57 /// Enqueue a work item. 55 /// Enqueue a work item.
58 /// </summary> 56 /// </summary>
59 /// <param name="workItem">A work item</param> 57 /// <param name="workItem">A work item</param>
60 public void Enqueue(IHasWorkItemPriority workItem) 58 public void Enqueue(IHasWorkItemPriority workItem)
61 { 59 {
62 Debug.Assert(null != workItem); 60 Debug.Assert(null != workItem);
63 61
64 int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; 62 int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1;
65 Debug.Assert(queueIndex >= 0); 63 Debug.Assert(queueIndex >= 0);
66 Debug.Assert(queueIndex < _queuesCount); 64 Debug.Assert(queueIndex < _queuesCount);
67 65
68 _queues[queueIndex].Enqueue(workItem); 66 _queues[queueIndex].AddLast(workItem);
69 ++_workItemsCount; 67 ++_workItemsCount;
70 ++_version; 68 ++_version;
71 } 69 }
72 70
73 /// <summary> 71 /// <summary>
74 /// Dequeque a work item. 72 /// Dequeque a work item.
75 /// </summary> 73 /// </summary>
76 /// <returns>Returns the next work item</returns> 74 /// <returns>Returns the next work item</returns>
77 public IHasWorkItemPriority Dequeue() 75 public IHasWorkItemPriority Dequeue()
78 { 76 {
79 IHasWorkItemPriority workItem = null; 77 IHasWorkItemPriority workItem = null;
80 78
81 if(_workItemsCount > 0) 79 if(_workItemsCount > 0)
82 { 80 {
83 int queueIndex = GetNextNonEmptyQueue(-1); 81 int queueIndex = GetNextNonEmptyQueue(-1);
84 Debug.Assert(queueIndex >= 0); 82 Debug.Assert(queueIndex >= 0);
85 workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority; 83 workItem = _queues[queueIndex].First.Value;
86 Debug.Assert(null != workItem); 84 _queues[queueIndex].RemoveFirst();
87 --_workItemsCount; 85 Debug.Assert(null != workItem);
88 ++_version; 86 --_workItemsCount;
89 } 87 ++_version;
90 88 }
91 return workItem; 89
92 } 90 return workItem;
93 91 }
94 /// <summary> 92
95 /// Find the next non empty queue starting at queue queueIndex+1 93 /// <summary>
96 /// </summary> 94 /// Find the next non empty queue starting at queue queueIndex+1
97 /// <param name="queueIndex">The index-1 to start from</param> 95 /// </summary>
98 /// <returns> 96 /// <param name="queueIndex">The index-1 to start from</param>
99 /// The index of the next non empty queue or -1 if all the queues are empty 97 /// <returns>
100 /// </returns> 98 /// The index of the next non empty queue or -1 if all the queues are empty
101 private int GetNextNonEmptyQueue(int queueIndex) 99 /// </returns>
102 { 100 private int GetNextNonEmptyQueue(int queueIndex)
103 for(int i = queueIndex+1; i < _queuesCount; ++i) 101 {
104 { 102 for(int i = queueIndex+1; i < _queuesCount; ++i)
105 if(_queues[i].Count > 0) 103 {
106 { 104 if(_queues[i].Count > 0)
107 return i; 105 {
108 } 106 return i;
109 } 107 }
110 return -1; 108 }
111 } 109 return -1;
112 110 }
113 /// <summary> 111
114 /// The number of work items 112 /// <summary>
115 /// </summary> 113 /// The number of work items
116 public int Count 114 /// </summary>
117 { 115 public int Count
118 get 116 {
119 { 117 get
120 return _workItemsCount; 118 {
121 } 119 return _workItemsCount;
122 } 120 }
123 121 }
124 /// <summary> 122
125 /// Clear all the work items 123 /// <summary>
126 /// </summary> 124 /// Clear all the work items
127 public void Clear() 125 /// </summary>
128 { 126 public void Clear()
129 if (_workItemsCount > 0) 127 {
130 { 128 if (_workItemsCount > 0)
131 foreach(Queue queue in _queues) 129 {
132 { 130 foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
133 queue.Clear(); 131 {
134 } 132 queue.Clear();
135 _workItemsCount = 0; 133 }
136 ++_version; 134 _workItemsCount = 0;
137 } 135 ++_version;
138 } 136 }
139 137 }
140 #endregion 138
141 139 #endregion
142 #region IEnumerable Members 140
143 141 #region IEnumerable Members
144 /// <summary> 142
145 /// Returns an enumerator to iterate over the work items 143 /// <summary>
146 /// </summary> 144 /// Returns an enumerator to iterate over the work items
147 /// <returns>Returns an enumerator</returns> 145 /// </summary>
148 public IEnumerator GetEnumerator() 146 /// <returns>Returns an enumerator</returns>
149 { 147 public IEnumerator GetEnumerator()
150 return new PriorityQueueEnumerator(this); 148 {
151 } 149 return new PriorityQueueEnumerator(this);
152 150 }
153 #endregion 151
154 152 #endregion
155 #region PriorityQueueEnumerator 153
156 154 #region PriorityQueueEnumerator
157 /// <summary> 155
158 /// The class the implements the enumerator 156 /// <summary>
159 /// </summary> 157 /// The class the implements the enumerator
160 private class PriorityQueueEnumerator : IEnumerator 158 /// </summary>
161 { 159 private class PriorityQueueEnumerator : IEnumerator
162 private PriorityQueue _priorityQueue; 160 {
163 private int _version; 161 private readonly PriorityQueue _priorityQueue;
164 private int _queueIndex; 162 private int _version;
165 private IEnumerator _enumerator; 163 private int _queueIndex;
166 164 private IEnumerator _enumerator;
167 public PriorityQueueEnumerator(PriorityQueue priorityQueue) 165
168 { 166 public PriorityQueueEnumerator(PriorityQueue priorityQueue)
169 _priorityQueue = priorityQueue; 167 {
170 _version = _priorityQueue._version; 168 _priorityQueue = priorityQueue;
171 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); 169 _version = _priorityQueue._version;
172 if (_queueIndex >= 0) 170 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
173 { 171 if (_queueIndex >= 0)
174 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); 172 {
175 } 173 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
176 else 174 }
177 { 175 else
178 _enumerator = null; 176 {
179 } 177 _enumerator = null;
180 } 178 }
181 179 }
182 #region IEnumerator Members 180
183 181 #region IEnumerator Members
184 public void Reset() 182
185 { 183 public void Reset()
186 _version = _priorityQueue._version; 184 {
187 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); 185 _version = _priorityQueue._version;
188 if (_queueIndex >= 0) 186 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
189 { 187 if (_queueIndex >= 0)
190 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); 188 {
191 } 189 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
192 else 190 }
193 { 191 else
194 _enumerator = null; 192 {
195 } 193 _enumerator = null;
196 } 194 }
197 195 }
198 public object Current 196
199 { 197 public object Current
200 get 198 {
201 { 199 get
202 Debug.Assert(null != _enumerator); 200 {
203 return _enumerator.Current; 201 Debug.Assert(null != _enumerator);
204 } 202 return _enumerator.Current;
205 } 203 }
206 204 }
207 public bool MoveNext() 205
208 { 206 public bool MoveNext()
209 if (null == _enumerator) 207 {
210 { 208 if (null == _enumerator)
211 return false; 209 {
212 } 210 return false;
213 211 }
214 if(_version != _priorityQueue._version) 212
215 { 213 if(_version != _priorityQueue._version)
216 throw new InvalidOperationException("The collection has been modified"); 214 {
217 215 throw new InvalidOperationException("The collection has been modified");
218 } 216
219 if (!_enumerator.MoveNext()) 217 }
220 { 218 if (!_enumerator.MoveNext())
221 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex); 219 {
222 if(-1 == _queueIndex) 220 _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex);
223 { 221 if(-1 == _queueIndex)
224 return false; 222 {
225 } 223 return false;
226 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); 224 }
227 _enumerator.MoveNext(); 225 _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
228 return true; 226 _enumerator.MoveNext();
229 } 227 return true;
230 return true; 228 }
231 } 229 return true;
232 230 }
233 #endregion 231
234 } 232 #endregion
235 233 }
236 #endregion 234
237 } 235 #endregion
238 236 }
239 #endregion 237
238 #endregion
240} 239}
diff --git a/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..4728c1f
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs
@@ -0,0 +1,23 @@
1using System.Reflection;
2using System.Runtime.CompilerServices;
3using 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..23a60bc
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/SLExt.cs
@@ -0,0 +1,16 @@
1#if _SILVERLIGHT
2
3using System.Threading;
4
5namespace 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..9b17f69
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs
@@ -0,0 +1,62 @@
1#if !(_WINDOWS_CE)
2
3using System;
4using System.Threading;
5
6namespace 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..0663d1d 100644
--- a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs
+++ b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs
@@ -1,354 +1,448 @@
1using System; 1using System;
2using System.Diagnostics; 2using System.Diagnostics;
3using System.Threading;
4
5namespace Amib.Threading
6{
7 public interface ISTPPerformanceCountersReader
8 {
9 long InUseThreads { get; }
10 long ActiveThreads { get; }
11 long WorkItemsQueued { get; }
12 long WorkItemsProcessed { get; }
13 }
14}
3 15
4namespace Amib.Threading.Internal 16namespace Amib.Threading.Internal
5{ 17{
6 internal enum STPPerformanceCounterType 18 internal interface ISTPInstancePerformanceCounters : IDisposable
7 { 19 {
8 // Fields 20 void Close();
9 ActiveThreads = 0, 21 void SampleThreads(long activeThreads, long inUseThreads);
10 InUseThreads = 1, 22 void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
11 OverheadThreads = 2, 23 void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
12 OverheadThreadsPercent = 3, 24 void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
13 OverheadThreadsPercentBase = 4, 25 }
26#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
14 27
15 WorkItems = 5, 28 internal enum STPPerformanceCounterType
16 WorkItemsInQueue = 6, 29 {
17 WorkItemsProcessed = 7, 30 // Fields
31 ActiveThreads = 0,
32 InUseThreads = 1,
33 OverheadThreads = 2,
34 OverheadThreadsPercent = 3,
35 OverheadThreadsPercentBase = 4,
18 36
19 WorkItemsQueuedPerSecond = 8, 37 WorkItems = 5,
20 WorkItemsProcessedPerSecond = 9, 38 WorkItemsInQueue = 6,
39 WorkItemsProcessed = 7,
21 40
22 AvgWorkItemWaitTime = 10, 41 WorkItemsQueuedPerSecond = 8,
23 AvgWorkItemWaitTimeBase = 11, 42 WorkItemsProcessedPerSecond = 9,
24 43
25 AvgWorkItemProcessTime = 12, 44 AvgWorkItemWaitTime = 10,
26 AvgWorkItemProcessTimeBase = 13, 45 AvgWorkItemWaitTimeBase = 11,
27 46
28 WorkItemsGroups = 14, 47 AvgWorkItemProcessTime = 12,
48 AvgWorkItemProcessTimeBase = 13,
29 49
30 LastCounter = 14, 50 WorkItemsGroups = 14,
31 }
32
33 51
34 /// <summary> 52 LastCounter = 14,
35 /// Summary description for STPPerformanceCounter. 53 }
36 /// </summary> 54
37 internal class STPPerformanceCounter
38 {
39 // Fields
40 private PerformanceCounterType _pcType;
41 protected string _counterHelp;
42 protected string _counterName;
43
44 // Methods
45 public STPPerformanceCounter(
46 string counterName,
47 string counterHelp,
48 PerformanceCounterType pcType)
49 {
50 this._counterName = counterName;
51 this._counterHelp = counterHelp;
52 this._pcType = pcType;
53 }
54
55 public void AddCounterToCollection(CounterCreationDataCollection counterData)
56 {
57 CounterCreationData counterCreationData = new CounterCreationData(
58 _counterName,
59 _counterHelp,
60 _pcType);
61 55
62 counterData.Add(counterCreationData); 56 /// <summary>
63 } 57 /// Summary description for STPPerformanceCounter.
58 /// </summary>
59 internal class STPPerformanceCounter
60 {
61 // Fields
62 private readonly PerformanceCounterType _pcType;
63 protected string _counterHelp;
64 protected string _counterName;
65
66 // Methods
67 public STPPerformanceCounter(
68 string counterName,
69 string counterHelp,
70 PerformanceCounterType pcType)
71 {
72 _counterName = counterName;
73 _counterHelp = counterHelp;
74 _pcType = pcType;
75 }
76
77 public void AddCounterToCollection(CounterCreationDataCollection counterData)
78 {
79 CounterCreationData counterCreationData = new CounterCreationData(
80 _counterName,
81 _counterHelp,
82 _pcType);
83
84 counterData.Add(counterCreationData);
85 }
86
87 // Properties
88 public string Name
89 {
90 get
91 {
92 return _counterName;
93 }
94 }
95 }
96
97 internal class STPPerformanceCounters
98 {
99 // Fields
100 internal STPPerformanceCounter[] _stpPerformanceCounters;
101 private static readonly STPPerformanceCounters _instance;
102 internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
103 internal const string _stpCategoryName = "SmartThreadPool";
104
105 // Methods
106 static STPPerformanceCounters()
107 {
108 _instance = new STPPerformanceCounters();
109 }
110
111 private STPPerformanceCounters()
112 {
113 STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[]
114 {
115 new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32),
116 new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32),
117 new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32),
118 new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction),
119 new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase),
120
121 new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32),
122 new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32),
123 new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32),
124
125 new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32),
126 new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32),
127
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 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
131 new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64),
132 new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase),
133
134 new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32),
135 };
136
137 _stpPerformanceCounters = stpPerformanceCounters;
138 SetupCategory();
139 }
140
141 private void SetupCategory()
142 {
143 if (!PerformanceCounterCategory.Exists(_stpCategoryName))
144 {
145 CounterCreationDataCollection counters = new CounterCreationDataCollection();
146
147 for (int i = 0; i < _stpPerformanceCounters.Length; i++)
148 {
149 _stpPerformanceCounters[i].AddCounterToCollection(counters);
150 }
151
152 PerformanceCounterCategory.Create(
153 _stpCategoryName,
154 _stpCategoryHelp,
155 PerformanceCounterCategoryType.MultiInstance,
156 counters);
157
158 }
159 }
64 160
65 // Properties 161 // Properties
66 public string Name 162 public static STPPerformanceCounters Instance
163 {
164 get
165 {
166 return _instance;
167 }
168 }
169 }
170
171 internal class STPInstancePerformanceCounter : IDisposable
172 {
173 // Fields
174 private bool _isDisposed;
175 private PerformanceCounter _pcs;
176
177 // Methods
178 protected STPInstancePerformanceCounter()
179 {
180 _isDisposed = false;
181 }
182
183 public STPInstancePerformanceCounter(
184 string instance,
185 STPPerformanceCounterType spcType) : this()
186 {
187 STPPerformanceCounters counters = STPPerformanceCounters.Instance;
188 _pcs = new PerformanceCounter(
189 STPPerformanceCounters._stpCategoryName,
190 counters._stpPerformanceCounters[(int) spcType].Name,
191 instance,
192 false);
193 _pcs.RawValue = _pcs.RawValue;
194 }
195
196
197 public void Close()
198 {
199 if (_pcs != null)
200 {
201 _pcs.RemoveInstance();
202 _pcs.Close();
203 _pcs = null;
204 }
205 }
206
207 public void Dispose()
208 {
209 Dispose(true);
210 }
211
212 public virtual void Dispose(bool disposing)
67 { 213 {
68 get 214 if (!_isDisposed)
69 { 215 {
70 return _counterName; 216 if (disposing)
217 {
218 Close();
219 }
71 } 220 }
72 } 221 _isDisposed = true;
73 }
74
75 internal class STPPerformanceCounters
76 {
77 // Fields
78 internal STPPerformanceCounter[] _stpPerformanceCounters;
79 private static STPPerformanceCounters _instance;
80 internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
81 internal const string _stpCategoryName = "SmartThreadPool";
82
83 // Methods
84 static STPPerformanceCounters()
85 {
86 _instance = new STPPerformanceCounters();
87 } 222 }
88 223
89 private STPPerformanceCounters() 224 public virtual void Increment()
90 { 225 {
91 STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] 226 _pcs.Increment();
92 { 227 }
93 new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), 228
94 new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), 229 public virtual void IncrementBy(long val)
95 new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), 230 {
96 new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), 231 _pcs.IncrementBy(val);
97 new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), 232 }
98 233
99 new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), 234 public virtual void Set(long val)
100 new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), 235 {
101 new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), 236 _pcs.RawValue = val;
102 237 }
103 new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), 238 }
104 new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), 239
105 240 internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
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), 241 {
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), 242 // Methods
243 public override void Increment() {}
244 public override void IncrementBy(long value) {}
245 public override void Set(long val) {}
246 }
247
248
249
250 internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters
251 {
252 private bool _isDisposed;
253 // Fields
254 private STPInstancePerformanceCounter[] _pcs;
255 private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
256
257 // Methods
258 static STPInstancePerformanceCounters()
259 {
260 _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter();
261 }
262
263 public STPInstancePerformanceCounters(string instance)
264 {
265 _isDisposed = false;
266 _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
267
268 // Call the STPPerformanceCounters.Instance so the static constructor will
269 // intialize the STPPerformanceCounters singleton.
270 STPPerformanceCounters.Instance.GetHashCode();
271
272 for (int i = 0; i < _pcs.Length; i++)
273 {
274 if (instance != null)
275 {
276 _pcs[i] = new STPInstancePerformanceCounter(
277 instance,
278 (STPPerformanceCounterType) i);
279 }
280 else
281 {
282 _pcs[i] = _stpInstanceNullPerformanceCounter;
283 }
284 }
285 }
286
108 287
109 new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), 288 public void Close()
110 new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), 289 {
290 if (null != _pcs)
291 {
292 for (int i = 0; i < _pcs.Length; i++)
293 {
294 if (null != _pcs[i])
295 {
296 _pcs[i].Dispose();
297 }
298 }
299 _pcs = null;
300 }
301 }
111 302
112 new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), 303 public void Dispose()
113 }; 304 {
305 Dispose(true);
306 }
114 307
115 _stpPerformanceCounters = stpPerformanceCounters; 308 public virtual void Dispose(bool disposing)
116 SetupCategory();
117 }
118
119 private void SetupCategory()
120 { 309 {
121 if (!PerformanceCounterCategory.Exists(_stpCategoryName)) 310 if (!_isDisposed)
122 { 311 {
123 CounterCreationDataCollection counters = new CounterCreationDataCollection(); 312 if (disposing)
124
125 for (int i = 0; i < _stpPerformanceCounters.Length; i++)
126 { 313 {
127 _stpPerformanceCounters[i].AddCounterToCollection(counters); 314 Close();
128 } 315 }
129
130
131 // *********** Remark for .NET 2.0 ***********
132 // If you are here, it means you got the warning that this overload
133 // of the method is deprecated in .NET 2.0. To use the correct
134 // method overload, uncomment the third argument of
135 // the method.
136 #pragma warning disable 0618
137 PerformanceCounterCategory.Create(
138 _stpCategoryName,
139 _stpCategoryHelp,
140 //PerformanceCounterCategoryType.MultiInstance,
141 counters);
142 #pragma warning restore 0618
143 } 316 }
317 _isDisposed = true;
144 } 318 }
145 319
146 // Properties 320 private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
147 public static STPPerformanceCounters Instance 321 {
148 { 322 return _pcs[(int) spcType];
149 get 323 }
150 { 324
151 return _instance; 325 public void SampleThreads(long activeThreads, long inUseThreads)
152 } 326 {
153 } 327 GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads);
154 } 328 GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads);
329 GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads);
330
331 GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads);
332 GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads);
333 }
334
335 public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
336 {
337 GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed);
338 GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued);
339 GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed);
340
341 GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued);
342 GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed);
343 }
344
345 public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
346 {
347 GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds);
348 GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment();
349 }
350
351 public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
352 {
353 GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds);
354 GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
355 }
356 }
357#endif
155 358
156 internal class STPInstancePerformanceCounter : IDisposable 359 internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
157 { 360 {
158 // Fields 361 private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();
159 private PerformanceCounter _pcs;
160 362
161 // Methods 363 public static NullSTPInstancePerformanceCounters Instance
162 protected STPInstancePerformanceCounter() 364 {
163 { 365 get { return _instance; }
164 } 366 }
165 367
166 public STPInstancePerformanceCounter( 368 public void Close() {}
167 string instance, 369 public void Dispose() {}
168 STPPerformanceCounterType spcType) 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
169 { 376 {
170 STPPerformanceCounters counters = STPPerformanceCounters.Instance; 377 get { return 0; }
171 _pcs = new PerformanceCounter(
172 STPPerformanceCounters._stpCategoryName,
173 counters._stpPerformanceCounters[(int) spcType].Name,
174 instance,
175 false);
176 _pcs.RawValue = _pcs.RawValue;
177 } 378 }
178 379
179 ~STPInstancePerformanceCounter() 380 public long ActiveThreads
180 { 381 {
181 Close(); 382 get { return 0; }
182 } 383 }
183 384
184 public void Close() 385 public long WorkItemsQueued
185 {
186 if (_pcs != null)
187 {
188 _pcs.RemoveInstance();
189 _pcs.Close();
190 _pcs = null;
191 }
192 }
193
194 public void Dispose()
195 { 386 {
196 Close(); 387 get { return 0; }
197 GC.SuppressFinalize(this);
198 }
199
200 public virtual void Increment()
201 {
202 _pcs.Increment();
203 }
204
205 public virtual void IncrementBy(long val)
206 {
207 _pcs.IncrementBy(val);
208 } 388 }
209 389
210 public virtual void Set(long val) 390 public long WorkItemsProcessed
211 { 391 {
212 _pcs.RawValue = val; 392 get { return 0; }
213 } 393 }
214 } 394 }
215 395
216 internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter 396 internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
217 { 397 {
218 // Methods 398 public void Close() { }
219 public STPInstanceNullPerformanceCounter() {} 399 public void Dispose() { }
220 public override void Increment() {}
221 public override void IncrementBy(long value) {}
222 public override void Set(long val) {}
223 }
224
225 internal interface ISTPInstancePerformanceCounters : IDisposable
226 {
227 void Close();
228 void SampleThreads(long activeThreads, long inUseThreads);
229 void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
230 void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
231 void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
232 }
233 400
401 private long _activeThreads;
402 private long _inUseThreads;
403 private long _workItemsQueued;
404 private long _workItemsProcessed;
234 405
235 internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable 406 public long InUseThreads
236 {
237 // Fields
238 private STPInstancePerformanceCounter[] _pcs;
239 private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
240
241 // Methods
242 static STPInstancePerformanceCounters()
243 { 407 {
244 _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter(); 408 get { return _inUseThreads; }
245 }
246
247 public STPInstancePerformanceCounters(string instance)
248 {
249 _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
250 // STPPerformanceCounters counters = STPPerformanceCounters.Instance;
251 for (int i = 0; i < _pcs.Length; i++)
252 {
253 if (instance != null)
254 {
255 _pcs[i] = new STPInstancePerformanceCounter(
256 instance,
257 (STPPerformanceCounterType) i);
258 }
259 else
260 {
261 _pcs[i] = _stpInstanceNullPerformanceCounter;
262 }
263 }
264 } 409 }
265
266 410
267 public void Close() 411 public long ActiveThreads
268 { 412 {
269 if (null != _pcs) 413 get { return _activeThreads; }
270 {
271 for (int i = 0; i < _pcs.Length; i++)
272 {
273 if (null != _pcs[i])
274 {
275 _pcs[i].Close();
276 }
277 }
278 _pcs = null;
279 }
280 } 414 }
281 415
282 ~STPInstancePerformanceCounters() 416 public long WorkItemsQueued
283 { 417 {
284 Close(); 418 get { return _workItemsQueued; }
285 } 419 }
286 420
287 public void Dispose() 421 public long WorkItemsProcessed
288 {
289 Close();
290 GC.SuppressFinalize(this);
291 }
292
293 private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
294 { 422 {
295 return _pcs[(int) spcType]; 423 get { return _workItemsProcessed; }
296 } 424 }
297 425
298 public void SampleThreads(long activeThreads, long inUseThreads) 426 public void SampleThreads(long activeThreads, long inUseThreads)
299 { 427 {
300 GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads); 428 _activeThreads = activeThreads;
301 GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads); 429 _inUseThreads = inUseThreads;
302 GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads);
303
304 GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads);
305 GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads);
306 } 430 }
307 431
308 public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) 432 public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
309 { 433 {
310 GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed); 434 _workItemsQueued = workItemsQueued;
311 GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued); 435 _workItemsProcessed = workItemsProcessed;
312 GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed);
313
314 GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued);
315 GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed);
316 } 436 }
317 437
318 public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) 438 public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
319 { 439 {
320 GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds); 440 // Not supported
321 GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment();
322 } 441 }
323 442
324 public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) 443 public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
325 { 444 {
326 GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds); 445 // Not supported
327 GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
328 }
329 }
330
331 internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
332 {
333 static NullSTPInstancePerformanceCounters()
334 {
335 } 446 }
336
337 private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null);
338
339 public static NullSTPInstancePerformanceCounters Instance
340 {
341 get { return _instance; }
342 }
343
344 public NullSTPInstancePerformanceCounters(string instance) {}
345 public void Close() {}
346 public void Dispose() {}
347
348 public void SampleThreads(long activeThreads, long inUseThreads) {}
349 public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
350 public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
351 public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
352 } 447 }
353
354} 448}
diff --git a/ThirdParty/SmartThreadPool/STPStartInfo.cs b/ThirdParty/SmartThreadPool/STPStartInfo.cs
index fa9ceb4..96fa094 100644
--- a/ThirdParty/SmartThreadPool/STPStartInfo.cs
+++ b/ThirdParty/SmartThreadPool/STPStartInfo.cs
@@ -1,113 +1,212 @@
1// Ami Bar 1using System;
2// amibar@gmail.com
3
4using System.Threading; 2using System.Threading;
5 3
6namespace Amib.Threading 4namespace Amib.Threading
7{ 5{
8 /// <summary> 6 /// <summary>
9 /// Summary description for STPStartInfo. 7 /// Summary description for STPStartInfo.
10 /// </summary> 8 /// </summary>
11 public class STPStartInfo : WIGStartInfo 9 public class STPStartInfo : WIGStartInfo
12 { 10 {
13 /// <summary> 11 private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
14 /// Idle timeout in milliseconds. 12 private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
15 /// If a thread is idle for _idleTimeout milliseconds then 13 private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
16 /// it may quit. 14#if !(WINDOWS_PHONE)
17 /// </summary> 15 private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
18 private int _idleTimeout; 16#endif
19 17 private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
20 /// <summary> 18 private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground;
21 /// The lower limit of threads in the pool. 19 private bool _enableLocalPerformanceCounters;
22 /// </summary> 20 private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName;
23 private int _minWorkerThreads; 21 private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize;
24 22
25 /// <summary> 23 public STPStartInfo()
26 /// The upper limit of threads in the pool.
27 /// </summary>
28 private int _maxWorkerThreads;
29
30 /// <summary>
31 /// The priority of the threads in the pool
32 /// </summary>
33 private ThreadPriority _threadPriority;
34
35 /// <summary>
36 /// The thread pool name. Threads will get names depending on this.
37 /// </summary>
38 private string _threadPoolName;
39
40 /// <summary>
41 /// If this field is not null then the performance counters are enabled
42 /// and use the string as the name of the instance.
43 /// </summary>
44 private string _pcInstanceName;
45
46 private int _stackSize;
47
48 public STPStartInfo() : base()
49 { 24 {
25 _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
26#if !(WINDOWS_PHONE)
27 _threadPriority = SmartThreadPool.DefaultThreadPriority;
28#endif
29 _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
50 _idleTimeout = SmartThreadPool.DefaultIdleTimeout; 30 _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
51 _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; 31 _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
52 _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
53 _threadPriority = SmartThreadPool.DefaultThreadPriority;
54 _threadPoolName = SmartThreadPool.DefaultThreadPoolName;
55 _pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
56 _stackSize = SmartThreadPool.DefaultStackSize;
57 } 32 }
58 33
59 public STPStartInfo(STPStartInfo stpStartInfo) : base(stpStartInfo) 34 public STPStartInfo(STPStartInfo stpStartInfo)
35 : base(stpStartInfo)
60 { 36 {
61 _idleTimeout = stpStartInfo._idleTimeout; 37 _idleTimeout = stpStartInfo.IdleTimeout;
62 _minWorkerThreads = stpStartInfo._minWorkerThreads; 38 _minWorkerThreads = stpStartInfo.MinWorkerThreads;
63 _maxWorkerThreads = stpStartInfo._maxWorkerThreads; 39 _maxWorkerThreads = stpStartInfo.MaxWorkerThreads;
64 _threadPriority = stpStartInfo._threadPriority; 40#if !(WINDOWS_PHONE)
41 _threadPriority = stpStartInfo.ThreadPriority;
42#endif
43 _performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName;
44 _enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters;
65 _threadPoolName = stpStartInfo._threadPoolName; 45 _threadPoolName = stpStartInfo._threadPoolName;
66 _pcInstanceName = stpStartInfo._pcInstanceName; 46 _areThreadsBackground = stpStartInfo.AreThreadsBackground;
67 _stackSize = stpStartInfo._stackSize; 47#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
48 _apartmentState = stpStartInfo._apartmentState;
49#endif
68 } 50 }
69 51
70 public int IdleTimeout 52 /// <summary>
71 { 53 /// Get/Set the idle timeout in milliseconds.
72 get { return _idleTimeout; } 54 /// If a thread is idle (starved) longer than IdleTimeout then it may quit.
73 set { _idleTimeout = value; } 55 /// </summary>
56 public virtual int IdleTimeout
57 {
58 get { return _idleTimeout; }
59 set
60 {
61 ThrowIfReadOnly();
62 _idleTimeout = value;
63 }
64 }
65
66
67 /// <summary>
68 /// Get/Set the lower limit of threads in the pool.
69 /// </summary>
70 public virtual int MinWorkerThreads
71 {
72 get { return _minWorkerThreads; }
73 set
74 {
75 ThrowIfReadOnly();
76 _minWorkerThreads = value;
77 }
78 }
79
80
81 /// <summary>
82 /// Get/Set the upper limit of threads in the pool.
83 /// </summary>
84 public virtual int MaxWorkerThreads
85 {
86 get { return _maxWorkerThreads; }
87 set
88 {
89 ThrowIfReadOnly();
90 _maxWorkerThreads = value;
91 }
92 }
93
94#if !(WINDOWS_PHONE)
95 /// <summary>
96 /// Get/Set the scheduling priority of the threads in the pool.
97 /// The Os handles the scheduling.
98 /// </summary>
99 public virtual ThreadPriority ThreadPriority
100 {
101 get { return _threadPriority; }
102 set
103 {
104 ThrowIfReadOnly();
105 _threadPriority = value;
106 }
107 }
108#endif
109 /// <summary>
110 /// Get/Set the thread pool name. Threads will get names depending on this.
111 /// </summary>
112 public virtual string ThreadPoolName {
113 get { return _threadPoolName; }
114 set
115 {
116 ThrowIfReadOnly ();
117 _threadPoolName = value;
118 }
74 } 119 }
75 120
76 public int MinWorkerThreads 121 /// <summary>
77 { 122 /// Get/Set the performance counter instance name of this SmartThreadPool
78 get { return _minWorkerThreads; } 123 /// The default is null which indicate not to use performance counters at all.
79 set { _minWorkerThreads = value; } 124 /// </summary>
80 } 125 public virtual string PerformanceCounterInstanceName
126 {
127 get { return _performanceCounterInstanceName; }
128 set
129 {
130 ThrowIfReadOnly();
131 _performanceCounterInstanceName = value;
132 }
133 }
81 134
82 public int MaxWorkerThreads 135 /// <summary>
83 { 136 /// Enable/Disable the local performance counter.
84 get { return _maxWorkerThreads; } 137 /// This enables the user to get some performance information about the SmartThreadPool
85 set { _maxWorkerThreads = value; } 138 /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.)
86 } 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 }
87 150
88 public ThreadPriority ThreadPriority 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()
89 { 169 {
90 get { return _threadPriority; } 170 return new STPStartInfo(this) { _readOnly = true };
91 set { _threadPriority = value; }
92 } 171 }
93 172
94 public virtual string ThreadPoolName 173#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
95 {
96 get { return _threadPoolName; }
97 set { _threadPoolName = value; }
98 }
99 174
175 private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState;
100 176
101 public string PerformanceCounterInstanceName 177 /// <summary>
178 /// Get/Set the apartment state of threads in the thread pool
179 /// </summary>
180 public ApartmentState ApartmentState
102 { 181 {
103 get { return _pcInstanceName; } 182 get { return _apartmentState; }
104 set { _pcInstanceName = value; } 183 set
105 } 184 {
106 185 ThrowIfReadOnly();
107 public int StackSize 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
108 { 196 {
109 get { return _stackSize; } 197 get { return _maxStackSize; }
110 set { _stackSize = value; } 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 }
111 } 207 }
208#endif
209
210#endif
112 } 211 }
113} 212}
diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs
new file mode 100644
index 0000000..d9502bb
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs
@@ -0,0 +1,60 @@
1
2using System;
3using Amib.Threading.Internal;
4
5namespace 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..a4f4ce5 100644
--- a/ThirdParty/SmartThreadPool/SmartThreadPool.cs
+++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs
@@ -1,61 +1,106 @@
1// Ami Bar 1#region Release History
2// amibar@gmail.com 2
3// 3// Smart Thread Pool
4// Smart thread pool in C#.
5// 7 Aug 2004 - Initial release 4// 7 Aug 2004 - Initial release
5//
6// 14 Sep 2004 - Bug fixes 6// 14 Sep 2004 - Bug fixes
7//
7// 15 Oct 2004 - Added new features 8// 15 Oct 2004 - Added new features
8// - Work items return result. 9// - Work items return result.
9// - Support waiting synchronization for multiple work items. 10// - Support waiting synchronization for multiple work items.
10// - Work items can be cancelled. 11// - Work items can be cancelled.
11// - Passage of the caller thread’s context to the thread in the pool. 12// - Passage of the caller thread’s context to the thread in the pool.
12// - Minimal usage of WIN32 handles. 13// - Minimal usage of WIN32 handles.
13// - Minor bug fixes. 14// - Minor bug fixes.
15//
14// 26 Dec 2004 - Changes: 16// 26 Dec 2004 - Changes:
15// - Removed static constructors. 17// - Removed static constructors.
16// - Added finalizers. 18// - Added finalizers.
17// - Changed Exceptions so they are serializable. 19// - Changed Exceptions so they are serializable.
18// - Fixed the bug in one of the SmartThreadPool constructors. 20// - Fixed the bug in one of the SmartThreadPool constructors.
19// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. 21// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters.
20// The SmartThreadPool.WaitAny() is still limited by the .NET Framework. 22// The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
21// - Added PostExecute with options on which cases to call it. 23// - Added PostExecute with options on which cases to call it.
22// - Added option to dispose of the state objects. 24// - Added option to dispose of the state objects.
23// - Added a WaitForIdle() method that waits until the work items queue is empty. 25// - Added a WaitForIdle() method that waits until the work items queue is empty.
24// - Added an STPStartInfo class for the initialization of the thread pool. 26// - Added an STPStartInfo class for the initialization of the thread pool.
25// - Changed exception handling so if a work item throws an exception it 27// - Changed exception handling so if a work item throws an exception it
26// is rethrown at GetResult(), rather then firing an UnhandledException event. 28// is rethrown at GetResult(), rather then firing an UnhandledException event.
27// Note that PostExecute exception are always ignored. 29// Note that PostExecute exception are always ignored.
30//
28// 25 Mar 2005 - Changes: 31// 25 Mar 2005 - Changes:
29// - Fixed lost of work items bug 32// - Fixed lost of work items bug
33//
30// 3 Jul 2005: Changes. 34// 3 Jul 2005: Changes.
31// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. 35// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed.
36//
32// 16 Aug 2005: Changes. 37// 16 Aug 2005: Changes.
33// - Fixed bug where the InUseThreads becomes negative when canceling work items. 38// - Fixed bug where the InUseThreads becomes negative when canceling work items.
34// 39//
35// 31 Jan 2006 - Changes: 40// 31 Jan 2006 - Changes:
36// - Added work items priority 41// - Added work items priority
37// - Removed support of chained delegates in callbacks and post executes (nobody really use this) 42// - Removed support of chained delegates in callbacks and post executes (nobody really use this)
38// - Added work items groups 43// - Added work items groups
39// - Added work items groups idle event 44// - Added work items groups idle event
40// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array 45// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array
41// it returns true rather then throwing an exception. 46// it returns true rather then throwing an exception.
42// - Added option to start the STP and the WIG as suspended 47// - Added option to start the STP and the WIG as suspended
43// - Exception behavior changed, the real exception is returned by an 48// - Exception behavior changed, the real exception is returned by an
44// inner exception 49// inner exception
45// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) 50// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.)
46// - Added performance counters 51// - Added performance counters
47// - Added priority to the threads in the pool 52// - Added priority to the threads in the pool
48// 53//
49// 13 Feb 2006 - Changes: 54// 13 Feb 2006 - Changes:
50// - Added a call to the dispose of the Performance Counter so 55// - Added a call to the dispose of the Performance Counter so
51// their won't be a Performance Counter leak. 56// their won't be a Performance Counter leak.
52// - Added exception catch in case the Performance Counters cannot 57// - Added exception catch in case the Performance Counters cannot
53// be created. 58// be created.
59//
60// 17 May 2008 - Changes:
61// - Changed the dispose behavior and removed the Finalizers.
62// - Enabled the change of the MaxThreads and MinThreads at run time.
63// - Enabled the change of the Concurrency of a IWorkItemsGroup at run
64// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency
65// refers to the MaxThreads.
66// - Improved the cancel behavior.
67// - Added events for thread creation and termination.
68// - Fixed the HttpContext context capture.
69// - Changed internal collections so they use generic collections
70// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup
71// - Added support for WinCE
72// - Added support for Action<T> and Func<T>
73//
74// 07 April 2009 - Changes:
75// - Added support for Silverlight and Mono
76// - Added Join, Choice, and Pipe to SmartThreadPool.
77// - Added local performance counters (for Mono, Silverlight, and WindowsCE)
78// - Changed duration measures from DateTime.Now to Stopwatch.
79// - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList<T>.
80//
81// 21 December 2009 - Changes:
82// - Added work item timeout (passive)
83//
84// 20 August 2012 - Changes:
85// - Added set name to threads
86// - Fixed the WorkItemsQueue.Dequeue.
87// Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... }
88// - Fixed SmartThreadPool.Pipe
89// - Added IsBackground option to threads
90// - Added ApartmentState to threads
91// - Fixed thread creation when queuing many work items at the same time.
92//
93// 24 August 2012 - Changes:
94// - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan
95// - Added option to set MaxStackSize of threads
96
97#endregion
54 98
55using System; 99using System;
56using System.Security; 100using System.Security;
57using System.Threading; 101using System.Threading;
58using System.Collections; 102using System.Collections;
103using System.Collections.Generic;
59using System.Diagnostics; 104using System.Diagnostics;
60using System.Runtime.CompilerServices; 105using System.Runtime.CompilerServices;
61 106
@@ -63,1213 +108,1503 @@ using Amib.Threading.Internal;
63 108
64namespace Amib.Threading 109namespace Amib.Threading
65{ 110{
66 #region SmartThreadPool class 111 #region SmartThreadPool class
67 /// <summary> 112 /// <summary>
68 /// Smart thread pool class. 113 /// Smart thread pool class.
69 /// </summary> 114 /// </summary>
70 public class SmartThreadPool : IWorkItemsGroup, IDisposable 115 public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable
71 { 116 {
72 #region Default Constants 117 #region Public Default Constants
73 118
74 /// <summary> 119 /// <summary>
75 /// Default minimum number of threads the thread pool contains. (0) 120 /// Default minimum number of threads the thread pool contains. (0)
76 /// </summary> 121 /// </summary>
77 public const int DefaultMinWorkerThreads = 0; 122 public const int DefaultMinWorkerThreads = 0;
78 123
79 /// <summary> 124 /// <summary>
80 /// Default maximum number of threads the thread pool contains. (25) 125 /// Default maximum number of threads the thread pool contains. (25)
81 /// </summary> 126 /// </summary>
82 public const int DefaultMaxWorkerThreads = 25; 127 public const int DefaultMaxWorkerThreads = 25;
83 128
84 /// <summary> 129 /// <summary>
85 /// Default idle timeout in milliseconds. (One minute) 130 /// Default idle timeout in milliseconds. (One minute)
131 /// </summary>
132 public const int DefaultIdleTimeout = 60*1000; // One minute
133
134 /// <summary>
135 /// Indicate to copy the security context of the caller and then use it in the call. (false)
136 /// </summary>
137 public const bool DefaultUseCallerCallContext = false;
138
139 /// <summary>
140 /// Indicate to copy the HTTP context of the caller and then use it in the call. (false)
141 /// </summary>
142 public const bool DefaultUseCallerHttpContext = false;
143
144 /// <summary>
145 /// Indicate to dispose of the state objects if they support the IDispose interface. (false)
146 /// </summary>
147 public const bool DefaultDisposeOfStateObjects = false;
148
149 /// <summary>
150 /// The default option to run the post execute (CallToPostExecute.Always)
151 /// </summary>
152 public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always;
153
154 /// <summary>
155 /// The default post execute method to run. (None)
156 /// When null it means not to call it.
157 /// </summary>
158 public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback;
159
160 /// <summary>
161 /// The default work item priority (WorkItemPriority.Normal)
162 /// </summary>
163 public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;
164
165 /// <summary>
166 /// The default is to work on work items as soon as they arrive
167 /// and not to wait for the start. (false)
168 /// </summary>
169 public const bool DefaultStartSuspended = false;
170
171 /// <summary>
172 /// The default name to use for the performance counters instance. (null)
173 /// </summary>
174 public static readonly string DefaultPerformanceCounterInstanceName;
175
176#if !(WINDOWS_PHONE)
177
178 /// <summary>
179 /// The default thread priority (ThreadPriority.Normal)
180 /// </summary>
181 public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
182#endif
183 /// <summary>
184 /// The default thread pool name. (SmartThreadPool)
86 /// </summary> 185 /// </summary>
87 public const int DefaultIdleTimeout = 60*1000; // One minute 186 public const string DefaultThreadPoolName = "SmartThreadPool";
88 187
89 /// <summary> 188 /// <summary>
90 /// Indicate to copy the security context of the caller and then use it in the call. (false) 189 /// The default Max Stack Size. (SmartThreadPool)
91 /// </summary> 190 /// </summary>
92 public const bool DefaultUseCallerCallContext = false; 191 public static readonly int? DefaultMaxStackSize = null;
93 192
94 /// <summary> 193 /// <summary>
95 /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) 194 /// The default fill state with params. (false)
195 /// It is relevant only to QueueWorkItem of Action&lt;...&gt;/Func&lt;...&gt;
96 /// </summary> 196 /// </summary>
97 public const bool DefaultUseCallerHttpContext = false; 197 public const bool DefaultFillStateWithArgs = false;
98 198
99 /// <summary> 199 /// <summary>
100 /// Indicate to dispose of the state objects if they support the IDispose interface. (false) 200 /// The default thread backgroundness. (true)
101 /// </summary> 201 /// </summary>
102 public const bool DefaultDisposeOfStateObjects = false; 202 public const bool DefaultAreThreadsBackground = true;
103 203
204#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
104 /// <summary> 205 /// <summary>
105 /// The default option to run the post execute 206 /// The default apartment state of a thread in the thread pool.
207 /// The default is ApartmentState.Unknown which means the STP will not
208 /// set the apartment of the thread. It will use the .NET default.
106 /// </summary> 209 /// </summary>
107 public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; 210 public const ApartmentState DefaultApartmentState = ApartmentState.Unknown;
211#endif
108 212
109 /// <summary> 213 #endregion
110 /// The default post execute method to run.
111 /// When null it means not to call it.
112 /// </summary>
113 public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null;
114 214
115 /// <summary> 215 #region Member Variables
116 /// The default work item priority
117 /// </summary>
118 public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;
119 216
120 /// <summary> 217 /// <summary>
121 /// The default is to work on work items as soon as they arrive 218 /// Dictionary of all the threads in the thread pool.
122 /// and not to wait for the start. 219 /// </summary>
123 /// </summary> 220 private readonly SynchronizedDictionary<Thread, ThreadEntry> _workerThreads = new SynchronizedDictionary<Thread, ThreadEntry>();
124 public const bool DefaultStartSuspended = false;
125 221
126 /// <summary> 222 /// <summary>
127 /// The default is not to use the performance counters 223 /// Queue of work items.
128 /// </summary> 224 /// </summary>
129 public static readonly string DefaultPerformanceCounterInstanceName = null; 225 private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue();
130 226
131 public static readonly int DefaultStackSize = 0; 227 /// <summary>
228 /// Count the work items handled.
229 /// Used by the performance counter.
230 /// </summary>
231 private int _workItemsProcessed;
132 232
133 /// <summary> 233 /// <summary>
134 /// The default thread priority 234 /// Number of threads that currently work (not idle).
135 /// </summary> 235 /// </summary>
136 public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; 236 private int _inUseWorkerThreads;
137 237
138 /// <summary> 238 /// <summary>
139 /// The default thread pool name 239 /// Stores a copy of the original STPStartInfo.
240 /// It is used to change the MinThread and MaxThreads
140 /// </summary> 241 /// </summary>
141 public const string DefaultThreadPoolName = "SmartThreadPool"; 242 private STPStartInfo _stpStartInfo;
142 243
143 #endregion 244 /// <summary>
245 /// Total number of work items that are stored in the work items queue
246 /// plus the work items that the threads in the pool are working on.
247 /// </summary>
248 private int _currentWorkItemsCount;
144 249
145 #region Member Variables 250 /// <summary>
251 /// Signaled when the thread pool is idle, i.e. no thread is busy
252 /// and the work items queue is empty
253 /// </summary>
254 //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
255 private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
146 256
147 /// <summary> 257 /// <summary>
148 /// Contains the name of this instance of SmartThreadPool. 258 /// An event to signal all the threads to quit immediately.
149 /// Can be changed by the user. 259 /// </summary>
150 /// </summary> 260 //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
151 private string _name = DefaultThreadPoolName; 261 private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false);
152 262
153 /// <summary> 263 /// <summary>
154 /// Hashtable of all the threads in the thread pool. 264 /// A flag to indicate if the Smart Thread Pool is now suspended.
155 /// </summary> 265 /// </summary>
156 private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable()); 266 private bool _isSuspended;
157 267
158 /// <summary> 268 /// <summary>
159 /// Queue of work items. 269 /// A flag to indicate the threads to quit.
160 /// </summary> 270 /// </summary>
161 private WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); 271 private bool _shutdown;
162 272
163 /// <summary> 273 /// <summary>
164 /// Count the work items handled. 274 /// Counts the threads created in the pool.
165 /// Used by the performance counter. 275 /// It is used to name the threads.
166 /// </summary> 276 /// </summary>
167 private long _workItemsProcessed = 0; 277 private int _threadCounter;
168 278
169 /// <summary> 279 /// <summary>
170 /// Number of threads that currently work (not idle). 280 /// Indicate that the SmartThreadPool has been disposed
171 /// </summary> 281 /// </summary>
172 private int _inUseWorkerThreads = 0; 282 private bool _isDisposed;
173 283
174 /// <summary> 284 /// <summary>
175 /// Start information to use. 285 /// Holds all the WorkItemsGroup instaces that have at least one
176 /// It is simpler than providing many constructors. 286 /// work item int the SmartThreadPool
177 /// </summary> 287 /// This variable is used in case of Shutdown
178 private STPStartInfo _stpStartInfo = new STPStartInfo(); 288 /// </summary>
289 private readonly SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup> _workItemsGroups = new SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup>();
179 290
180 /// <summary> 291 /// <summary>
181 /// Total number of work items that are stored in the work items queue 292 /// A common object for all the work items int the STP
182 /// plus the work items that the threads in the pool are working on. 293 /// so we can mark them to cancel in O(1)
183 /// </summary> 294 /// </summary>
184 private int _currentWorkItemsCount = 0; 295 private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup();
185 296
186 /// <summary> 297 /// <summary>
187 /// Signaled when the thread pool is idle, i.e. no thread is busy 298 /// Windows STP performance counters
188 /// and the work items queue is empty
189 /// </summary> 299 /// </summary>
190 private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); 300 private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance;
191 301
192 /// <summary> 302 /// <summary>
193 /// An event to signal all the threads to quit immediately. 303 /// Local STP performance counters
194 /// </summary> 304 /// </summary>
195 private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); 305 private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance;
196 306
197 /// <summary>
198 /// A flag to indicate the threads to quit.
199 /// </summary>
200 private bool _shutdown = false;
201 307
202 /// <summary> 308#if (WINDOWS_PHONE)
203 /// Counts the threads created in the pool. 309 private static readonly Dictionary<int, ThreadEntry> _threadEntries = new Dictionary<int, ThreadEntry>();
204 /// It is used to name the threads. 310#elif (_WINDOWS_CE)
205 /// </summary> 311 private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot();
206 private int _threadCounter = 0; 312#else
313 [ThreadStatic]
314 private static ThreadEntry _threadEntry;
207 315
208 /// <summary> 316#endif
209 /// Indicate that the SmartThreadPool has been disposed
210 /// </summary>
211 private bool _isDisposed = false;
212 317
213 /// <summary> 318 /// <summary>
214 /// Event to send that the thread pool is idle 319 /// An event to call after a thread is created, but before
320 /// it's first use.
215 /// </summary> 321 /// </summary>
216 private event EventHandler _stpIdle; 322 private event ThreadInitializationHandler _onThreadInitialization;
217 323
218 /// <summary> 324 /// <summary>
219 /// On idle event 325 /// An event to call when a thread is about to exit, after
326 /// it is no longer belong to the pool.
220 /// </summary> 327 /// </summary>
221 //private event WorkItemsGroupIdleHandler _onIdle; 328 private event ThreadTerminationHandler _onThreadTermination;
222 329
223 /// <summary> 330 #endregion
224 /// Holds all the WorkItemsGroup instaces that have at least one
225 /// work item int the SmartThreadPool
226 /// This variable is used in case of Shutdown
227 /// </summary>
228 private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable());
229 331
230 /// <summary> 332 #region Per thread properties
231 /// A reference from each thread in the thread pool to its SmartThreadPool
232 /// object container.
233 /// With this variable a thread can know whatever it belongs to a
234 /// SmartThreadPool.
235 /// </summary>
236 [ThreadStatic]
237 private static SmartThreadPool _smartThreadPool;
238 333
239 /// <summary> 334 /// <summary>
240 /// A reference to the current work item a thread from the thread pool 335 /// A reference to the current work item a thread from the thread pool
241 /// is executing. 336 /// is executing.
242 /// </summary> 337 /// </summary>
243 [ThreadStatic] 338 internal static ThreadEntry CurrentThreadEntry
244 private static WorkItem _currentWorkItem; 339 {
245 340#if (WINDOWS_PHONE)
246 /// <summary> 341 get
247 /// STP performance counters 342 {
248 /// </summary> 343 lock(_threadEntries)
249 private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance; 344 {
250 345 ThreadEntry threadEntry;
346 if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry))
347 {
348 return threadEntry;
349 }
350 }
351 return null;
352 }
353 set
354 {
355 lock(_threadEntries)
356 {
357 _threadEntries[Thread.CurrentThread.ManagedThreadId] = value;
358 }
359 }
360#elif (_WINDOWS_CE)
361 get
362 {
363 //Thread.CurrentThread.ManagedThreadId
364 return Thread.GetData(_threadEntrySlot) as ThreadEntry;
365 }
366 set
367 {
368 Thread.SetData(_threadEntrySlot, value);
369 }
370#else
371 get
372 {
373 return _threadEntry;
374 }
375 set
376 {
377 _threadEntry = value;
378 }
379#endif
380 }
251 #endregion 381 #endregion
252 382
253 #region Construction and Finalization 383 #region Construction and Finalization
254 384
255 /// <summary> 385 /// <summary>
256 /// Constructor 386 /// Constructor
257 /// </summary> 387 /// </summary>
258 public SmartThreadPool() 388 public SmartThreadPool()
259 { 389 {
390 _stpStartInfo = new STPStartInfo();
260 Initialize(); 391 Initialize();
261 } 392 }
262 393
263 /// <summary> 394 /// <summary>
264 /// Constructor 395 /// Constructor
265 /// </summary> 396 /// </summary>
266 /// <param name="idleTimeout">Idle timeout in milliseconds</param> 397 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
267 public SmartThreadPool(int idleTimeout) 398 public SmartThreadPool(int idleTimeout)
268 { 399 {
269 _stpStartInfo.IdleTimeout = idleTimeout; 400 _stpStartInfo = new STPStartInfo
270 Initialize(); 401 {
271 } 402 IdleTimeout = idleTimeout,
403 };
404 Initialize();
405 }
406
407 /// <summary>
408 /// Constructor
409 /// </summary>
410 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
411 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
412 public SmartThreadPool(
413 int idleTimeout,
414 int maxWorkerThreads)
415 {
416 _stpStartInfo = new STPStartInfo
417 {
418 IdleTimeout = idleTimeout,
419 MaxWorkerThreads = maxWorkerThreads,
420 };
421 Initialize();
422 }
423
424 /// <summary>
425 /// Constructor
426 /// </summary>
427 /// <param name="idleTimeout">Idle timeout in milliseconds</param>
428 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
429 /// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
430 public SmartThreadPool(
431 int idleTimeout,
432 int maxWorkerThreads,
433 int minWorkerThreads)
434 {
435 _stpStartInfo = new STPStartInfo
436 {
437 IdleTimeout = idleTimeout,
438 MaxWorkerThreads = maxWorkerThreads,
439 MinWorkerThreads = minWorkerThreads,
440 };
441 Initialize();
442 }
272 443
273 /// <summary> 444 /// <summary>
274 /// Constructor 445 /// Constructor
275 /// </summary> 446 /// </summary>
276 /// <param name="idleTimeout">Idle timeout in milliseconds</param> 447 /// <param name="stpStartInfo">A SmartThreadPool configuration that overrides the default behavior</param>
277 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param> 448 public SmartThreadPool(STPStartInfo stpStartInfo)
278 public SmartThreadPool( 449 {
279 int idleTimeout, 450 _stpStartInfo = new STPStartInfo(stpStartInfo);
280 int maxWorkerThreads) 451 Initialize();
281 { 452 }
282 _stpStartInfo.IdleTimeout = idleTimeout;
283 _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
284 Initialize();
285 }
286 453
287 /// <summary> 454 private void Initialize()
288 /// Constructor 455 {
289 /// </summary> 456 Name = _stpStartInfo.ThreadPoolName;
290 /// <param name="idleTimeout">Idle timeout in milliseconds</param> 457 ValidateSTPStartInfo();
291 /// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
292 /// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
293 public SmartThreadPool(
294 int idleTimeout,
295 int maxWorkerThreads,
296 int minWorkerThreads)
297 {
298 _stpStartInfo.IdleTimeout = idleTimeout;
299 _stpStartInfo.MaxWorkerThreads = maxWorkerThreads;
300 _stpStartInfo.MinWorkerThreads = minWorkerThreads;
301 Initialize();
302 }
303 458
304 /// <summary> 459 // _stpStartInfoRW stores a read/write copy of the STPStartInfo.
305 /// Constructor 460 // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten
306 /// </summary>
307 public SmartThreadPool(STPStartInfo stpStartInfo)
308 {
309 _stpStartInfo = new STPStartInfo(stpStartInfo);
310 Initialize();
311 }
312 461
313 private void Initialize() 462 _isSuspended = _stpStartInfo.StartSuspended;
314 {
315 Name = _stpStartInfo.ThreadPoolName;
316 ValidateSTPStartInfo();
317 463
464#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE)
465 if (null != _stpStartInfo.PerformanceCounterInstanceName)
466 {
467 throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters");
468 }
469#else
318 if (null != _stpStartInfo.PerformanceCounterInstanceName) 470 if (null != _stpStartInfo.PerformanceCounterInstanceName)
319 { 471 {
320 try 472 try
321 { 473 {
322 _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); 474 _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
323 } 475 }
324 catch(Exception e) 476 catch (Exception e)
325 { 477 {
326 Debug.WriteLine("Unable to create Performance Counters: " + e.ToString()); 478 Debug.WriteLine("Unable to create Performance Counters: " + e);
327 _pcs = NullSTPInstancePerformanceCounters.Instance; 479 _windowsPCs = NullSTPInstancePerformanceCounters.Instance;
328 } 480 }
329 } 481 }
482#endif
330 483
331 StartOptimalNumberOfThreads(); 484 if (_stpStartInfo.EnableLocalPerformanceCounters)
332 }
333
334 private void StartOptimalNumberOfThreads()
335 {
336 int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads);
337 threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads);
338 StartThreads(threadsCount);
339 }
340
341 private void ValidateSTPStartInfo()
342 {
343 if (_stpStartInfo.MinWorkerThreads < 0)
344 {
345 throw new ArgumentOutOfRangeException(
346 "MinWorkerThreads", "MinWorkerThreads cannot be negative");
347 }
348
349 if (_stpStartInfo.MaxWorkerThreads <= 0)
350 { 485 {
351 throw new ArgumentOutOfRangeException( 486 _localPCs = new LocalSTPInstancePerformanceCounters();
352 "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero");
353 } 487 }
354 488
355 if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) 489 // If the STP is not started suspended then start the threads.
490 if (!_isSuspended)
356 { 491 {
357 throw new ArgumentOutOfRangeException( 492 StartOptimalNumberOfThreads();
358 "MinWorkerThreads, maxWorkerThreads",
359 "MaxWorkerThreads must be greater or equal to MinWorkerThreads");
360 } 493 }
361 } 494 }
362 495
363 private void ValidateCallback(Delegate callback) 496 private void StartOptimalNumberOfThreads()
364 { 497 {
365 if(callback.GetInvocationList().Length > 1) 498 int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads);
499 threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads);
500 threadsCount -= _workerThreads.Count;
501 if (threadsCount > 0)
366 { 502 {
367 throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); 503 StartThreads(threadsCount);
368 } 504 }
369 } 505 }
370 506
371 #endregion 507 private void ValidateSTPStartInfo()
508 {
509 if (_stpStartInfo.MinWorkerThreads < 0)
510 {
511 throw new ArgumentOutOfRangeException(
512 "MinWorkerThreads", "MinWorkerThreads cannot be negative");
513 }
372 514
373 #region Thread Processing 515 if (_stpStartInfo.MaxWorkerThreads <= 0)
516 {
517 throw new ArgumentOutOfRangeException(
518 "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero");
519 }
374 520
375 /// <summary> 521 if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads)
376 /// Waits on the queue for a work item, shutdown, or timeout. 522 {
377 /// </summary> 523 throw new ArgumentOutOfRangeException(
378 /// <returns> 524 "MinWorkerThreads, maxWorkerThreads",
379 /// Returns the WaitingCallback or null in case of timeout or shutdown. 525 "MaxWorkerThreads must be greater or equal to MinWorkerThreads");
380 /// </returns> 526 }
381 private WorkItem Dequeue() 527 }
382 { 528
383 WorkItem workItem = 529 private static void ValidateCallback(Delegate callback)
530 {
531 if(callback.GetInvocationList().Length > 1)
532 {
533 throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
534 }
535 }
536
537 #endregion
538
539 #region Thread Processing
540
541 /// <summary>
542 /// Waits on the queue for a work item, shutdown, or timeout.
543 /// </summary>
544 /// <returns>
545 /// Returns the WaitingCallback or null in case of timeout or shutdown.
546 /// </returns>
547 private WorkItem Dequeue()
548 {
549 WorkItem workItem =
384 _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); 550 _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent);
385 551
386 return workItem; 552 return workItem;
387 } 553 }
388 554
389 /// <summary> 555 /// <summary>
390 /// Put a new work item in the queue 556 /// Put a new work item in the queue
391 /// </summary> 557 /// </summary>
392 /// <param name="workItem">A work item to queue</param> 558 /// <param name="workItem">A work item to queue</param>
393 private void Enqueue(WorkItem workItem) 559 internal override void Enqueue(WorkItem workItem)
394 { 560 {
395 Enqueue(workItem, true); 561 // Make sure the workItem is not null
396 } 562 Debug.Assert(null != workItem);
397 563
398 /// <summary> 564 IncrementWorkItemsCount();
399 /// Put a new work item in the queue 565
400 /// </summary> 566 workItem.CanceledSmartThreadPool = _canceledSmartThreadPool;
401 /// <param name="workItem">A work item to queue</param> 567 _workItemsQueue.EnqueueWorkItem(workItem);
402 internal void Enqueue(WorkItem workItem, bool incrementWorkItems) 568 workItem.WorkItemIsQueued();
403 { 569
404 // Make sure the workItem is not null 570 // If all the threads are busy then try to create a new one
405 Debug.Assert(null != workItem); 571 if (_currentWorkItemsCount > _workerThreads.Count)
406 572 {
407 if (incrementWorkItems) 573 StartThreads(1);
408 { 574 }
409 IncrementWorkItemsCount(); 575 }
410 } 576
411 577 private void IncrementWorkItemsCount()
412 _workItemsQueue.EnqueueWorkItem(workItem); 578 {
413 workItem.WorkItemIsQueued(); 579 _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
414 580 _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
415 // If all the threads are busy then try to create a new one 581
416 if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count) 582 int count = Interlocked.Increment(ref _currentWorkItemsCount);
417 { 583 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
418 StartThreads(1); 584 if (count == 1)
419 } 585 {
420 } 586 IsIdle = false;
421
422 private void IncrementWorkItemsCount()
423 {
424 _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
425
426 int count = Interlocked.Increment(ref _currentWorkItemsCount);
427 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
428 if (count == 1)
429 {
430 //Trace.WriteLine("STP is NOT idle");
431 _isIdleWaitHandle.Reset(); 587 _isIdleWaitHandle.Reset();
432 } 588 }
433 } 589 }
434
435 private void DecrementWorkItemsCount()
436 {
437 ++_workItemsProcessed;
438
439 // The counter counts even if the work item was cancelled
440 _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
441 590
591 private void DecrementWorkItemsCount()
592 {
442 int count = Interlocked.Decrement(ref _currentWorkItemsCount); 593 int count = Interlocked.Decrement(ref _currentWorkItemsCount);
443 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); 594 //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
444 if (count == 0) 595 if (count == 0)
445 { 596 {
446 //Trace.WriteLine("STP is idle"); 597 IsIdle = true;
447 _isIdleWaitHandle.Set(); 598 _isIdleWaitHandle.Set();
448 } 599 }
449 }
450 600
451 internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) 601 Interlocked.Increment(ref _workItemsProcessed);
452 {
453 _workItemsGroups[workItemsGroup] = workItemsGroup;
454 }
455
456 internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
457 {
458 if (_workItemsGroups.Contains(workItemsGroup))
459 {
460 _workItemsGroups.Remove(workItemsGroup);
461 }
462 }
463 602
464 /// <summary> 603 if (!_shutdown)
465 /// Inform that the current thread is about to quit or quiting.
466 /// The same thread may call this method more than once.
467 /// </summary>
468 private void InformCompleted()
469 {
470 // There is no need to lock the two methods together
471 // since only the current thread removes itself
472 // and the _workerThreads is a synchronized hashtable
473 if (_workerThreads.Contains(Thread.CurrentThread))
474 { 604 {
475 _workerThreads.Remove(Thread.CurrentThread); 605 // The counter counts even if the work item was cancelled
476 _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); 606 _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
607 _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
477 } 608 }
478 }
479 609
480 /// <summary> 610 }
481 /// Starts new threads 611
482 /// </summary> 612 internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
483 /// <param name="threadsCount">The number of threads to start</param> 613 {
484 private void StartThreads(int threadsCount) 614 _workItemsGroups[workItemsGroup] = workItemsGroup;
485 { 615 }
486 if (_stpStartInfo.StartSuspended) 616
487 { 617 internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
488 return; 618 {
489 } 619 if (_workItemsGroups.Contains(workItemsGroup))
620 {
621 _workItemsGroups.Remove(workItemsGroup);
622 }
623 }
624
625 /// <summary>
626 /// Inform that the current thread is about to quit or quiting.
627 /// The same thread may call this method more than once.
628 /// </summary>
629 private void InformCompleted()
630 {
631 // There is no need to lock the two methods together
632 // since only the current thread removes itself
633 // and the _workerThreads is a synchronized dictionary
634 if (_workerThreads.Contains(Thread.CurrentThread))
635 {
636 _workerThreads.Remove(Thread.CurrentThread);
637 _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
638 _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
639 }
640 }
641
642 /// <summary>
643 /// Starts new threads
644 /// </summary>
645 /// <param name="threadsCount">The number of threads to start</param>
646 private void StartThreads(int threadsCount)
647 {
648 if (_isSuspended)
649 {
650 return;
651 }
652
653 lock(_workerThreads.SyncRoot)
654 {
655 // Don't start threads on shut down
656 if (_shutdown)
657 {
658 return;
659 }
660
661 for(int i = 0; i < threadsCount; ++i)
662 {
663 // Don't create more threads then the upper limit
664 if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads)
665 {
666 return;
667 }
490 668
491 lock(_workerThreads.SyncRoot) 669 // Create a new thread
492 {
493 // Don't start threads on shut down
494 if (_shutdown)
495 {
496 return;
497 }
498 670
499 for(int i = 0; i < threadsCount; ++i) 671#if (_SILVERLIGHT) || (WINDOWS_PHONE)
500 { 672 Thread workerThread = new Thread(ProcessQueuedItems);
501 // Don't create more threads then the upper limit 673#else
502 if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) 674 Thread workerThread =
675 _stpStartInfo.MaxStackSize.HasValue
676 ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value)
677 : new Thread(ProcessQueuedItems);
678#endif
679 // Configure the new thread and start it
680 workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
681 workerThread.IsBackground = _stpStartInfo.AreThreadsBackground;
682
683#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE)
684 if (_stpStartInfo.ApartmentState != ApartmentState.Unknown)
503 { 685 {
504 return; 686 workerThread.SetApartmentState(_stpStartInfo.ApartmentState);
505 } 687 }
688#endif
506 689
507 // Create a new thread 690#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
508 Thread workerThread;
509 if (_stpStartInfo.StackSize > 0)
510 workerThread = new Thread(ProcessQueuedItems, _stpStartInfo.StackSize);
511 else
512 workerThread = new Thread(ProcessQueuedItems);
513
514 // Configure the new thread and start it
515 workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
516 workerThread.IsBackground = true;
517 workerThread.Priority = _stpStartInfo.ThreadPriority; 691 workerThread.Priority = _stpStartInfo.ThreadPriority;
692#endif
518 workerThread.Start(); 693 workerThread.Start();
519 ++_threadCounter; 694 ++_threadCounter;
520 695
521 // Add the new thread to the hashtable and update its creation 696 // Add it to the dictionary and update its creation time.
522 // time. 697 _workerThreads[workerThread] = new ThreadEntry(this);
523 _workerThreads[workerThread] = DateTime.Now; 698
524 _pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); 699 _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
525 } 700 _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
526 } 701 }
527 } 702 }
528 703 }
529 /// <summary> 704
530 /// A worker thread method that processes work items from the work items queue. 705 /// <summary>
531 /// </summary> 706 /// A worker thread method that processes work items from the work items queue.
532 private void ProcessQueuedItems() 707 /// </summary>
533 { 708 private void ProcessQueuedItems()
534 // Initialize the _smartThreadPool variable 709 {
535 _smartThreadPool = this; 710 // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks
536 711 // of the dictionary.
537 try 712 CurrentThreadEntry = _workerThreads[Thread.CurrentThread];
538 { 713
539 bool bInUseWorkerThreadsWasIncremented = false; 714 FireOnThreadInitialization();
540 715
541 // Process until shutdown. 716 try
542 while(!_shutdown) 717 {
543 { 718 bool bInUseWorkerThreadsWasIncremented = false;
544 // Update the last time this thread was seen alive. 719
545 // It's good for debugging. 720 // Process until shutdown.
546 _workerThreads[Thread.CurrentThread] = DateTime.Now; 721 while(!_shutdown)
547 722 {
548 // Wait for a work item, shutdown, or timeout 723 // Update the last time this thread was seen alive.
549 WorkItem workItem = Dequeue(); 724 // It's good for debugging.
550 725 CurrentThreadEntry.IAmAlive();
551 // Update the last time this thread was seen alive. 726
552 // It's good for debugging. 727 // The following block handles the when the MaxWorkerThreads has been
553 _workerThreads[Thread.CurrentThread] = DateTime.Now; 728 // incremented by the user at run-time.
554 729 // Double lock for quit.
555 // On timeout or shut down. 730 if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads)
556 if (null == workItem)
557 { 731 {
558 // Double lock for quit. 732 lock (_workerThreads.SyncRoot)
559 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
560 { 733 {
561 lock(_workerThreads.SyncRoot) 734 if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads)
562 { 735 {
563 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) 736 // Inform that the thread is quiting and then quit.
564 { 737 // This method must be called within this lock or else
565 // Inform that the thread is quiting and then quit. 738 // more threads will quit and the thread pool will go
566 // This method must be called within this lock or else 739 // below the lower limit.
567 // more threads will quit and the thread pool will go 740 InformCompleted();
568 // below the lower limit. 741 break;
569 InformCompleted();
570 break;
571 }
572 } 742 }
573 } 743 }
574 } 744 }
575 745
576 // If we didn't quit then skip to the next iteration. 746 // Wait for a work item, shutdown, or timeout
577 if (null == workItem) 747 WorkItem workItem = Dequeue();
578 {
579 continue;
580 }
581
582 try
583 {
584 // Initialize the value to false
585 bInUseWorkerThreadsWasIncremented = false;
586
587 // Change the state of the work item to 'in progress' if possible.
588 // We do it here so if the work item has been canceled we won't
589 // increment the _inUseWorkerThreads.
590 // The cancel mechanism doesn't delete items from the queue,
591 // it marks the work item as canceled, and when the work item
592 // is dequeued, we just skip it.
593 // If the post execute of work item is set to always or to
594 // call when the work item is canceled then the StartingWorkItem()
595 // will return true, so the post execute can run.
596 if (!workItem.StartingWorkItem())
597 {
598 continue;
599 }
600 748
601 // Execute the callback. Make sure to accurately 749 // Update the last time this thread was seen alive.
602 // record how many callbacks are currently executing. 750 // It's good for debugging.
603 int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); 751 CurrentThreadEntry.IAmAlive();
604 _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
605 752
606 // Mark that the _inUseWorkerThreads incremented, so in the finally{} 753 // On timeout or shut down.
607 // statement we will decrement it correctly. 754 if (null == workItem)
608 bInUseWorkerThreadsWasIncremented = true; 755 {
609 756 // Double lock for quit.
610 // Set the _currentWorkItem to the current work item 757 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
611 _currentWorkItem = workItem; 758 {
612 759 lock(_workerThreads.SyncRoot)
613 lock(workItem) 760 {
614 { 761 if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
615 workItem.currentThread = Thread.CurrentThread; 762 {
616 } 763 // Inform that the thread is quiting and then quit.
617 764 // This method must be called within this lock or else
618 ExecuteWorkItem(workItem); 765 // more threads will quit and the thread pool will go
619 766 // below the lower limit.
620 lock(workItem) 767 InformCompleted();
621 { 768 break;
622 workItem.currentThread = null; 769 }
623 } 770 }
624 771 }
625 } 772 }
626 catch(ThreadAbortException ex) 773
627 { 774 // If we didn't quit then skip to the next iteration.
628 lock(workItem) 775 if (null == workItem)
629 { 776 {
630 workItem.currentThread = null; 777 continue;
631 } 778 }
632 ex.GetHashCode(); 779
633 Thread.ResetAbort(); 780 try
634 } 781 {
635 catch(Exception ex) 782 // Initialize the value to false
636 { 783 bInUseWorkerThreadsWasIncremented = false;
784
785 // Set the Current Work Item of the thread.
786 // Store the Current Work Item before the workItem.StartingWorkItem() is called,
787 // so WorkItem.Cancel can work when the work item is between InQueue and InProgress
788 // states.
789 // If the work item has been cancelled BEFORE the workItem.StartingWorkItem()
790 // (work item is in InQueue state) then workItem.StartingWorkItem() will return false.
791 // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then
792 // (work item is in InProgress state) then the thread will be aborted
793 CurrentThreadEntry.CurrentWorkItem = workItem;
794
795 // Change the state of the work item to 'in progress' if possible.
796 // We do it here so if the work item has been canceled we won't
797 // increment the _inUseWorkerThreads.
798 // The cancel mechanism doesn't delete items from the queue,
799 // it marks the work item as canceled, and when the work item
800 // is dequeued, we just skip it.
801 // If the post execute of work item is set to always or to
802 // call when the work item is canceled then the StartingWorkItem()
803 // will return true, so the post execute can run.
804 if (!workItem.StartingWorkItem())
805 {
806 continue;
807 }
808
809 // Execute the callback. Make sure to accurately
810 // record how many callbacks are currently executing.
811 int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads);
812 _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
813 _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
814
815 // Mark that the _inUseWorkerThreads incremented, so in the finally{}
816 // statement we will decrement it correctly.
817 bInUseWorkerThreadsWasIncremented = true;
818
819 workItem.FireWorkItemStarted();
820
821 ExecuteWorkItem(workItem);
822 }
823 catch(Exception ex)
824 {
637 ex.GetHashCode(); 825 ex.GetHashCode();
638 // Do nothing 826 // Do nothing
639 } 827 }
640 finally 828 finally
641 { 829 {
642 lock(workItem) 830 workItem.DisposeOfState();
643 { 831
644 workItem.currentThread = null; 832 // Set the CurrentWorkItem to null, since we
645 } 833 // no longer run user's code.
646 834 CurrentThreadEntry.CurrentWorkItem = null;
647 if (null != workItem) 835
648 { 836 // Decrement the _inUseWorkerThreads only if we had
649 workItem.DisposeOfState(); 837 // incremented it. Note the cancelled work items don't
650 } 838 // increment _inUseWorkerThreads.
651 839 if (bInUseWorkerThreadsWasIncremented)
652 // Set the _currentWorkItem to null, since we 840 {
653 // no longer run user's code. 841 int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads);
654 _currentWorkItem = null; 842 _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
655 843 _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads);
656 // Decrement the _inUseWorkerThreads only if we had 844 }
657 // incremented it. Note the cancelled work items don't 845
658 // increment _inUseWorkerThreads. 846 // Notify that the work item has been completed.
659 if (bInUseWorkerThreadsWasIncremented) 847 // WorkItemsGroup may enqueue their next work item.
660 { 848 workItem.FireWorkItemCompleted();
661 int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); 849
662 _pcs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); 850 // Decrement the number of work items here so the idle
663 } 851 // ManualResetEvent won't fluctuate.
664 852 DecrementWorkItemsCount();
665 // Notify that the work item has been completed. 853 }
666 // WorkItemsGroup may enqueue their next work item. 854 }
667 workItem.FireWorkItemCompleted(); 855 }
668 856 catch(ThreadAbortException tae)
669 // Decrement the number of work items here so the idle 857 {
670 // ManualResetEvent won't fluctuate.
671 DecrementWorkItemsCount();
672 }
673 }
674 }
675 catch(ThreadAbortException tae)
676 {
677 tae.GetHashCode(); 858 tae.GetHashCode();
678 // Handle the abort exception gracfully. 859 // Handle the abort exception gracfully.
679 Thread.ResetAbort(); 860#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
680 } 861 Thread.ResetAbort();
681 catch(Exception e) 862#endif
682 {
683 Debug.Assert(null != e);
684 }
685 finally
686 {
687 InformCompleted();
688 }
689 }
690
691 private void ExecuteWorkItem(WorkItem workItem)
692 {
693 _pcs.SampleWorkItemsWaitTime(workItem.WaitingTime);
694 try
695 {
696 workItem.Execute();
697 }
698 catch
699 {
700 throw;
701 } 863 }
702 finally 864 catch(Exception e)
865 {
866 Debug.Assert(null != e);
867 }
868 finally
869 {
870 InformCompleted();
871 FireOnThreadTermination();
872 }
873 }
874
875 private void ExecuteWorkItem(WorkItem workItem)
876 {
877 _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime);
878 _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime);
879 try
880 {
881 workItem.Execute();
882 }
883 finally
884 {
885 _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime);
886 _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime);
887 }
888 }
889
890
891 #endregion
892
893 #region Public Methods
894
895 private void ValidateWaitForIdle()
896 {
897 if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this)
898 {
899 throw new NotSupportedException(
900 "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
901 }
902 }
903
904 internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup)
905 {
906 if (null == CurrentThreadEntry)
703 { 907 {
704 _pcs.SampleWorkItemsProcessTime(workItem.ProcessTime); 908 return;
705 } 909 }
706 }
707
708
709 #endregion
710 910
711 #region Public Methods 911 WorkItem workItem = CurrentThreadEntry.CurrentWorkItem;
712 912 ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem);
713 /// <summary> 913 if ((null != workItemsGroup) &&
714 /// Queue a work item 914 (null != workItem) &&
915 CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup))
916 {
917 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
918 }
919 }
920
921 [MethodImpl(MethodImplOptions.NoInlining)]
922 private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem)
923 {
924 if ((null != workItemsGroup) &&
925 (null != workItem) &&
926 workItem.WasQueuedBy(workItemsGroup))
927 {
928 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
929 }
930 }
931
932 /// <summary>
933 /// Force the SmartThreadPool to shutdown
934 /// </summary>
935 public void Shutdown()
936 {
937 Shutdown(true, 0);
938 }
939
940 /// <summary>
941 /// Force the SmartThreadPool to shutdown with timeout
715 /// </summary> 942 /// </summary>
716 /// <param name="callback">A callback to execute</param> 943 public void Shutdown(bool forceAbort, TimeSpan timeout)
717 /// <returns>Returns a work item result</returns> 944 {
718 public IWorkItemResult QueueWorkItem(WorkItemCallback callback) 945 Shutdown(forceAbort, (int)timeout.TotalMilliseconds);
719 { 946 }
720 ValidateNotDisposed(); 947
721 ValidateCallback(callback); 948 /// <summary>
722 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback); 949 /// Empties the queue of work items and abort the threads in the pool.
723 Enqueue(workItem); 950 /// </summary>
724 return workItem.GetWorkItemResult(); 951 public void Shutdown(bool forceAbort, int millisecondsTimeout)
725 } 952 {
953 ValidateNotDisposed();
954
955 ISTPInstancePerformanceCounters pcs = _windowsPCs;
956
957 if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs)
958 {
959 // Set the _pcs to "null" to stop updating the performance
960 // counters
961 _windowsPCs = NullSTPInstancePerformanceCounters.Instance;
962
963 pcs.Dispose();
964 }
965
966 Thread [] threads;
967 lock(_workerThreads.SyncRoot)
968 {
969 // Shutdown the work items queue
970 _workItemsQueue.Dispose();
971
972 // Signal the threads to exit
973 _shutdown = true;
974 _shuttingDownEvent.Set();
975
976 // Make a copy of the threads' references in the pool
977 threads = new Thread [_workerThreads.Count];
978 _workerThreads.Keys.CopyTo(threads, 0);
979 }
980
981 int millisecondsLeft = millisecondsTimeout;
982 Stopwatch stopwatch = Stopwatch.StartNew();
983 //DateTime start = DateTime.UtcNow;
984 bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
985 bool timeout = false;
986
987 // Each iteration we update the time left for the timeout.
988 foreach(Thread thread in threads)
989 {
990 // Join don't work with negative numbers
991 if (!waitInfinitely && (millisecondsLeft < 0))
992 {
993 timeout = true;
994 break;
995 }
996
997 // Wait for the thread to terminate
998 bool success = thread.Join(millisecondsLeft);
999 if(!success)
1000 {
1001 timeout = true;
1002 break;
1003 }
1004
1005 if(!waitInfinitely)
1006 {
1007 // Update the time left to wait
1008 //TimeSpan ts = DateTime.UtcNow - start;
1009 millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
1010 }
1011 }
1012
1013 if (timeout && forceAbort)
1014 {
1015 // Abort the threads in the pool
1016 foreach(Thread thread in threads)
1017 {
1018
1019 if ((thread != null)
1020#if !(_WINDOWS_CE)
1021 && thread.IsAlive
1022#endif
1023 )
1024 {
1025 try
1026 {
1027 thread.Abort(); // Shutdown
1028 }
1029 catch(SecurityException e)
1030 {
1031 e.GetHashCode();
1032 }
1033 catch(ThreadStateException ex)
1034 {
1035 ex.GetHashCode();
1036 // In case the thread has been terminated
1037 // after the check if it is alive.
1038 }
1039 }
1040 }
1041 }
1042 }
1043
1044 /// <summary>
1045 /// Wait for all work items to complete
1046 /// </summary>
1047 /// <param name="waitableResults">Array of work item result objects</param>
1048 /// <returns>
1049 /// true when every work item in workItemResults has completed; otherwise false.
1050 /// </returns>
1051 public static bool WaitAll(
1052 IWaitableResult [] waitableResults)
1053 {
1054 return WaitAll(waitableResults, Timeout.Infinite, true);
1055 }
1056
1057 /// <summary>
1058 /// Wait for all work items to complete
1059 /// </summary>
1060 /// <param name="waitableResults">Array of work item result objects</param>
1061 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1062 /// <param name="exitContext">
1063 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1064 /// </param>
1065 /// <returns>
1066 /// true when every work item in workItemResults has completed; otherwise false.
1067 /// </returns>
1068 public static bool WaitAll(
1069 IWaitableResult [] waitableResults,
1070 TimeSpan timeout,
1071 bool exitContext)
1072 {
1073 return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext);
1074 }
1075
1076 /// <summary>
1077 /// Wait for all work items to complete
1078 /// </summary>
1079 /// <param name="waitableResults">Array of work item result objects</param>
1080 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1081 /// <param name="exitContext">
1082 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1083 /// </param>
1084 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1085 /// <returns>
1086 /// true when every work item in workItemResults has completed; otherwise false.
1087 /// </returns>
1088 public static bool WaitAll(
1089 IWaitableResult[] waitableResults,
1090 TimeSpan timeout,
1091 bool exitContext,
1092 WaitHandle cancelWaitHandle)
1093 {
1094 return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
1095 }
1096
1097 /// <summary>
1098 /// Wait for all work items to complete
1099 /// </summary>
1100 /// <param name="waitableResults">Array of work item result objects</param>
1101 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1102 /// <param name="exitContext">
1103 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1104 /// </param>
1105 /// <returns>
1106 /// true when every work item in workItemResults has completed; otherwise false.
1107 /// </returns>
1108 public static bool WaitAll(
1109 IWaitableResult [] waitableResults,
1110 int millisecondsTimeout,
1111 bool exitContext)
1112 {
1113 return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null);
1114 }
1115
1116 /// <summary>
1117 /// Wait for all work items to complete
1118 /// </summary>
1119 /// <param name="waitableResults">Array of work item result objects</param>
1120 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1121 /// <param name="exitContext">
1122 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1123 /// </param>
1124 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1125 /// <returns>
1126 /// true when every work item in workItemResults has completed; otherwise false.
1127 /// </returns>
1128 public static bool WaitAll(
1129 IWaitableResult[] waitableResults,
1130 int millisecondsTimeout,
1131 bool exitContext,
1132 WaitHandle cancelWaitHandle)
1133 {
1134 return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
1135 }
1136
1137
1138 /// <summary>
1139 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1140 /// </summary>
1141 /// <param name="waitableResults">Array of work item result objects</param>
1142 /// <returns>
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 /// </returns>
1145 public static int WaitAny(
1146 IWaitableResult [] waitableResults)
1147 {
1148 return WaitAny(waitableResults, Timeout.Infinite, true);
1149 }
1150
1151 /// <summary>
1152 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1153 /// </summary>
1154 /// <param name="waitableResults">Array of work item result objects</param>
1155 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1156 /// <param name="exitContext">
1157 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1158 /// </param>
1159 /// <returns>
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 /// </returns>
1162 public static int WaitAny(
1163 IWaitableResult[] waitableResults,
1164 TimeSpan timeout,
1165 bool exitContext)
1166 {
1167 return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext);
1168 }
1169
1170 /// <summary>
1171 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1172 /// </summary>
1173 /// <param name="waitableResults">Array of work item result objects</param>
1174 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1175 /// <param name="exitContext">
1176 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1177 /// </param>
1178 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1179 /// <returns>
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 /// </returns>
1182 public static int WaitAny(
1183 IWaitableResult [] waitableResults,
1184 TimeSpan timeout,
1185 bool exitContext,
1186 WaitHandle cancelWaitHandle)
1187 {
1188 return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
1189 }
1190
1191 /// <summary>
1192 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1193 /// </summary>
1194 /// <param name="waitableResults">Array of work item result objects</param>
1195 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1196 /// <param name="exitContext">
1197 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1198 /// </param>
1199 /// <returns>
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 /// </returns>
1202 public static int WaitAny(
1203 IWaitableResult [] waitableResults,
1204 int millisecondsTimeout,
1205 bool exitContext)
1206 {
1207 return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null);
1208 }
1209
1210 /// <summary>
1211 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1212 /// </summary>
1213 /// <param name="waitableResults">Array of work item result objects</param>
1214 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1215 /// <param name="exitContext">
1216 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1217 /// </param>
1218 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1219 /// <returns>
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 /// </returns>
1222 public static int WaitAny(
1223 IWaitableResult [] waitableResults,
1224 int millisecondsTimeout,
1225 bool exitContext,
1226 WaitHandle cancelWaitHandle)
1227 {
1228 return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
1229 }
1230
1231 /// <summary>
1232 /// Creates a new WorkItemsGroup.
1233 /// </summary>
1234 /// <param name="concurrency">The number of work items that can be run concurrently</param>
1235 /// <returns>A reference to the WorkItemsGroup</returns>
1236 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency)
1237 {
1238 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo);
1239 return workItemsGroup;
1240 }
726 1241
727 /// <summary> 1242 /// <summary>
728 /// Queue a work item 1243 /// Creates a new WorkItemsGroup.
729 /// </summary> 1244 /// </summary>
730 /// <param name="callback">A callback to execute</param> 1245 /// <param name="concurrency">The number of work items that can be run concurrently</param>
731 /// <param name="workItemPriority">The priority of the work item</param> 1246 /// <param name="wigStartInfo">A WorkItemsGroup configuration that overrides the default behavior</param>
732 /// <returns>Returns a work item result</returns> 1247 /// <returns>A reference to the WorkItemsGroup</returns>
733 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) 1248 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo)
734 { 1249 {
735 ValidateNotDisposed(); 1250 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo);
736 ValidateCallback(callback); 1251 return workItemsGroup;
737 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority); 1252 }
738 Enqueue(workItem);
739 return workItem.GetWorkItemResult();
740 }
741 1253
742 /// <summary> 1254 #region Fire Thread's Events
743 /// Queue a work item
744 /// </summary>
745 /// <param name="workItemInfo">Work item info</param>
746 /// <param name="callback">A callback to execute</param>
747 /// <returns>Returns a work item result</returns>
748 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
749 {
750 ValidateNotDisposed();
751 ValidateCallback(callback);
752 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback);
753 Enqueue(workItem);
754 return workItem.GetWorkItemResult();
755 }
756 1255
757 /// <summary> 1256 private void FireOnThreadInitialization()
758 /// Queue a work item
759 /// </summary>
760 /// <param name="callback">A callback to execute</param>
761 /// <param name="state">
762 /// The context object of the work item. Used for passing arguments to the work item.
763 /// </param>
764 /// <returns>Returns a work item result</returns>
765 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
766 { 1257 {
767 ValidateNotDisposed(); 1258 if (null != _onThreadInitialization)
768 ValidateCallback(callback); 1259 {
769 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state); 1260 foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList())
770 Enqueue(workItem); 1261 {
771 return workItem.GetWorkItemResult(); 1262 try
1263 {
1264 tih();
1265 }
1266 catch (Exception e)
1267 {
1268 e.GetHashCode();
1269 Debug.Assert(false);
1270 throw;
1271 }
1272 }
1273 }
772 } 1274 }
773 1275
774 /// <summary> 1276 private void FireOnThreadTermination()
775 /// Queue a work item
776 /// </summary>
777 /// <param name="callback">A callback to execute</param>
778 /// <param name="state">
779 /// The context object of the work item. Used for passing arguments to the work item.
780 /// </param>
781 /// <param name="workItemPriority">The work item priority</param>
782 /// <returns>Returns a work item result</returns>
783 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
784 { 1277 {
785 ValidateNotDisposed(); 1278 if (null != _onThreadTermination)
786 ValidateCallback(callback); 1279 {
787 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority); 1280 foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList())
788 Enqueue(workItem); 1281 {
789 return workItem.GetWorkItemResult(); 1282 try
1283 {
1284 tth();
1285 }
1286 catch (Exception e)
1287 {
1288 e.GetHashCode();
1289 Debug.Assert(false);
1290 throw;
1291 }
1292 }
1293 }
790 } 1294 }
791 1295
792 /// <summary> 1296 #endregion
793 /// Queue a work item
794 /// </summary>
795 /// <param name="workItemInfo">Work item information</param>
796 /// <param name="callback">A callback to execute</param>
797 /// <param name="state">
798 /// The context object of the work item. Used for passing arguments to the work item.
799 /// </param>
800 /// <returns>Returns a work item result</returns>
801 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
802 {
803 ValidateNotDisposed();
804 ValidateCallback(callback);
805 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state);
806 Enqueue(workItem);
807 return workItem.GetWorkItemResult();
808 }
809 1297
810 /// <summary> 1298 /// <summary>
811 /// Queue a work item 1299 /// This event is fired when a thread is created.
1300 /// Use it to initialize a thread before the work items use it.
812 /// </summary> 1301 /// </summary>
813 /// <param name="callback">A callback to execute</param> 1302 public event ThreadInitializationHandler OnThreadInitialization
814 /// <param name="state">
815 /// The context object of the work item. Used for passing arguments to the work item.
816 /// </param>
817 /// <param name="postExecuteWorkItemCallback">
818 /// A delegate to call after the callback completion
819 /// </param>
820 /// <returns>Returns a work item result</returns>
821 public IWorkItemResult QueueWorkItem(
822 WorkItemCallback callback,
823 object state,
824 PostExecuteWorkItemCallback postExecuteWorkItemCallback)
825 { 1303 {
826 ValidateNotDisposed(); 1304 add { _onThreadInitialization += value; }
827 ValidateCallback(callback); 1305 remove { _onThreadInitialization -= value; }
828 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback);
829 Enqueue(workItem);
830 return workItem.GetWorkItemResult();
831 } 1306 }
832 1307
833 /// <summary> 1308 /// <summary>
834 /// Queue a work item 1309 /// This event is fired when a thread is terminating.
1310 /// Use it for cleanup.
835 /// </summary> 1311 /// </summary>
836 /// <param name="callback">A callback to execute</param> 1312 public event ThreadTerminationHandler OnThreadTermination
837 /// <param name="state">
838 /// The context object of the work item. Used for passing arguments to the work item.
839 /// </param>
840 /// <param name="postExecuteWorkItemCallback">
841 /// A delegate to call after the callback completion
842 /// </param>
843 /// <param name="workItemPriority">The work item priority</param>
844 /// <returns>Returns a work item result</returns>
845 public IWorkItemResult QueueWorkItem(
846 WorkItemCallback callback,
847 object state,
848 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
849 WorkItemPriority workItemPriority)
850 { 1313 {
851 ValidateNotDisposed(); 1314 add { _onThreadTermination += value; }
852 ValidateCallback(callback); 1315 remove { _onThreadTermination -= value; }
853 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
854 Enqueue(workItem);
855 return workItem.GetWorkItemResult();
856 } 1316 }
857 1317
858 /// <summary>
859 /// Queue a work item
860 /// </summary>
861 /// <param name="callback">A callback to execute</param>
862 /// <param name="state">
863 /// The context object of the work item. Used for passing arguments to the work item.
864 /// </param>
865 /// <param name="postExecuteWorkItemCallback">
866 /// A delegate to call after the callback completion
867 /// </param>
868 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
869 /// <returns>Returns a work item result</returns>
870 public IWorkItemResult QueueWorkItem(
871 WorkItemCallback callback,
872 object state,
873 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
874 CallToPostExecute callToPostExecute)
875 {
876 ValidateNotDisposed();
877 ValidateCallback(callback);
878 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
879 Enqueue(workItem);
880 return workItem.GetWorkItemResult();
881 }
882 1318
883 /// <summary> 1319 internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig)
884 /// Queue a work item
885 /// </summary>
886 /// <param name="callback">A callback to execute</param>
887 /// <param name="state">
888 /// The context object of the work item. Used for passing arguments to the work item.
889 /// </param>
890 /// <param name="postExecuteWorkItemCallback">
891 /// A delegate to call after the callback completion
892 /// </param>
893 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
894 /// <param name="workItemPriority">The work item priority</param>
895 /// <returns>Returns a work item result</returns>
896 public IWorkItemResult QueueWorkItem(
897 WorkItemCallback callback,
898 object state,
899 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
900 CallToPostExecute callToPostExecute,
901 WorkItemPriority workItemPriority)
902 { 1320 {
903 ValidateNotDisposed(); 1321 foreach (ThreadEntry threadEntry in _workerThreads.Values)
904 ValidateCallback(callback); 1322 {
905 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); 1323 WorkItem workItem = threadEntry.CurrentWorkItem;
906 Enqueue(workItem); 1324 if (null != workItem &&
907 return workItem.GetWorkItemResult(); 1325 workItem.WasQueuedBy(wig) &&
1326 !workItem.IsCanceled)
1327 {
1328 threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true);
1329 }
1330 }
908 } 1331 }
909 1332
910 /// <summary> 1333
911 /// Wait for the thread pool to be idle
912 /// </summary>
913 public void WaitForIdle()
914 {
915 WaitForIdle(Timeout.Infinite);
916 }
917 1334
918 /// <summary> 1335 #endregion
919 /// Wait for the thread pool to be idle
920 /// </summary>
921 public bool WaitForIdle(TimeSpan timeout)
922 {
923 return WaitForIdle((int)timeout.TotalMilliseconds);
924 }
925 1336
926 /// <summary> 1337 #region Properties
927 /// Wait for the thread pool to be idle
928 /// </summary>
929 public bool WaitForIdle(int millisecondsTimeout)
930 {
931 ValidateWaitForIdle();
932 return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
933 }
934 1338
935 private void ValidateWaitForIdle() 1339 /// <summary>
936 { 1340 /// Get/Set the lower limit of threads in the pool.
937 if(_smartThreadPool == this) 1341 /// </summary>
1342 public int MinThreads
1343 {
1344 get
1345 {
1346 ValidateNotDisposed();
1347 return _stpStartInfo.MinWorkerThreads;
1348 }
1349 set
938 { 1350 {
939 throw new NotSupportedException( 1351 Debug.Assert(value >= 0);
940 "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); 1352 Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads);
1353 if (_stpStartInfo.MaxWorkerThreads < value)
1354 {
1355 _stpStartInfo.MaxWorkerThreads = value;
1356 }
1357 _stpStartInfo.MinWorkerThreads = value;
1358 StartOptimalNumberOfThreads();
941 } 1359 }
942 } 1360 }
1361
1362 /// <summary>
1363 /// Get/Set the upper limit of threads in the pool.
1364 /// </summary>
1365 public int MaxThreads
1366 {
1367 get
1368 {
1369 ValidateNotDisposed();
1370 return _stpStartInfo.MaxWorkerThreads;
1371 }
943 1372
944 internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) 1373 set
1374 {
1375 Debug.Assert(value > 0);
1376 Debug.Assert(value >= _stpStartInfo.MinWorkerThreads);
1377 if (_stpStartInfo.MinWorkerThreads > value)
1378 {
1379 _stpStartInfo.MinWorkerThreads = value;
1380 }
1381 _stpStartInfo.MaxWorkerThreads = value;
1382 StartOptimalNumberOfThreads();
1383 }
1384 }
1385 /// <summary>
1386 /// Get the number of threads in the thread pool.
1387 /// Should be between the lower and the upper limits.
1388 /// </summary>
1389 public int ActiveThreads
1390 {
1391 get
1392 {
1393 ValidateNotDisposed();
1394 return _workerThreads.Count;
1395 }
1396 }
1397
1398 /// <summary>
1399 /// Get the number of busy (not idle) threads in the thread pool.
1400 /// </summary>
1401 public int InUseThreads
1402 {
1403 get
1404 {
1405 ValidateNotDisposed();
1406 return _inUseWorkerThreads;
1407 }
1408 }
1409
1410 /// <summary>
1411 /// Returns true if the current running work item has been cancelled.
1412 /// Must be used within the work item's callback method.
1413 /// The work item should sample this value in order to know if it
1414 /// needs to quit before its completion.
1415 /// </summary>
1416 public static bool IsWorkItemCanceled
945 { 1417 {
946 ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem); 1418 get
947 if ((null != workItemsGroup) &&
948 (null != SmartThreadPool._currentWorkItem) &&
949 SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup))
950 { 1419 {
951 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); 1420 return CurrentThreadEntry.CurrentWorkItem.IsCanceled;
952 } 1421 }
953 } 1422 }
954 1423
955 [MethodImpl(MethodImplOptions.NoInlining)] 1424 /// <summary>
956 private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) 1425 /// Checks if the work item has been cancelled, and if yes then abort the thread.
1426 /// Can be used with Cancel and timeout
1427 /// </summary>
1428 public static void AbortOnWorkItemCancel()
957 { 1429 {
958 if ((null != workItemsGroup) && 1430 if (IsWorkItemCanceled)
959 (null != workItem) &&
960 workItem.WasQueuedBy(workItemsGroup))
961 { 1431 {
962 throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock"); 1432 Thread.CurrentThread.Abort();
963 } 1433 }
964 } 1434 }
965 1435
966
967
968 /// <summary> 1436 /// <summary>
969 /// Force the SmartThreadPool to shutdown 1437 /// Thread Pool start information (readonly)
970 /// </summary> 1438 /// </summary>
971 public void Shutdown() 1439 public STPStartInfo STPStartInfo
972 { 1440 {
973 Shutdown(true, 0); 1441 get
1442 {
1443 return _stpStartInfo.AsReadOnly();
1444 }
974 } 1445 }
975 1446
976 public void Shutdown(bool forceAbort, TimeSpan timeout) 1447 public bool IsShuttingdown
977 { 1448 {
978 Shutdown(forceAbort, (int)timeout.TotalMilliseconds); 1449 get { return _shutdown; }
979 } 1450 }
980 1451
981 /// <summary> 1452 /// <summary>
982 /// Empties the queue of work items and abort the threads in the pool. 1453 /// Return the local calculated performance counters
1454 /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true.
983 /// </summary> 1455 /// </summary>
984 public void Shutdown(bool forceAbort, int millisecondsTimeout) 1456 public ISTPPerformanceCountersReader PerformanceCountersReader
985 { 1457 {
986 ValidateNotDisposed(); 1458 get { return (ISTPPerformanceCountersReader)_localPCs; }
987 1459 }
988 ISTPInstancePerformanceCounters pcs = _pcs;
989
990 if (NullSTPInstancePerformanceCounters.Instance != _pcs)
991 {
992 _pcs.Dispose();
993 // Set the _pcs to "null" to stop updating the performance
994 // counters
995 _pcs = NullSTPInstancePerformanceCounters.Instance;
996 }
997
998 Thread [] threads = null;
999 lock(_workerThreads.SyncRoot)
1000 {
1001 // Shutdown the work items queue
1002 _workItemsQueue.Dispose();
1003
1004 // Signal the threads to exit
1005 _shutdown = true;
1006 _shuttingDownEvent.Set();
1007 1460
1008 // Make a copy of the threads' references in the pool 1461 #endregion
1009 threads = new Thread [_workerThreads.Count];
1010 _workerThreads.Keys.CopyTo(threads, 0);
1011 }
1012 1462
1013 int millisecondsLeft = millisecondsTimeout; 1463 #region IDisposable Members
1014 DateTime start = DateTime.Now;
1015 bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
1016 bool timeout = false;
1017 1464
1018 // Each iteration we update the time left for the timeout. 1465 public void Dispose()
1019 foreach(Thread thread in threads) 1466 {
1467 if (!_isDisposed)
1020 { 1468 {
1021 // Join don't work with negative numbers 1469 if (!_shutdown)
1022 if (!waitInfinitely && (millisecondsLeft < 0))
1023 { 1470 {
1024 timeout = true; 1471 Shutdown();
1025 break;
1026 } 1472 }
1027 1473
1028 // Wait for the thread to terminate 1474 if (null != _shuttingDownEvent)
1029 bool success = thread.Join(millisecondsLeft);
1030 if(!success)
1031 { 1475 {
1032 timeout = true; 1476 _shuttingDownEvent.Close();
1033 break; 1477 _shuttingDownEvent = null;
1034 } 1478 }
1035 1479 _workerThreads.Clear();
1036 if(!waitInfinitely) 1480
1481 if (null != _isIdleWaitHandle)
1037 { 1482 {
1038 // Update the time left to wait 1483 _isIdleWaitHandle.Close();
1039 TimeSpan ts = DateTime.Now - start; 1484 _isIdleWaitHandle = null;
1040 millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
1041 } 1485 }
1042 }
1043 1486
1044 if (timeout && forceAbort) 1487 _isDisposed = true;
1045 {
1046 // Abort the threads in the pool
1047 foreach(Thread thread in threads)
1048 {
1049 if ((thread != null) && thread.IsAlive)
1050 {
1051 try
1052 {
1053 thread.Abort("Shutdown");
1054 }
1055 catch(SecurityException e)
1056 {
1057 e.GetHashCode();
1058 }
1059 catch(ThreadStateException ex)
1060 {
1061 ex.GetHashCode();
1062 // In case the thread has been terminated
1063 // after the check if it is alive.
1064 }
1065 }
1066 }
1067 } 1488 }
1068
1069 // Dispose of the performance counters
1070 pcs.Dispose();
1071 } 1489 }
1072 1490
1073 /// <summary> 1491 private void ValidateNotDisposed()
1074 /// Wait for all work items to complete
1075 /// </summary>
1076 /// <param name="workItemResults">Array of work item result objects</param>
1077 /// <returns>
1078 /// true when every work item in workItemResults has completed; otherwise false.
1079 /// </returns>
1080 public static bool WaitAll(
1081 IWorkItemResult [] workItemResults)
1082 { 1492 {
1083 return WaitAll(workItemResults, Timeout.Infinite, true); 1493 if(_isDisposed)
1494 {
1495 throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
1496 }
1084 } 1497 }
1498 #endregion
1499
1500 #region WorkItemsGroupBase Overrides
1085 1501
1086 /// <summary> 1502 /// <summary>
1087 /// Wait for all work items to complete 1503 /// Get/Set the maximum number of work items that execute cocurrency on the thread pool
1088 /// </summary> 1504 /// </summary>
1089 /// <param name="workItemResults">Array of work item result objects</param> 1505 public override int Concurrency
1090 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> 1506 {
1091 /// <param name="exitContext"> 1507 get { return MaxThreads; }
1092 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 1508 set { MaxThreads = value; }
1093 /// </param> 1509 }
1094 /// <returns> 1510
1095 /// true when every work item in workItemResults has completed; otherwise false. 1511 /// <summary>
1096 /// </returns> 1512 /// Get the number of work items in the queue.
1097 public static bool WaitAll( 1513 /// </summary>
1098 IWorkItemResult [] workItemResults, 1514 public override int WaitingCallbacks
1099 TimeSpan timeout, 1515 {
1100 bool exitContext) 1516 get
1101 { 1517 {
1102 return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext); 1518 ValidateNotDisposed();
1103 } 1519 return _workItemsQueue.Count;
1520 }
1521 }
1104 1522
1105 /// <summary> 1523 /// <summary>
1106 /// Wait for all work items to complete 1524 /// Get an array with all the state objects of the currently running items.
1525 /// The array represents a snap shot and impact performance.
1107 /// </summary> 1526 /// </summary>
1108 /// <param name="workItemResults">Array of work item result objects</param> 1527 public override object[] GetStates()
1109 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
1110 /// <param name="exitContext">
1111 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1112 /// </param>
1113 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1114 /// <returns>
1115 /// true when every work item in workItemResults has completed; otherwise false.
1116 /// </returns>
1117 public static bool WaitAll(
1118 IWorkItemResult [] workItemResults,
1119 TimeSpan timeout,
1120 bool exitContext,
1121 WaitHandle cancelWaitHandle)
1122 { 1528 {
1123 return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); 1529 object[] states = _workItemsQueue.GetStates();
1530 return states;
1124 } 1531 }
1125 1532
1126 /// <summary> 1533 /// <summary>
1127 /// Wait for all work items to complete 1534 /// WorkItemsGroup start information (readonly)
1128 /// </summary> 1535 /// </summary>
1129 /// <param name="workItemResults">Array of work item result objects</param> 1536 public override WIGStartInfo WIGStartInfo
1130 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1131 /// <param name="exitContext">
1132 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1133 /// </param>
1134 /// <returns>
1135 /// true when every work item in workItemResults has completed; otherwise false.
1136 /// </returns>
1137 public static bool WaitAll(
1138 IWorkItemResult [] workItemResults,
1139 int millisecondsTimeout,
1140 bool exitContext)
1141 { 1537 {
1142 return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null); 1538 get { return _stpStartInfo.AsReadOnly(); }
1143 } 1539 }
1144 1540
1145 /// <summary> 1541 /// <summary>
1146 /// Wait for all work items to complete 1542 /// Start the thread pool if it was started suspended.
1543 /// If it is already running, this method is ignored.
1147 /// </summary> 1544 /// </summary>
1148 /// <param name="workItemResults">Array of work item result objects</param> 1545 public override void Start()
1149 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1150 /// <param name="exitContext">
1151 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1152 /// </param>
1153 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1154 /// <returns>
1155 /// true when every work item in workItemResults has completed; otherwise false.
1156 /// </returns>
1157 public static bool WaitAll(
1158 IWorkItemResult [] workItemResults,
1159 int millisecondsTimeout,
1160 bool exitContext,
1161 WaitHandle cancelWaitHandle)
1162 { 1546 {
1163 return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle); 1547 if (!_isSuspended)
1164 } 1548 {
1549 return;
1550 }
1551 _isSuspended = false;
1165 1552
1553 ICollection workItemsGroups = _workItemsGroups.Values;
1554 foreach (WorkItemsGroup workItemsGroup in workItemsGroups)
1555 {
1556 workItemsGroup.OnSTPIsStarting();
1557 }
1166 1558
1167 /// <summary> 1559 StartOptimalNumberOfThreads();
1168 /// Waits for any of the work items in the specified array to complete, cancel, or timeout
1169 /// </summary>
1170 /// <param name="workItemResults">Array of work item result objects</param>
1171 /// <returns>
1172 /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
1173 /// </returns>
1174 public static int WaitAny(
1175 IWorkItemResult [] workItemResults)
1176 {
1177 return WaitAny(workItemResults, Timeout.Infinite, true);
1178 } 1560 }
1179 1561
1180 /// <summary> 1562 /// <summary>
1181 /// Waits for any of the work items in the specified array to complete, cancel, or timeout 1563 /// Cancel all work items using thread abortion
1182 /// </summary> 1564 /// </summary>
1183 /// <param name="workItemResults">Array of work item result objects</param> 1565 /// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
1184 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> 1566 public override void Cancel(bool abortExecution)
1185 /// <param name="exitContext">
1186 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1187 /// </param>
1188 /// <returns>
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.
1190 /// </returns>
1191 public static int WaitAny(
1192 IWorkItemResult [] workItemResults,
1193 TimeSpan timeout,
1194 bool exitContext)
1195 { 1567 {
1196 return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext); 1568 _canceledSmartThreadPool.IsCanceled = true;
1197 } 1569 _canceledSmartThreadPool = new CanceledWorkItemsGroup();
1198 1570
1199 /// <summary> 1571 ICollection workItemsGroups = _workItemsGroups.Values;
1200 /// Waits for any of the work items in the specified array to complete, cancel, or timeout 1572 foreach (WorkItemsGroup workItemsGroup in workItemsGroups)
1201 /// </summary> 1573 {
1202 /// <param name="workItemResults">Array of work item result objects</param> 1574 workItemsGroup.Cancel(abortExecution);
1203 /// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param> 1575 }
1204 /// <param name="exitContext"> 1576
1205 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 1577 if (abortExecution)
1206 /// </param> 1578 {
1207 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> 1579 foreach (ThreadEntry threadEntry in _workerThreads.Values)
1208 /// <returns> 1580 {
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. 1581 WorkItem workItem = threadEntry.CurrentWorkItem;
1210 /// </returns> 1582 if (null != workItem &&
1211 public static int WaitAny( 1583 threadEntry.AssociatedSmartThreadPool == this &&
1212 IWorkItemResult [] workItemResults, 1584 !workItem.IsCanceled)
1213 TimeSpan timeout, 1585 {
1214 bool exitContext, 1586 threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true);
1215 WaitHandle cancelWaitHandle) 1587 }
1216 { 1588 }
1217 return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); 1589 }
1218 } 1590 }
1219 1591
1220 /// <summary> 1592 /// <summary>
1221 /// Waits for any of the work items in the specified array to complete, cancel, or timeout 1593 /// Wait for the thread pool to be idle
1222 /// </summary> 1594 /// </summary>
1223 /// <param name="workItemResults">Array of work item result objects</param> 1595 public override bool WaitForIdle(int millisecondsTimeout)
1224 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1225 /// <param name="exitContext">
1226 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1227 /// </param>
1228 /// <returns>
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.
1230 /// </returns>
1231 public static int WaitAny(
1232 IWorkItemResult [] workItemResults,
1233 int millisecondsTimeout,
1234 bool exitContext)
1235 { 1596 {
1236 return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null); 1597 ValidateWaitForIdle();
1598 return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
1237 } 1599 }
1238 1600
1239 /// <summary> 1601 /// <summary>
1240 /// Waits for any of the work items in the specified array to complete, cancel, or timeout 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.
1241 /// </summary> 1606 /// </summary>
1242 /// <param name="workItemResults">Array of work item result objects</param> 1607 public override event WorkItemsGroupIdleHandler OnIdle
1243 /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
1244 /// <param name="exitContext">
1245 /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
1246 /// </param>
1247 /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
1248 /// <returns>
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.
1250 /// </returns>
1251 public static int WaitAny(
1252 IWorkItemResult [] workItemResults,
1253 int millisecondsTimeout,
1254 bool exitContext,
1255 WaitHandle cancelWaitHandle)
1256 {
1257 return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
1258 }
1259
1260 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency)
1261 {
1262 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo);
1263 return workItemsGroup;
1264 }
1265
1266 public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo)
1267 {
1268 IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo);
1269 return workItemsGroup;
1270 }
1271
1272 public event WorkItemsGroupIdleHandler OnIdle
1273 { 1608 {
1274 add 1609 add
1275 { 1610 {
@@ -1283,166 +1618,115 @@ namespace Amib.Threading
1283 } 1618 }
1284 } 1619 }
1285 1620
1286 public void Cancel() 1621 internal override void PreQueueWorkItem()
1287 {
1288 ICollection workItemsGroups = _workItemsGroups.Values;
1289 foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
1290 {
1291 workItemsGroup.Cancel();
1292 }
1293 }
1294
1295 public void Start()
1296 { 1622 {
1297 lock (this) 1623 ValidateNotDisposed();
1298 {
1299 if (!this._stpStartInfo.StartSuspended)
1300 {
1301 return;
1302 }
1303 _stpStartInfo.StartSuspended = false;
1304 }
1305
1306 ICollection workItemsGroups = _workItemsGroups.Values;
1307 foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
1308 {
1309 workItemsGroup.OnSTPIsStarting();
1310 }
1311
1312 StartOptimalNumberOfThreads();
1313 } 1624 }
1314 1625
1315 #endregion 1626 #endregion
1316 1627
1317 #region Properties 1628 #region Join, Choice, Pipe, etc.
1318 1629
1319 /// <summary> 1630 /// <summary>
1320 /// Get/Set the name of the SmartThreadPool instance 1631 /// Executes all actions in parallel.
1632 /// Returns when they all finish.
1321 /// </summary> 1633 /// </summary>
1322 public string Name 1634 /// <param name="actions">Actions to execute</param>
1323 { 1635 public void Join(IEnumerable<Action> actions)
1324 get 1636 {
1325 { 1637 WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
1326 return _name; 1638 IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo);
1327 } 1639 foreach (Action action in actions)
1328
1329 set
1330 {
1331 _name = value;
1332 }
1333 }
1334
1335 /// <summary>
1336 /// Get the lower limit of threads in the pool.
1337 /// </summary>
1338 public int MinThreads
1339 {
1340 get
1341 { 1640 {
1342 ValidateNotDisposed(); 1641 workItemsGroup.QueueWorkItem(action);
1343 return _stpStartInfo.MinWorkerThreads;
1344 } 1642 }
1643 workItemsGroup.Start();
1644 workItemsGroup.WaitForIdle();
1345 } 1645 }
1346 1646
1347 /// <summary> 1647 /// <summary>
1348 /// Get the upper limit of threads in the pool. 1648 /// Executes all actions in parallel.
1349 /// </summary> 1649 /// Returns when they all finish.
1350 public int MaxThreads
1351 {
1352 get
1353 {
1354 ValidateNotDisposed();
1355 return _stpStartInfo.MaxWorkerThreads;
1356 }
1357 }
1358 /// <summary>
1359 /// Get the number of threads in the thread pool.
1360 /// Should be between the lower and the upper limits.
1361 /// </summary> 1650 /// </summary>
1362 public int ActiveThreads 1651 /// <param name="actions">Actions to execute</param>
1363 { 1652 public void Join(params Action[] actions)
1364 get 1653 {
1365 { 1654 Join((IEnumerable<Action>)actions);
1366 ValidateNotDisposed();
1367 return _workerThreads.Count;
1368 }
1369 } 1655 }
1370 1656
1371 /// <summary> 1657 private class ChoiceIndex
1372 /// Get the number of busy (not idle) threads in the thread pool. 1658 {
1373 /// </summary> 1659 public int _index = -1;
1374 public int InUseThreads
1375 {
1376 get
1377 {
1378 ValidateNotDisposed();
1379 return _inUseWorkerThreads;
1380 }
1381 } 1660 }
1382 1661
1383 /// <summary> 1662 /// <summary>
1384 /// Get the number of work items in the queue. 1663 /// Executes all actions in parallel
1664 /// Returns when the first one completes
1385 /// </summary> 1665 /// </summary>
1386 public int WaitingCallbacks 1666 /// <param name="actions">Actions to execute</param>
1387 { 1667 public int Choice(IEnumerable<Action> actions)
1388 get 1668 {
1389 { 1669 WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
1390 ValidateNotDisposed(); 1670 IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo);
1391 return _workItemsQueue.Count;
1392 }
1393 }
1394 1671
1672 ManualResetEvent anActionCompleted = new ManualResetEvent(false);
1395 1673
1396 public event EventHandler Idle 1674 ChoiceIndex choiceIndex = new ChoiceIndex();
1397 { 1675
1398 add 1676 int i = 0;
1677 foreach (Action action in actions)
1399 { 1678 {
1400 _stpIdle += value; 1679 Action act = action;
1680 int value = i;
1681 workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); });
1682 ++i;
1401 } 1683 }
1684 workItemsGroup.Start();
1685 anActionCompleted.WaitOne();
1402 1686
1403 remove 1687 return choiceIndex._index;
1404 {
1405 _stpIdle -= value;
1406 }
1407 } 1688 }
1408 1689
1409 #endregion 1690 /// <summary>
1410 1691 /// Executes all actions in parallel
1411 #region IDisposable Members 1692 /// Returns when the first one completes
1412 1693 /// </summary>
1413// ~SmartThreadPool() 1694 /// <param name="actions">Actions to execute</param>
1414// { 1695 public int Choice(params Action[] actions)
1415// Dispose(); 1696 {
1416// } 1697 return Choice((IEnumerable<Action>)actions);
1698 }
1417 1699
1418 public void Dispose() 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)
1419 { 1707 {
1420 if (!_isDisposed) 1708 WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
1709 IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo);
1710 foreach (Action<T> action in actions)
1421 { 1711 {
1422 if (!_shutdown) 1712 Action<T> act = action;
1423 { 1713 workItemsGroup.QueueWorkItem(() => act(pipeState));
1424 Shutdown();
1425 }
1426
1427 if (null != _shuttingDownEvent)
1428 {
1429 _shuttingDownEvent.Close();
1430 _shuttingDownEvent = null;
1431 }
1432 _workerThreads.Clear();
1433 _isDisposed = true;
1434 GC.SuppressFinalize(this);
1435 } 1714 }
1715 workItemsGroup.Start();
1716 workItemsGroup.WaitForIdle();
1436 } 1717 }
1437 1718
1438 private void ValidateNotDisposed() 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)
1439 { 1726 {
1440 if(_isDisposed) 1727 Pipe(pipeState, (IEnumerable<Action<T>>)actions);
1441 {
1442 throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
1443 }
1444 } 1728 }
1445 #endregion 1729 #endregion
1446 } 1730 }
1447 #endregion 1731 #endregion
1448} 1732}
diff --git a/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs
new file mode 100644
index 0000000..0cce19f
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs
@@ -0,0 +1,89 @@
1using System.Collections.Generic;
2
3namespace 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..8af195b 100644
--- a/ThirdParty/SmartThreadPool/WIGStartInfo.cs
+++ b/ThirdParty/SmartThreadPool/WIGStartInfo.cs
@@ -1,99 +1,171 @@
1// Ami Bar 1using System;
2// amibar@gmail.com
3 2
4namespace Amib.Threading 3namespace Amib.Threading
5{ 4{
6 /// <summary> 5 /// <summary>
7 /// Summary description for WIGStartInfo. 6 /// Summary description for WIGStartInfo.
8 /// </summary> 7 /// </summary>
9 public class WIGStartInfo 8 public class WIGStartInfo
10 { 9 {
11 /// <summary>
12 /// Use the caller's security context
13 /// </summary>
14 private bool _useCallerCallContext; 10 private bool _useCallerCallContext;
15
16 /// <summary>
17 /// Use the caller's HTTP context
18 /// </summary>
19 private bool _useCallerHttpContext; 11 private bool _useCallerHttpContext;
20
21 /// <summary>
22 /// Dispose of the state object of a work item
23 /// </summary>
24 private bool _disposeOfStateObjects; 12 private bool _disposeOfStateObjects;
25
26 /// <summary>
27 /// The option to run the post execute
28 /// </summary>
29 private CallToPostExecute _callToPostExecute; 13 private CallToPostExecute _callToPostExecute;
30
31 /// <summary>
32 /// A post execute callback to call when none is provided in
33 /// the QueueWorkItem method.
34 /// </summary>
35 private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; 14 private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
36
37 /// <summary>
38 /// Indicate the WorkItemsGroup to suspend the handling of the work items
39 /// until the Start() method is called.
40 /// </summary>
41 private bool _startSuspended; 15 private bool _startSuspended;
16 private WorkItemPriority _workItemPriority;
17 private bool _fillStateWithArgs;
42 18
43 public WIGStartInfo() 19 protected bool _readOnly;
20
21 public WIGStartInfo()
44 { 22 {
45 _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; 23 _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
46 _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; 24 _workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
47 _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
48 _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
49 _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
50 _startSuspended = SmartThreadPool.DefaultStartSuspended; 25 _startSuspended = SmartThreadPool.DefaultStartSuspended;
26 _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
27 _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
28 _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
29 _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
30 _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
51 } 31 }
52 32
53 public WIGStartInfo(WIGStartInfo wigStartInfo) 33 public WIGStartInfo(WIGStartInfo wigStartInfo)
54 { 34 {
55 _useCallerCallContext = wigStartInfo._useCallerCallContext; 35 _useCallerCallContext = wigStartInfo.UseCallerCallContext;
56 _useCallerHttpContext = wigStartInfo._useCallerHttpContext; 36 _useCallerHttpContext = wigStartInfo.UseCallerHttpContext;
57 _disposeOfStateObjects = wigStartInfo._disposeOfStateObjects; 37 _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
58 _callToPostExecute = wigStartInfo._callToPostExecute; 38 _callToPostExecute = wigStartInfo.CallToPostExecute;
59 _postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback; 39 _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
60 _startSuspended = wigStartInfo._startSuspended; 40 _workItemPriority = wigStartInfo.WorkItemPriority;
41 _startSuspended = wigStartInfo.StartSuspended;
42 _fillStateWithArgs = wigStartInfo.FillStateWithArgs;
61 } 43 }
62 44
63 public bool UseCallerCallContext 45 protected void ThrowIfReadOnly()
64 { 46 {
65 get { return _useCallerCallContext; } 47 if (_readOnly)
66 set { _useCallerCallContext = value; } 48 {
49 throw new NotSupportedException("This is a readonly instance and set is not supported");
50 }
67 } 51 }
68 52
69 public bool UseCallerHttpContext 53 /// <summary>
70 { 54 /// Get/Set if to use the caller's security context
71 get { return _useCallerHttpContext; } 55 /// </summary>
72 set { _useCallerHttpContext = value; } 56 public virtual bool UseCallerCallContext
73 } 57 {
58 get { return _useCallerCallContext; }
59 set
60 {
61 ThrowIfReadOnly();
62 _useCallerCallContext = value;
63 }
64 }
74 65
75 public bool DisposeOfStateObjects
76 {
77 get { return _disposeOfStateObjects; }
78 set { _disposeOfStateObjects = value; }
79 }
80 66
81 public CallToPostExecute CallToPostExecute 67 /// <summary>
82 { 68 /// Get/Set if to use the caller's HTTP context
83 get { return _callToPostExecute; } 69 /// </summary>
84 set { _callToPostExecute = value; } 70 public virtual bool UseCallerHttpContext
85 } 71 {
72 get { return _useCallerHttpContext; }
73 set
74 {
75 ThrowIfReadOnly();
76 _useCallerHttpContext = value;
77 }
78 }
86 79
87 public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
88 {
89 get { return _postExecuteWorkItemCallback; }
90 set { _postExecuteWorkItemCallback = value; }
91 }
92 80
93 public bool StartSuspended 81 /// <summary>
94 { 82 /// Get/Set if to dispose of the state object of a work item
95 get { return _startSuspended; } 83 /// </summary>
96 set { _startSuspended = value; } 84 public virtual bool DisposeOfStateObjects
97 } 85 {
86 get { return _disposeOfStateObjects; }
87 set
88 {
89 ThrowIfReadOnly();
90 _disposeOfStateObjects = value;
91 }
92 }
93
94
95 /// <summary>
96 /// Get/Set the run the post execute options
97 /// </summary>
98 public virtual CallToPostExecute CallToPostExecute
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&lt;...&gt;/Func&lt;...&gt; 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 }
98 } 170 }
99} 171}
diff --git a/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs
new file mode 100644
index 0000000..435a14b
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs
@@ -0,0 +1,190 @@
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading;
5
6namespace 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..185f10c 100644
--- a/ThirdParty/SmartThreadPool/WorkItem.cs
+++ b/ThirdParty/SmartThreadPool/WorkItem.cs
@@ -1,58 +1,13 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System; 1using System;
5using System.Threading; 2using System.Threading;
6using System.Diagnostics; 3using System.Diagnostics;
7 4
8namespace Amib.Threading.Internal 5namespace 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,93 @@ 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 // **************************
757 // Stock SmartThreadPool 2.2.3 sets these to true and relies on the thread to check the
758 // WorkItem cancellation status. However, OpenSimulator uses a different mechanism to notify
759 // scripts of co-operative termination and the abort code also relies on this method
760 // returning false in order to implement a small wait.
761 //
762 // Therefore, as was the case previously with STP, we will not signal successful cancellation
763 // here. It's possible that OpenSimulator code could be changed in the future to remove
764 // the need for this change.
765 // **************************
766 success = false;
767 signalComplete = false;
768 }
769 break;
633 case WorkItemState.InQueue: 770 case WorkItemState.InQueue:
634 // Signal to the wait for completion that the work 771 // Signal to the wait for completion that the work
635 // item has been completed (canceled). There is no 772 // item has been completed (canceled). There is no
636 // reason to wait for it to get out of the queue 773 // reason to wait for it to get out of the queue
637 SignalComplete(true); 774 signalComplete = true;
638 //Debug.WriteLine("Work item canceled"); 775 //Debug.WriteLine("Work item canceled");
639 return true; 776 success = true;
777 break;
778 }
779
780 if (signalComplete)
781 {
782 SignalComplete(true);
640 } 783 }
641 } 784 }
642 return false; 785 return success;
643 } 786 }
644 787
645 /// <summary> 788 /// <summary>
@@ -653,7 +796,7 @@ namespace Amib.Threading.Internal
653 bool exitContext, 796 bool exitContext,
654 WaitHandle cancelWaitHandle) 797 WaitHandle cancelWaitHandle)
655 { 798 {
656 Exception e = null; 799 Exception e;
657 object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); 800 object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
658 if (null != e) 801 if (null != e)
659 { 802 {
@@ -694,7 +837,7 @@ namespace Amib.Threading.Internal
694 { 837 {
695 WaitHandle wh = GetWaitHandle(); 838 WaitHandle wh = GetWaitHandle();
696 839
697 bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext); 840 bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext);
698 841
699 ReleaseWaitHandle(); 842 ReleaseWaitHandle();
700 843
@@ -706,10 +849,10 @@ namespace Amib.Threading.Internal
706 else 849 else
707 { 850 {
708 WaitHandle wh = GetWaitHandle(); 851 WaitHandle wh = GetWaitHandle();
709 int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); 852 int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
710 ReleaseWaitHandle(); 853 ReleaseWaitHandle();
711 854
712 switch(result) 855 switch (result)
713 { 856 {
714 case 0: 857 case 0:
715 // The work item signaled 858 // The work item signaled
@@ -717,7 +860,7 @@ namespace Amib.Threading.Internal
717 // work item (not the get result) 860 // work item (not the get result)
718 break; 861 break;
719 case 1: 862 case 1:
720 case WaitHandle.WaitTimeout: 863 case STPEventWaitHandle.WaitTimeout:
721 throw new WorkItemTimeoutException("Work item timeout"); 864 throw new WorkItemTimeoutException("Work item timeout");
722 default: 865 default:
723 Debug.Assert(false); 866 Debug.Assert(false);
@@ -745,11 +888,11 @@ namespace Amib.Threading.Internal
745 /// </summary> 888 /// </summary>
746 private WaitHandle GetWaitHandle() 889 private WaitHandle GetWaitHandle()
747 { 890 {
748 lock(this) 891 lock (this)
749 { 892 {
750 if (null == _workItemCompleted) 893 if (null == _workItemCompleted)
751 { 894 {
752 _workItemCompleted = new ManualResetEvent(IsCompleted); 895 _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted);
753 } 896 }
754 ++_workItemCompletedRefCount; 897 ++_workItemCompletedRefCount;
755 } 898 }
@@ -758,7 +901,7 @@ namespace Amib.Threading.Internal
758 901
759 private void ReleaseWaitHandle() 902 private void ReleaseWaitHandle()
760 { 903 {
761 lock(this) 904 lock (this)
762 { 905 {
763 if (null != _workItemCompleted) 906 if (null != _workItemCompleted)
764 { 907 {
@@ -779,10 +922,10 @@ namespace Amib.Threading.Internal
779 { 922 {
780 get 923 get
781 { 924 {
782 lock(this) 925 lock (this)
783 { 926 {
784 WorkItemState workItemState = GetWorkItemState(); 927 WorkItemState workItemState = GetWorkItemState();
785 return ((workItemState == WorkItemState.Completed) || 928 return ((workItemState == WorkItemState.Completed) ||
786 (workItemState == WorkItemState.Canceled)); 929 (workItemState == WorkItemState.Canceled));
787 } 930 }
788 } 931 }
@@ -795,7 +938,7 @@ namespace Amib.Threading.Internal
795 { 938 {
796 get 939 get
797 { 940 {
798 lock(this) 941 lock (this)
799 { 942 {
800 return (GetWorkItemState() == WorkItemState.Canceled); 943 return (GetWorkItemState() == WorkItemState.Canceled);
801 } 944 }
@@ -843,172 +986,6 @@ namespace Amib.Threading.Internal
843 } 986 }
844 } 987 }
845 988
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() 989 public void DisposeOfState()
1013 { 990 {
1014 if (_workItemInfo.DisposeOfStateObjects) 991 if (_workItemInfo.DisposeOfStateObjects)
@@ -1021,15 +998,5 @@ namespace Amib.Threading.Internal
1021 } 998 }
1022 } 999 }
1023 } 1000 }
1024
1025 public void Abort()
1026 {
1027 lock (this)
1028 {
1029 if(currentThread != null)
1030 currentThread.Abort();
1031 }
1032 }
1033 } 1001 }
1034 #endregion
1035} 1002}
diff --git a/ThirdParty/SmartThreadPool/WorkItemFactory.cs b/ThirdParty/SmartThreadPool/WorkItemFactory.cs
index dfcb54f..16ccd81 100644
--- a/ThirdParty/SmartThreadPool/WorkItemFactory.cs
+++ b/ThirdParty/SmartThreadPool/WorkItemFactory.cs
@@ -1,148 +1,152 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System; 1using System;
5 2
6namespace Amib.Threading.Internal 3namespace Amib.Threading.Internal
7{ 4{
8 #region WorkItemFactory class 5 #region WorkItemFactory class
9 6
10 public class WorkItemFactory 7 public class WorkItemFactory
11 { 8 {
12 /// <summary> 9 /// <summary>
13 /// Create a new work item 10 /// Create a new work item
14 /// </summary> 11 /// </summary>
12 /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
13 /// <param name="wigStartInfo">Work item group start information</param>
14 /// <param name="callback">A callback to execute</param>
15 /// <returns>Returns a work item</returns>
16 public static WorkItem CreateWorkItem(
17 IWorkItemsGroup workItemsGroup,
18 WIGStartInfo wigStartInfo,
19 WorkItemCallback callback)
20 {
21 return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null);
22 }
23
24 /// <summary>
25 /// Create a new work item
26 /// </summary>
27 /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
15 /// <param name="wigStartInfo">Work item group start information</param> 28 /// <param name="wigStartInfo">Work item group start information</param>
16 /// <param name="callback">A callback to execute</param> 29 /// <param name="callback">A callback to execute</param>
17 /// <returns>Returns a work item</returns> 30 /// <param name="workItemPriority">The priority of the work item</param>
18 public static WorkItem CreateWorkItem( 31 /// <returns>Returns a work item</returns>
19 IWorkItemsGroup workItemsGroup, 32 public static WorkItem CreateWorkItem(
20 WIGStartInfo wigStartInfo, 33 IWorkItemsGroup workItemsGroup,
21 WorkItemCallback callback) 34 WIGStartInfo wigStartInfo,
22 { 35 WorkItemCallback callback,
23 return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); 36 WorkItemPriority workItemPriority)
24 } 37 {
25 38 return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority);
26 /// <summary> 39 }
27 /// Create a new work item 40
28 /// </summary> 41 /// <summary>
29 /// <param name="wigStartInfo">Work item group start information</param> 42 /// Create a new work item
30 /// <param name="callback">A callback to execute</param> 43 /// </summary>
31 /// <param name="workItemPriority">The priority of the work item</param> 44 /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
32 /// <returns>Returns a work item</returns>
33 public static WorkItem CreateWorkItem(
34 IWorkItemsGroup workItemsGroup,
35 WIGStartInfo wigStartInfo,
36 WorkItemCallback callback,
37 WorkItemPriority workItemPriority)
38 {
39 return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority);
40 }
41
42 /// <summary>
43 /// Create a new work item
44 /// </summary>
45 /// <param name="wigStartInfo">Work item group start information</param> 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="workItemsGroup">The WorkItemsGroup of this workitem</param>
66 /// <param name="wigStartInfo">Work item group start information</param> 67 /// <param name="wigStartInfo">Work item group start information</param>
67 /// <param name="callback">A callback to execute</param> 68 /// <param name="callback">A callback to execute</param>
68 /// <param name="state"> 69 /// <param name="state">
69 /// The context object of the work item. Used for passing arguments to the work item. 70 /// The context object of the work item. Used for passing arguments to the work item.
70 /// </param> 71 /// </param>
71 /// <returns>Returns a work item</returns> 72 /// <returns>Returns a work item</returns>
72 public static WorkItem CreateWorkItem( 73 public static WorkItem CreateWorkItem(
73 IWorkItemsGroup workItemsGroup, 74 IWorkItemsGroup workItemsGroup,
74 WIGStartInfo wigStartInfo, 75 WIGStartInfo wigStartInfo,
75 WorkItemCallback callback, 76 WorkItemCallback callback,
76 object state) 77 object state)
77 { 78 {
78 ValidateCallback(callback); 79 ValidateCallback(callback);
79 80
80 WorkItemInfo workItemInfo = new WorkItemInfo(); 81 WorkItemInfo workItemInfo = new WorkItemInfo();
81 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; 82 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
82 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; 83 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
83 workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; 84 workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
84 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; 85 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
85 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; 86 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
86 87 workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
87 WorkItem workItem = new WorkItem( 88
88 workItemsGroup, 89 WorkItem workItem = new WorkItem(
89 workItemInfo, 90 workItemsGroup,
90 callback, 91 workItemInfo,
91 state); 92 callback,
92 return workItem; 93 state);
93 } 94 return workItem;
94 95 }
95 /// <summary> 96
96 /// Create a new work item 97 /// <summary>
97 /// </summary> 98 /// Create a new work item
98 /// <param name="wigStartInfo">Work item group start information</param> 99 /// </summary>
99 /// <param name="callback">A callback to execute</param> 100 /// <param name="workItemsGroup">The work items group</param>
100 /// <param name="state"> 101 /// <param name="wigStartInfo">Work item group start information</param>
101 /// The context object of the work item. Used for passing arguments to the work item. 102 /// <param name="callback">A callback to execute</param>
102 /// </param> 103 /// <param name="state">
103 /// <param name="workItemPriority">The work item priority</param> 104 /// The context object of the work item. Used for passing arguments to the work item.
104 /// <returns>Returns a work item</returns> 105 /// </param>
105 public static WorkItem CreateWorkItem( 106 /// <param name="workItemPriority">The work item priority</param>
106 IWorkItemsGroup workItemsGroup, 107 /// <returns>Returns a work item</returns>
107 WIGStartInfo wigStartInfo, 108 public static WorkItem CreateWorkItem(
108 WorkItemCallback callback, 109 IWorkItemsGroup workItemsGroup,
109 object state, 110 WIGStartInfo wigStartInfo,
110 WorkItemPriority workItemPriority) 111 WorkItemCallback callback,
111 { 112 object state,
112 ValidateCallback(callback); 113 WorkItemPriority workItemPriority)
113 114 {
114 WorkItemInfo workItemInfo = new WorkItemInfo(); 115 ValidateCallback(callback);
115 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; 116
116 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; 117 WorkItemInfo workItemInfo = new WorkItemInfo();
117 workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; 118 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
118 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; 119 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
119 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; 120 workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
120 workItemInfo.WorkItemPriority = workItemPriority; 121 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
121 122 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
122 WorkItem workItem = new WorkItem( 123 workItemInfo.WorkItemPriority = workItemPriority;
123 workItemsGroup, 124
124 workItemInfo, 125 WorkItem workItem = new WorkItem(
125 callback, 126 workItemsGroup,
126 state); 127 workItemInfo,
127 128 callback,
128 return workItem; 129 state);
129 } 130
130 131 return workItem;
131 /// <summary> 132 }
132 /// Create a new work item 133
133 /// </summary> 134 /// <summary>
134 /// <param name="wigStartInfo">Work item group start information</param> 135 /// Create a new work item
135 /// <param name="workItemInfo">Work item information</param> 136 /// </summary>
136 /// <param name="callback">A callback to execute</param> 137 /// <param name="workItemsGroup">The work items group</param>
137 /// <param name="state"> 138 /// <param name="wigStartInfo">Work item group start information</param>
138 /// The context object of the work item. Used for passing arguments to the work item. 139 /// <param name="workItemInfo">Work item information</param>
139 /// </param> 140 /// <param name="callback">A callback to execute</param>
140 /// <returns>Returns a work item</returns> 141 /// <param name="state">
142 /// The context object of the work item. Used for passing arguments to the work item.
143 /// </param>
144 /// <returns>Returns a work item</returns>
141 public static WorkItem CreateWorkItem( 145 public static WorkItem CreateWorkItem(
142 IWorkItemsGroup workItemsGroup, 146 IWorkItemsGroup workItemsGroup,
143 WIGStartInfo wigStartInfo, 147 WIGStartInfo wigStartInfo,
144 WorkItemInfo workItemInfo, 148 WorkItemInfo workItemInfo,
145 WorkItemCallback callback, 149 WorkItemCallback callback,
146 object state) 150 object state)
147 { 151 {
148 ValidateCallback(callback); 152 ValidateCallback(callback);
@@ -151,183 +155,189 @@ namespace Amib.Threading.Internal
151 WorkItem workItem = new WorkItem( 155 WorkItem workItem = new WorkItem(
152 workItemsGroup, 156 workItemsGroup,
153 new WorkItemInfo(workItemInfo), 157 new WorkItemInfo(workItemInfo),
154 callback, 158 callback,
155 state); 159 state);
156 160
157 return workItem; 161 return workItem;
158 } 162 }
159 163
160 /// <summary> 164 /// <summary>
161 /// Create a new work item 165 /// Create a new work item
162 /// </summary> 166 /// </summary>
163 /// <param name="wigStartInfo">Work item group start information</param> 167 /// <param name="workItemsGroup">The work items group</param>
164 /// <param name="callback">A callback to execute</param> 168 /// <param name="wigStartInfo">Work item group start information</param>
165 /// <param name="state"> 169 /// <param name="callback">A callback to execute</param>
166 /// The context object of the work item. Used for passing arguments to the work item. 170 /// <param name="state">
167 /// </param> 171 /// The context object of the work item. Used for passing arguments to the work item.
168 /// <param name="postExecuteWorkItemCallback"> 172 /// </param>
169 /// A delegate to call after the callback completion 173 /// <param name="postExecuteWorkItemCallback">
170 /// </param> 174 /// A delegate to call after the callback completion
171 /// <returns>Returns a work item</returns> 175 /// </param>
172 public static WorkItem CreateWorkItem( 176 /// <returns>Returns a work item</returns>
173 IWorkItemsGroup workItemsGroup, 177 public static WorkItem CreateWorkItem(
174 WIGStartInfo wigStartInfo, 178 IWorkItemsGroup workItemsGroup,
175 WorkItemCallback callback, 179 WIGStartInfo wigStartInfo,
176 object state, 180 WorkItemCallback callback,
177 PostExecuteWorkItemCallback postExecuteWorkItemCallback) 181 object state,
178 { 182 PostExecuteWorkItemCallback postExecuteWorkItemCallback)
179 ValidateCallback(callback); 183 {
180 ValidateCallback(postExecuteWorkItemCallback); 184 ValidateCallback(callback);
181 185 ValidateCallback(postExecuteWorkItemCallback);
182 WorkItemInfo workItemInfo = new WorkItemInfo(); 186
183 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; 187 WorkItemInfo workItemInfo = new WorkItemInfo();
184 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; 188 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
185 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; 189 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
186 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; 190 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
187 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; 191 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
188 192 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
189 WorkItem workItem = new WorkItem( 193 workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
190 workItemsGroup, 194
191 workItemInfo, 195 WorkItem workItem = new WorkItem(
192 callback, 196 workItemsGroup,
193 state); 197 workItemInfo,
194 198 callback,
195 return workItem; 199 state);
196 } 200
197 201 return workItem;
198 /// <summary> 202 }
199 /// Create a new work item 203
200 /// </summary> 204 /// <summary>
201 /// <param name="wigStartInfo">Work item group start information</param> 205 /// Create a new work item
202 /// <param name="callback">A callback to execute</param> 206 /// </summary>
203 /// <param name="state"> 207 /// <param name="workItemsGroup">The work items group</param>
204 /// The context object of the work item. Used for passing arguments to the work item. 208 /// <param name="wigStartInfo">Work item group start information</param>
205 /// </param> 209 /// <param name="callback">A callback to execute</param>
206 /// <param name="postExecuteWorkItemCallback"> 210 /// <param name="state">
207 /// A delegate to call after the callback completion 211 /// The context object of the work item. Used for passing arguments to the work item.
208 /// </param> 212 /// </param>
209 /// <param name="workItemPriority">The work item priority</param> 213 /// <param name="postExecuteWorkItemCallback">
210 /// <returns>Returns a work item</returns> 214 /// A delegate to call after the callback completion
211 public static WorkItem CreateWorkItem( 215 /// </param>
212 IWorkItemsGroup workItemsGroup, 216 /// <param name="workItemPriority">The work item priority</param>
213 WIGStartInfo wigStartInfo, 217 /// <returns>Returns a work item</returns>
214 WorkItemCallback callback, 218 public static WorkItem CreateWorkItem(
215 object state, 219 IWorkItemsGroup workItemsGroup,
216 PostExecuteWorkItemCallback postExecuteWorkItemCallback, 220 WIGStartInfo wigStartInfo,
217 WorkItemPriority workItemPriority) 221 WorkItemCallback callback,
218 { 222 object state,
219 ValidateCallback(callback); 223 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
220 ValidateCallback(postExecuteWorkItemCallback); 224 WorkItemPriority workItemPriority)
221 225 {
222 WorkItemInfo workItemInfo = new WorkItemInfo(); 226 ValidateCallback(callback);
223 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; 227 ValidateCallback(postExecuteWorkItemCallback);
224 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; 228
225 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; 229 WorkItemInfo workItemInfo = new WorkItemInfo();
226 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; 230 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
227 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; 231 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
228 workItemInfo.WorkItemPriority = workItemPriority; 232 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
229 233 workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
230 WorkItem workItem = new WorkItem( 234 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
231 workItemsGroup, 235 workItemInfo.WorkItemPriority = workItemPriority;
232 workItemInfo, 236
233 callback, 237 WorkItem workItem = new WorkItem(
234 state); 238 workItemsGroup,
235 239 workItemInfo,
236 return workItem; 240 callback,
237 } 241 state);
238 242
239 /// <summary> 243 return workItem;
240 /// Create a new work item 244 }
241 /// </summary> 245
242 /// <param name="wigStartInfo">Work item group start information</param> 246 /// <summary>
243 /// <param name="callback">A callback to execute</param> 247 /// Create a new work item
244 /// <param name="state"> 248 /// </summary>
245 /// The context object of the work item. Used for passing arguments to the work item. 249 /// <param name="workItemsGroup">The work items group</param>
246 /// </param> 250 /// <param name="wigStartInfo">Work item group start information</param>
247 /// <param name="postExecuteWorkItemCallback"> 251 /// <param name="callback">A callback to execute</param>
248 /// A delegate to call after the callback completion 252 /// <param name="state">
249 /// </param> 253 /// The context object of the work item. Used for passing arguments to the work item.
250 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> 254 /// </param>
251 /// <returns>Returns a work item</returns> 255 /// <param name="postExecuteWorkItemCallback">
252 public static WorkItem CreateWorkItem( 256 /// A delegate to call after the callback completion
253 IWorkItemsGroup workItemsGroup, 257 /// </param>
254 WIGStartInfo wigStartInfo, 258 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
255 WorkItemCallback callback, 259 /// <returns>Returns a work item</returns>
256 object state, 260 public static WorkItem CreateWorkItem(
257 PostExecuteWorkItemCallback postExecuteWorkItemCallback, 261 IWorkItemsGroup workItemsGroup,
258 CallToPostExecute callToPostExecute) 262 WIGStartInfo wigStartInfo,
259 { 263 WorkItemCallback callback,
260 ValidateCallback(callback); 264 object state,
261 ValidateCallback(postExecuteWorkItemCallback); 265 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
262 266 CallToPostExecute callToPostExecute)
263 WorkItemInfo workItemInfo = new WorkItemInfo(); 267 {
264 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; 268 ValidateCallback(callback);
265 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; 269 ValidateCallback(postExecuteWorkItemCallback);
266 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; 270
267 workItemInfo.CallToPostExecute = callToPostExecute; 271 WorkItemInfo workItemInfo = new WorkItemInfo();
268 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; 272 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
269 273 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
270 WorkItem workItem = new WorkItem( 274 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
271 workItemsGroup, 275 workItemInfo.CallToPostExecute = callToPostExecute;
272 workItemInfo, 276 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
273 callback, 277 workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
274 state); 278
275 279 WorkItem workItem = new WorkItem(
276 return workItem; 280 workItemsGroup,
277 } 281 workItemInfo,
278 282 callback,
279 /// <summary> 283 state);
280 /// Create a new work item 284
281 /// </summary> 285 return workItem;
282 /// <param name="wigStartInfo">Work item group start information</param> 286 }
283 /// <param name="callback">A callback to execute</param> 287
284 /// <param name="state"> 288 /// <summary>
285 /// The context object of the work item. Used for passing arguments to the work item. 289 /// Create a new work item
286 /// </param> 290 /// </summary>
287 /// <param name="postExecuteWorkItemCallback"> 291 /// <param name="workItemsGroup">The work items group</param>
288 /// A delegate to call after the callback completion 292 /// <param name="wigStartInfo">Work item group start information</param>
289 /// </param> 293 /// <param name="callback">A callback to execute</param>
290 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param> 294 /// <param name="state">
291 /// <param name="workItemPriority">The work item priority</param> 295 /// The context object of the work item. Used for passing arguments to the work item.
292 /// <returns>Returns a work item</returns> 296 /// </param>
293 public static WorkItem CreateWorkItem( 297 /// <param name="postExecuteWorkItemCallback">
294 IWorkItemsGroup workItemsGroup, 298 /// A delegate to call after the callback completion
295 WIGStartInfo wigStartInfo, 299 /// </param>
296 WorkItemCallback callback, 300 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
297 object state, 301 /// <param name="workItemPriority">The work item priority</param>
298 PostExecuteWorkItemCallback postExecuteWorkItemCallback, 302 /// <returns>Returns a work item</returns>
299 CallToPostExecute callToPostExecute, 303 public static WorkItem CreateWorkItem(
300 WorkItemPriority workItemPriority) 304 IWorkItemsGroup workItemsGroup,
301 { 305 WIGStartInfo wigStartInfo,
302 306 WorkItemCallback callback,
303 ValidateCallback(callback); 307 object state,
304 ValidateCallback(postExecuteWorkItemCallback); 308 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
305 309 CallToPostExecute callToPostExecute,
306 WorkItemInfo workItemInfo = new WorkItemInfo(); 310 WorkItemPriority workItemPriority)
307 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; 311 {
308 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; 312
309 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; 313 ValidateCallback(callback);
310 workItemInfo.CallToPostExecute = callToPostExecute; 314 ValidateCallback(postExecuteWorkItemCallback);
311 workItemInfo.WorkItemPriority = workItemPriority; 315
312 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; 316 WorkItemInfo workItemInfo = new WorkItemInfo();
313 317 workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
314 WorkItem workItem = new WorkItem( 318 workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
315 workItemsGroup, 319 workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
316 workItemInfo, 320 workItemInfo.CallToPostExecute = callToPostExecute;
317 callback, 321 workItemInfo.WorkItemPriority = workItemPriority;
318 state); 322 workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
319 323
320 return workItem; 324 WorkItem workItem = new WorkItem(
321 } 325 workItemsGroup,
322 326 workItemInfo,
323 private static void ValidateCallback(Delegate callback) 327 callback,
324 { 328 state);
325 if(callback.GetInvocationList().Length > 1) 329
326 { 330 return workItem;
327 throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); 331 }
328 } 332
329 } 333 private static void ValidateCallback(Delegate callback)
330 } 334 {
331 335 if (callback != null && callback.GetInvocationList().Length > 1)
332 #endregion 336 {
337 throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
338 }
339 }
340 }
341
342 #endregion
333} 343}
diff --git a/ThirdParty/SmartThreadPool/WorkItemInfo.cs b/ThirdParty/SmartThreadPool/WorkItemInfo.cs
index c259339..0d7fc85 100644
--- a/ThirdParty/SmartThreadPool/WorkItemInfo.cs
+++ b/ThirdParty/SmartThreadPool/WorkItemInfo.cs
@@ -1,102 +1,69 @@
1// Ami Bar
2// amibar@gmail.com
3
4namespace Amib.Threading 1namespace Amib.Threading
5{ 2{
6 #region WorkItemInfo class 3 #region WorkItemInfo class
7
8 /// <summary>
9 /// Summary description for WorkItemInfo.
10 /// </summary>
11 public class WorkItemInfo
12 {
13 /// <summary>
14 /// Use the caller's security context
15 /// </summary>
16 private bool _useCallerCallContext;
17
18 /// <summary>
19 /// Use the caller's security context
20 /// </summary>
21 private bool _useCallerHttpContext;
22
23 /// <summary>
24 /// Dispose of the state object of a work item
25 /// </summary>
26 private bool _disposeOfStateObjects;
27
28 /// <summary>
29 /// The option to run the post execute
30 /// </summary>
31 private CallToPostExecute _callToPostExecute;
32 4
33 /// <summary> 5 /// <summary>
34 /// A post execute callback to call when none is provided in 6 /// Summary description for WorkItemInfo.
35 /// the QueueWorkItem method. 7 /// </summary>
36 /// </summary> 8 public class WorkItemInfo
37 private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; 9 {
38 10 public WorkItemInfo()
39 /// <summary> 11 {
40 /// The priority of the work item 12 UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
41 /// </summary> 13 UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
42 private WorkItemPriority _workItemPriority; 14 DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
15 CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
16 PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
17 WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
18 }
43 19
44 public WorkItemInfo() 20 public WorkItemInfo(WorkItemInfo workItemInfo)
45 { 21 {
46 _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; 22 UseCallerCallContext = workItemInfo.UseCallerCallContext;
47 _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; 23 UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
48 _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; 24 DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
49 _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; 25 CallToPostExecute = workItemInfo.CallToPostExecute;
50 _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; 26 PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
51 _workItemPriority = SmartThreadPool.DefaultWorkItemPriority; 27 WorkItemPriority = workItemInfo.WorkItemPriority;
52 } 28 Timeout = workItemInfo.Timeout;
29 }
53 30
54 public WorkItemInfo(WorkItemInfo workItemInfo) 31 /// <summary>
55 { 32 /// Get/Set if to use the caller's security context
56 _useCallerCallContext = workItemInfo._useCallerCallContext; 33 /// </summary>
57 _useCallerHttpContext = workItemInfo._useCallerHttpContext; 34 public bool UseCallerCallContext { get; set; }
58 _disposeOfStateObjects = workItemInfo._disposeOfStateObjects;
59 _callToPostExecute = workItemInfo._callToPostExecute;
60 _postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback;
61 _workItemPriority = workItemInfo._workItemPriority;
62 }
63 35
64 public bool UseCallerCallContext 36 /// <summary>
65 { 37 /// Get/Set if to use the caller's HTTP context
66 get { return _useCallerCallContext; } 38 /// </summary>
67 set { _useCallerCallContext = value; } 39 public bool UseCallerHttpContext { get; set; }
68 }
69 40
70 public bool UseCallerHttpContext 41 /// <summary>
71 { 42 /// Get/Set if to dispose of the state object of a work item
72 get { return _useCallerHttpContext; } 43 /// </summary>
73 set { _useCallerHttpContext = value; } 44 public bool DisposeOfStateObjects { get; set; }
74 }
75 45
76 public bool DisposeOfStateObjects 46 /// <summary>
77 { 47 /// Get/Set the run the post execute options
78 get { return _disposeOfStateObjects; } 48 /// </summary>
79 set { _disposeOfStateObjects = value; } 49 public CallToPostExecute CallToPostExecute { get; set; }
80 }
81 50
82 public CallToPostExecute CallToPostExecute 51 /// <summary>
83 { 52 /// Get/Set the post execute callback
84 get { return _callToPostExecute; } 53 /// </summary>
85 set { _callToPostExecute = value; } 54 public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }
86 }
87 55
88 public PostExecuteWorkItemCallback PostExecuteWorkItemCallback 56 /// <summary>
89 { 57 /// Get/Set the work item's priority
90 get { return _postExecuteWorkItemCallback; } 58 /// </summary>
91 set { _postExecuteWorkItemCallback = value; } 59 public WorkItemPriority WorkItemPriority { get; set; }
92 }
93 60
94 public WorkItemPriority WorkItemPriority 61 /// <summary>
95 { 62 /// Get/Set the work item's timout in milliseconds.
96 get { return _workItemPriority; } 63 /// This is a passive timout. When the timout expires the work item won't be actively aborted!
97 set { _workItemPriority = value; } 64 /// </summary>
98 } 65 public long Timeout { get; set; }
99 } 66 }
100 67
101 #endregion 68 #endregion
102} 69}
diff --git a/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs
new file mode 100644
index 0000000..d1eff95
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs
@@ -0,0 +1,128 @@
1using System;
2using System.Threading;
3
4namespace 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..d9d34ac 100644
--- a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs
+++ b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs
@@ -1,6 +1,3 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System; 1using System;
5using System.Threading; 2using System.Threading;
6using System.Runtime.CompilerServices; 3using System.Runtime.CompilerServices;
@@ -8,505 +5,357 @@ using System.Diagnostics;
8 5
9namespace Amib.Threading.Internal 6namespace Amib.Threading.Internal
10{ 7{
11 #region WorkItemsGroup class
12
13 /// <summary>
14 /// Summary description for WorkItemsGroup.
15 /// </summary>
16 public class WorkItemsGroup : IWorkItemsGroup
17 {
18 #region Private members
19
20 private object _lock = new object();
21 /// <summary>
22 /// Contains the name of this instance of SmartThreadPool.
23 /// Can be changed by the user.
24 /// </summary>
25 private string _name = "WorkItemsGroup";
26
27 /// <summary>
28 /// A reference to the SmartThreadPool instance that created this
29 /// WorkItemsGroup.
30 /// </summary>
31 private SmartThreadPool _stp;
32
33 /// <summary>
34 /// The OnIdle event
35 /// </summary>
36 private event WorkItemsGroupIdleHandler _onIdle;
37 8
38 /// <summary> 9 #region WorkItemsGroup class
39 /// Defines how many work items of this WorkItemsGroup can run at once.
40 /// </summary>
41 private int _concurrency;
42 10
43 /// <summary> 11 /// <summary>
44 /// Priority queue to hold work items before they are passed 12 /// Summary description for WorkItemsGroup.
45 /// to the SmartThreadPool. 13 /// </summary>
46 /// </summary> 14 public class WorkItemsGroup : WorkItemsGroupBase
47 private PriorityQueue _workItemsQueue; 15 {
16 #region Private members
48 17
49 /// <summary> 18 private readonly object _lock = new object();
50 /// Indicate how many work items are waiting in the SmartThreadPool
51 /// queue.
52 /// This value is used to apply the concurrency.
53 /// </summary>
54 private int _workItemsInStpQueue;
55 19
56 /// <summary> 20 /// <summary>
57 /// Indicate how many work items are currently running in the SmartThreadPool. 21 /// A reference to the SmartThreadPool instance that created this
58 /// This value is used with the Cancel, to calculate if we can send new 22 /// WorkItemsGroup.
59 /// work items to the STP. 23 /// </summary>
60 /// </summary> 24 private readonly SmartThreadPool _stp;
61 private int _workItemsExecutingInStp = 0;
62 25
63 /// <summary> 26 /// <summary>
64 /// WorkItemsGroup start information 27 /// The OnIdle event
65 /// </summary> 28 /// </summary>
66 private WIGStartInfo _workItemsGroupStartInfo; 29 private event WorkItemsGroupIdleHandler _onIdle;
67 30
68 /// <summary> 31 /// <summary>
69 /// Signaled when all of the WorkItemsGroup's work item completed. 32 /// A flag to indicate if the Work Items Group is now suspended.
70 /// </summary> 33 /// </summary>
71 private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); 34 private bool _isSuspended;
72 35
73 /// <summary> 36 /// <summary>
74 /// A common object for all the work items that this work items group 37 /// Defines how many work items of this WorkItemsGroup can run at once.
75 /// generate so we can mark them to cancel in O(1) 38 /// </summary>
76 /// </summary> 39 private int _concurrency;
77 private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); 40
78 41 /// <summary>
79 #endregion 42 /// Priority queue to hold work items before they are passed
80 43 /// to the SmartThreadPool.
81 #region Construction 44 /// </summary>
82 45 private readonly PriorityQueue _workItemsQueue;
83 public WorkItemsGroup( 46
84 SmartThreadPool stp, 47 /// <summary>
85 int concurrency, 48 /// Indicate how many work items are waiting in the SmartThreadPool
86 WIGStartInfo wigStartInfo) 49 /// queue.
50 /// This value is used to apply the concurrency.
51 /// </summary>
52 private int _workItemsInStpQueue;
53
54 /// <summary>
55 /// Indicate how many work items are currently running in the SmartThreadPool.
56 /// This value is used with the Cancel, to calculate if we can send new
57 /// work items to the STP.
58 /// </summary>
59 private int _workItemsExecutingInStp = 0;
60
61 /// <summary>
62 /// WorkItemsGroup start information
63 /// </summary>
64 private readonly WIGStartInfo _workItemsGroupStartInfo;
65
66 /// <summary>
67 /// Signaled when all of the WorkItemsGroup's work item completed.
68 /// </summary>
69 //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
70 private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
71
72 /// <summary>
73 /// 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 /// </summary>
76 private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
77
78 #endregion
79
80 #region Construction
81
82 public WorkItemsGroup(
83 SmartThreadPool stp,
84 int concurrency,
85 WIGStartInfo wigStartInfo)
86 {
87 if (concurrency <= 0)
88 {
89 throw new ArgumentOutOfRangeException(
90 "concurrency",
91#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
92 concurrency,
93#endif
94 "concurrency must be greater than zero");
95 }
96 _stp = stp;
97 _concurrency = concurrency;
98 _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly();
99 _workItemsQueue = new PriorityQueue();
100 Name = "WorkItemsGroup";
101
102 // The _workItemsInStpQueue gets the number of currently executing work items,
103 // because once a work item is executing, it cannot be cancelled.
104 _workItemsInStpQueue = _workItemsExecutingInStp;
105
106 _isSuspended = _workItemsGroupStartInfo.StartSuspended;
107 }
108
109 #endregion
110
111 #region WorkItemsGroupBase Overrides
112
113 public override int Concurrency
87 { 114 {
88 if (concurrency <= 0) 115 get { return _concurrency; }
89 {
90 throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero");
91 }
92 _stp = stp;
93 _concurrency = concurrency;
94 _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo);
95 _workItemsQueue = new PriorityQueue();
96
97 // The _workItemsInStpQueue gets the number of currently executing work items,
98 // because once a work item is executing, it cannot be cancelled.
99 _workItemsInStpQueue = _workItemsExecutingInStp;
100 }
101
102 #endregion
103
104 #region IWorkItemsGroup implementation
105
106 /// <summary>
107 /// Get/Set the name of the SmartThreadPool instance
108 /// </summary>
109 public string Name
110 {
111 get
112 {
113 return _name;
114 }
115
116 set 116 set
117 { 117 {
118 _name = value; 118 Debug.Assert(value > 0);
119 }
120 }
121
122 /// <summary>
123 /// Queue a work item
124 /// </summary>
125 /// <param name="callback">A callback to execute</param>
126 /// <returns>Returns a work item result</returns>
127 public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
128 {
129 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback);
130 EnqueueToSTPNextWorkItem(workItem);
131 return workItem.GetWorkItemResult();
132 }
133 119
134 /// <summary> 120 int diff = value - _concurrency;
135 /// Queue a work item 121 _concurrency = value;
136 /// </summary> 122 if (diff > 0)
137 /// <param name="callback">A callback to execute</param> 123 {
138 /// <param name="workItemPriority">The priority of the work item</param> 124 EnqueueToSTPNextNWorkItem(diff);
139 /// <returns>Returns a work item result</returns> 125 }
140 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) 126 }
141 {
142 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority);
143 EnqueueToSTPNextWorkItem(workItem);
144 return workItem.GetWorkItemResult();
145 }
146
147 /// <summary>
148 /// Queue a work item
149 /// </summary>
150 /// <param name="workItemInfo">Work item info</param>
151 /// <param name="callback">A callback to execute</param>
152 /// <returns>Returns a work item result</returns>
153 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
154 {
155 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback);
156 EnqueueToSTPNextWorkItem(workItem);
157 return workItem.GetWorkItemResult();
158 }
159
160 /// <summary>
161 /// Queue a work item
162 /// </summary>
163 /// <param name="callback">A callback to execute</param>
164 /// <param name="state">
165 /// The context object of the work item. Used for passing arguments to the work item.
166 /// </param>
167 /// <returns>Returns a work item result</returns>
168 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
169 {
170 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state);
171 EnqueueToSTPNextWorkItem(workItem);
172 return workItem.GetWorkItemResult();
173 }
174
175 /// <summary>
176 /// Queue a work item
177 /// </summary>
178 /// <param name="callback">A callback to execute</param>
179 /// <param name="state">
180 /// The context object of the work item. Used for passing arguments to the work item.
181 /// </param>
182 /// <param name="workItemPriority">The work item priority</param>
183 /// <returns>Returns a work item result</returns>
184 public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
185 {
186 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority);
187 EnqueueToSTPNextWorkItem(workItem);
188 return workItem.GetWorkItemResult();
189 }
190
191 /// <summary>
192 /// Queue a work item
193 /// </summary>
194 /// <param name="workItemInfo">Work item information</param>
195 /// <param name="callback">A callback to execute</param>
196 /// <param name="state">
197 /// The context object of the work item. Used for passing arguments to the work item.
198 /// </param>
199 /// <returns>Returns a work item result</returns>
200 public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
201 {
202 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state);
203 EnqueueToSTPNextWorkItem(workItem);
204 return workItem.GetWorkItemResult();
205 }
206
207 /// <summary>
208 /// Queue a work item
209 /// </summary>
210 /// <param name="callback">A callback to execute</param>
211 /// <param name="state">
212 /// The context object of the work item. Used for passing arguments to the work item.
213 /// </param>
214 /// <param name="postExecuteWorkItemCallback">
215 /// A delegate to call after the callback completion
216 /// </param>
217 /// <returns>Returns a work item result</returns>
218 public IWorkItemResult QueueWorkItem(
219 WorkItemCallback callback,
220 object state,
221 PostExecuteWorkItemCallback postExecuteWorkItemCallback)
222 {
223 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback);
224 EnqueueToSTPNextWorkItem(workItem);
225 return workItem.GetWorkItemResult();
226 }
227
228 /// <summary>
229 /// Queue a work item
230 /// </summary>
231 /// <param name="callback">A callback to execute</param>
232 /// <param name="state">
233 /// The context object of the work item. Used for passing arguments to the work item.
234 /// </param>
235 /// <param name="postExecuteWorkItemCallback">
236 /// A delegate to call after the callback completion
237 /// </param>
238 /// <param name="workItemPriority">The work item priority</param>
239 /// <returns>Returns a work item result</returns>
240 public IWorkItemResult QueueWorkItem(
241 WorkItemCallback callback,
242 object state,
243 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
244 WorkItemPriority workItemPriority)
245 {
246 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
247 EnqueueToSTPNextWorkItem(workItem);
248 return workItem.GetWorkItemResult();
249 }
250
251 /// <summary>
252 /// Queue a work item
253 /// </summary>
254 /// <param name="callback">A callback to execute</param>
255 /// <param name="state">
256 /// The context object of the work item. Used for passing arguments to the work item.
257 /// </param>
258 /// <param name="postExecuteWorkItemCallback">
259 /// A delegate to call after the callback completion
260 /// </param>
261 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
262 /// <returns>Returns a work item result</returns>
263 public IWorkItemResult QueueWorkItem(
264 WorkItemCallback callback,
265 object state,
266 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
267 CallToPostExecute callToPostExecute)
268 {
269 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
270 EnqueueToSTPNextWorkItem(workItem);
271 return workItem.GetWorkItemResult();
272 } 127 }
273 128
274 /// <summary> 129 public override int WaitingCallbacks
275 /// Queue a work item
276 /// </summary>
277 /// <param name="callback">A callback to execute</param>
278 /// <param name="state">
279 /// The context object of the work item. Used for passing arguments to the work item.
280 /// </param>
281 /// <param name="postExecuteWorkItemCallback">
282 /// A delegate to call after the callback completion
283 /// </param>
284 /// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
285 /// <param name="workItemPriority">The work item priority</param>
286 /// <returns>Returns a work item result</returns>
287 public IWorkItemResult QueueWorkItem(
288 WorkItemCallback callback,
289 object state,
290 PostExecuteWorkItemCallback postExecuteWorkItemCallback,
291 CallToPostExecute callToPostExecute,
292 WorkItemPriority workItemPriority)
293 { 130 {
294 WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); 131 get { return _workItemsQueue.Count; }
295 EnqueueToSTPNextWorkItem(workItem);
296 return workItem.GetWorkItemResult();
297 } 132 }
298 133
299 /// <summary> 134 public override object[] GetStates()
300 /// Wait for the thread pool to be idle
301 /// </summary>
302 public void WaitForIdle()
303 { 135 {
304 WaitForIdle(Timeout.Infinite); 136 lock (_lock)
137 {
138 object[] states = new object[_workItemsQueue.Count];
139 int i = 0;
140 foreach (WorkItem workItem in _workItemsQueue)
141 {
142 states[i] = workItem.GetWorkItemResult().State;
143 ++i;
144 }
145 return states;
146 }
305 } 147 }
306 148
307 /// <summary> 149 /// <summary>
308 /// Wait for the thread pool to be idle 150 /// WorkItemsGroup start information
309 /// </summary> 151 /// </summary>
310 public bool WaitForIdle(TimeSpan timeout) 152 public override WIGStartInfo WIGStartInfo
311 { 153 {
312 return WaitForIdle((int)timeout.TotalMilliseconds); 154 get { return _workItemsGroupStartInfo; }
313 } 155 }
314 156
315 /// <summary> 157 /// <summary>
158 /// Start the Work Items Group if it was started suspended
159 /// </summary>
160 public override void Start()
161 {
162 // If the Work Items Group already started then quit
163 if (!_isSuspended)
164 {
165 return;
166 }
167 _isSuspended = false;
168
169 EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
170 }
171
172 public override void Cancel(bool abortExecution)
173 {
174 lock (_lock)
175 {
176 _canceledWorkItemsGroup.IsCanceled = true;
177 _workItemsQueue.Clear();
178 _workItemsInStpQueue = 0;
179 _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
180 }
181
182 if (abortExecution)
183 {
184 _stp.CancelAbortWorkItemsGroup(this);
185 }
186 }
187
188 /// <summary>
316 /// Wait for the thread pool to be idle 189 /// Wait for the thread pool to be idle
317 /// </summary> 190 /// </summary>
318 public bool WaitForIdle(int millisecondsTimeout) 191 public override bool WaitForIdle(int millisecondsTimeout)
319 { 192 {
320 _stp.ValidateWorkItemsGroupWaitForIdle(this); 193 SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
321 return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false); 194 return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
322 } 195 }
323 196
324 public int WaitingCallbacks 197 public override event WorkItemsGroupIdleHandler OnIdle
325 { 198 {
326 get 199 add { _onIdle += value; }
327 { 200 remove { _onIdle -= value; }
328 return _workItemsQueue.Count; 201 }
329 }
330 }
331 202
332 public event WorkItemsGroupIdleHandler OnIdle 203 #endregion
333 {
334 add
335 {
336 _onIdle += value;
337 }
338 remove
339 {
340 _onIdle -= value;
341 }
342 }
343 204
344 public void Cancel() 205 #region Private methods
345 {
346 lock(_lock)
347 {
348 _canceledWorkItemsGroup.IsCanceled = true;
349 _workItemsQueue.Clear();
350 _workItemsInStpQueue = 0;
351 _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
352 }
353 }
354 206
355 public void Start() 207 private void RegisterToWorkItemCompletion(IWorkItemResult wir)
356 { 208 {
357 lock (this) 209 IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
358 { 210 iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
359 if (!_workItemsGroupStartInfo.StartSuspended) 211 iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
360 { 212 }
361 return;
362 }
363 _workItemsGroupStartInfo.StartSuspended = false;
364 }
365
366 for(int i = 0; i < _concurrency; ++i)
367 {
368 EnqueueToSTPNextWorkItem(null, false);
369 }
370 }
371
372 #endregion
373 213
374 #region Private methods 214 public void OnSTPIsStarting()
375 215 {
376 private void RegisterToWorkItemCompletion(IWorkItemResult wir) 216 if (_isSuspended)
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 { 217 {
410 return; 218 return;
411 } 219 }
220
221 EnqueueToSTPNextNWorkItem(_concurrency);
222 }
412 223
413 Delegate[] delegates = onIdle.GetInvocationList(); 224 public void EnqueueToSTPNextNWorkItem(int count)
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 { 225 {
427 lock(_lock) 226 for (int i = 0; i < count; ++i)
428 { 227 {
429 ++_workItemsExecutingInStp; 228 EnqueueToSTPNextWorkItem(null, false);
430 } 229 }
431 } 230 }
432 231
433 private void OnWorkItemCompletedCallback(WorkItem workItem) 232 private object FireOnIdle(object state)
434 { 233 {
435 EnqueueToSTPNextWorkItem(null, true); 234 FireOnIdleImpl(_onIdle);
436 } 235 return null;
437 236 }
438 private void EnqueueToSTPNextWorkItem(WorkItem workItem) 237
238 [MethodImpl(MethodImplOptions.NoInlining)]
239 private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
240 {
241 if(null == onIdle)
242 {
243 return;
244 }
245
246 Delegate[] delegates = onIdle.GetInvocationList();
247 foreach(WorkItemsGroupIdleHandler eh in delegates)
248 {
249 try
250 {
251 eh(this);
252 }
253 catch { } // Suppress exceptions
254 }
255 }
256
257 private void OnWorkItemStartedCallback(WorkItem workItem)
258 {
259 lock(_lock)
260 {
261 ++_workItemsExecutingInStp;
262 }
263 }
264
265 private void OnWorkItemCompletedCallback(WorkItem workItem)
266 {
267 EnqueueToSTPNextWorkItem(null, true);
268 }
269
270 internal override void Enqueue(WorkItem workItem)
439 { 271 {
440 EnqueueToSTPNextWorkItem(workItem, false); 272 EnqueueToSTPNextWorkItem(workItem);
441 } 273 }
442 274
443 private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue) 275 private void EnqueueToSTPNextWorkItem(WorkItem workItem)
444 { 276 {
445 lock(_lock) 277 EnqueueToSTPNextWorkItem(workItem, false);
446 { 278 }
447 // Got here from OnWorkItemCompletedCallback() 279
448 if (decrementWorkItemsInStpQueue) 280 private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
449 { 281 {
450 --_workItemsInStpQueue; 282 lock(_lock)
451 283 {
452 if(_workItemsInStpQueue < 0) 284 // Got here from OnWorkItemCompletedCallback()
453 { 285 if (decrementWorkItemsInStpQueue)
454 _workItemsInStpQueue = 0; 286 {
455 } 287 --_workItemsInStpQueue;
456 288
457 --_workItemsExecutingInStp; 289 if(_workItemsInStpQueue < 0)
458 290 {
459 if(_workItemsExecutingInStp < 0) 291 _workItemsInStpQueue = 0;
460 { 292 }
461 _workItemsExecutingInStp = 0; 293
462 } 294 --_workItemsExecutingInStp;
463 } 295
464 296 if(_workItemsExecutingInStp < 0)
465 // If the work item is not null then enqueue it 297 {
466 if (null != workItem) 298 _workItemsExecutingInStp = 0;
467 { 299 }
468 workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup; 300 }
469 301
470 RegisterToWorkItemCompletion(workItem.GetWorkItemResult()); 302 // If the work item is not null then enqueue it
471 _workItemsQueue.Enqueue(workItem); 303 if (null != workItem)
472 //_stp.IncrementWorkItemsCount(); 304 {
473 305 workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;
474 if ((1 == _workItemsQueue.Count) && 306
475 (0 == _workItemsInStpQueue)) 307 RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
476 { 308 _workItemsQueue.Enqueue(workItem);
477 _stp.RegisterWorkItemsGroup(this); 309 //_stp.IncrementWorkItemsCount();
478 Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle"); 310
311 if ((1 == _workItemsQueue.Count) &&
312 (0 == _workItemsInStpQueue))
313 {
314 _stp.RegisterWorkItemsGroup(this);
315 IsIdle = false;
479 _isIdleWaitHandle.Reset(); 316 _isIdleWaitHandle.Reset();
480 } 317 }
481 } 318 }
482 319
483 // If the work items queue of the group is empty than quit 320 // If the work items queue of the group is empty than quit
484 if (0 == _workItemsQueue.Count) 321 if (0 == _workItemsQueue.Count)
485 { 322 {
486 if (0 == _workItemsInStpQueue) 323 if (0 == _workItemsInStpQueue)
487 { 324 {
488 _stp.UnregisterWorkItemsGroup(this); 325 _stp.UnregisterWorkItemsGroup(this);
489 Trace.WriteLine("WorkItemsGroup " + Name + " is idle"); 326 IsIdle = true;
490 _isIdleWaitHandle.Set(); 327 _isIdleWaitHandle.Set();
491 _stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle)); 328 if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
492 } 329 {
493 return; 330 _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
494 } 331 }
495 332 }
496 if (!_workItemsGroupStartInfo.StartSuspended) 333 return;
497 { 334 }
498 if (_workItemsInStpQueue < _concurrency) 335
499 { 336 if (!_isSuspended)
500 WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; 337 {
501 _stp.Enqueue(nextWorkItem, true); 338 if (_workItemsInStpQueue < _concurrency)
502 ++_workItemsInStpQueue; 339 {
503 } 340 WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
504 } 341 try
505 } 342 {
506 } 343 _stp.Enqueue(nextWorkItem);
507 344 }
508 #endregion 345 catch (ObjectDisposedException e)
346 {
347 e.GetHashCode();
348 // The STP has been shutdown
349 }
350
351 ++_workItemsInStpQueue;
352 }
353 }
354 }
355 }
356
357 #endregion
509 } 358 }
510 359
511 #endregion 360 #endregion
512} 361}
diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs
new file mode 100644
index 0000000..27fae5e
--- /dev/null
+++ b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs
@@ -0,0 +1,471 @@
1using System;
2using System.Threading;
3
4namespace 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..e0bc916 100644
--- a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
+++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs
@@ -1,109 +1,151 @@
1// Ami Bar
2// amibar@gmail.com
3
4using System; 1using System;
2using System.Collections.Generic;
5using System.Threading; 3using System.Threading;
6 4
7namespace Amib.Threading.Internal 5namespace Amib.Threading.Internal
8{ 6{
9 #region WorkItemsQueue class 7 #region WorkItemsQueue class
10 8
11 /// <summary> 9 /// <summary>
12 /// WorkItemsQueue class. 10 /// WorkItemsQueue class.
13 /// </summary> 11 /// </summary>
14 public class WorkItemsQueue : IDisposable 12 public class WorkItemsQueue : IDisposable
15 { 13 {
16 #region Member variables 14 #region Member variables
17 15
18 /// <summary> 16 /// <summary>
19 /// Waiters queue (implemented as stack). 17 /// Waiters queue (implemented as stack).
20 /// </summary> 18 /// </summary>
21 private WaiterEntry _headWaiterEntry = new WaiterEntry(); 19 private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();
22 20
23 /// <summary> 21 /// <summary>
24 /// Waiters count 22 /// Waiters count
25 /// </summary> 23 /// </summary>
26 private int _waitersCount = 0; 24 private int _waitersCount = 0;
27 25
28 /// <summary> 26 /// <summary>
29 /// Work items queue 27 /// Work items queue
30 /// </summary> 28 /// </summary>
31 private PriorityQueue _workItems = new PriorityQueue(); 29 private readonly PriorityQueue _workItems = new PriorityQueue();
32 30
33 /// <summary> 31 /// <summary>
34 /// Indicate that work items are allowed to be queued 32 /// Indicate that work items are allowed to be queued
35 /// </summary> 33 /// </summary>
36 private bool _isWorkItemsQueueActive = true; 34 private bool _isWorkItemsQueueActive = true;
37 35
38 /// <summary>
39 /// Each thread in the thread pool keeps its own waiter entry.
40 /// </summary>
41 [ThreadStatic]
42 private static WaiterEntry _waiterEntry;
43 36
44 /// <summary> 37#if (WINDOWS_PHONE)
45 /// A flag that indicates if the WorkItemsQueue has been disposed. 38 private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>();
46 /// </summary> 39#elif (_WINDOWS_CE)
47 private bool _isDisposed = false; 40 private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot();
41#else
48 42
49 #endregion 43 [ThreadStatic]
44 private static WaiterEntry _waiterEntry;
45#endif
50 46
51 #region Public properties
52 47
53 /// <summary> 48 /// <summary>
54 /// Returns the current number of work items in the queue 49 /// Each thread in the thread pool keeps its own waiter entry.
55 /// </summary> 50 /// </summary>
56 public int Count 51 private static WaiterEntry CurrentWaiterEntry
57 { 52 {
53#if (WINDOWS_PHONE)
58 get 54 get
59 { 55 {
60 lock(this) 56 lock (_waiterEntries)
61 { 57 {
62 ValidateNotDisposed(); 58 WaiterEntry waiterEntry;
63 return _workItems.Count; 59 if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry))
60 {
61 return waiterEntry;
62 }
64 } 63 }
64 return null;
65 } 65 }
66 } 66 set
67
68 /// <summary>
69 /// Returns the current number of waiters
70 /// </summary>
71 public int WaitersCount
72 {
73 get
74 { 67 {
75 lock(this) 68 lock (_waiterEntries)
76 { 69 {
77 ValidateNotDisposed(); 70 _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value;
78 return _waitersCount;
79 } 71 }
80 } 72 }
73#elif (_WINDOWS_CE)
74 get
75 {
76 return Thread.GetData(_waiterEntrySlot) as WaiterEntry;
77 }
78 set
79 {
80 Thread.SetData(_waiterEntrySlot, value);
81 }
82#else
83 get
84 {
85 return _waiterEntry;
86 }
87 set
88 {
89 _waiterEntry = value;
90 }
91#endif
81 } 92 }
82 93
94 /// <summary>
95 /// A flag that indicates if the WorkItemsQueue has been disposed.
96 /// </summary>
97 private bool _isDisposed = false;
83 98
84 #endregion 99 #endregion
85 100
86 #region Public methods 101 #region Public properties
87 102
88 /// <summary> 103 /// <summary>
89 /// Enqueue a work item to the queue. 104 /// Returns the current number of work items in the queue
90 /// </summary> 105 /// </summary>
91 public bool EnqueueWorkItem(WorkItem workItem) 106 public int Count
92 { 107 {
93 // A work item cannot be null, since null is used in the 108 get
94 // WaitForWorkItem() method to indicate timeout or cancel 109 {
95 if (null == workItem) 110 return _workItems.Count;
96 { 111 }
97 throw new ArgumentNullException("workItem" , "workItem cannot be null"); 112 }
98 } 113
114 /// <summary>
115 /// Returns the current number of waiters
116 /// </summary>
117 public int WaitersCount
118 {
119 get
120 {
121 return _waitersCount;
122 }
123 }
99 124
100 bool enqueue = true;
101 125
102 // First check if there is a waiter waiting for work item. During 126 #endregion
103 // the check, timed out waiters are ignored. If there is no 127
104 // waiter then the work item is queued. 128 #region Public methods
105 lock(this) 129
106 { 130 /// <summary>
131 /// Enqueue a work item to the queue.
132 /// </summary>
133 public bool EnqueueWorkItem(WorkItem workItem)
134 {
135 // A work item cannot be null, since null is used in the
136 // WaitForWorkItem() method to indicate timeout or cancel
137 if (null == workItem)
138 {
139 throw new ArgumentNullException("workItem" , "workItem cannot be null");
140 }
141
142 bool enqueue = true;
143
144 // First check if there is a waiter waiting for work item. During
145 // the check, timed out waiters are ignored. If there is no
146 // waiter then the work item is queued.
147 lock(this)
148 {
107 ValidateNotDisposed(); 149 ValidateNotDisposed();
108 150
109 if (!_isWorkItemsQueueActive) 151 if (!_isWorkItemsQueueActive)
@@ -111,56 +153,55 @@ namespace Amib.Threading.Internal
111 return false; 153 return false;
112 } 154 }
113 155
114 while(_waitersCount > 0) 156 while(_waitersCount > 0)
115 { 157 {
116 // Dequeue a waiter. 158 // Dequeue a waiter.
117 WaiterEntry waiterEntry = PopWaiter(); 159 WaiterEntry waiterEntry = PopWaiter();
118 160
119 // Signal the waiter. On success break the loop 161 // Signal the waiter. On success break the loop
120 if (waiterEntry.Signal(workItem)) 162 if (waiterEntry.Signal(workItem))
121 { 163 {
122 enqueue = false; 164 enqueue = false;
123 break; 165 break;
124 } 166 }
125 } 167 }
126 168
127 if (enqueue) 169 if (enqueue)
128 { 170 {
129 // Enqueue the work item 171 // Enqueue the work item
130 _workItems.Enqueue(workItem); 172 _workItems.Enqueue(workItem);
131 } 173 }
132 } 174 }
133 return true; 175 return true;
134 } 176 }
135 177
136 178
137 /// <summary> 179 /// <summary>
138 /// Waits for a work item or exits on timeout or cancel 180 /// Waits for a work item or exits on timeout or cancel
139 /// </summary> 181 /// </summary>
140 /// <param name="millisecondsTimeout">Timeout in milliseconds</param> 182 /// <param name="millisecondsTimeout">Timeout in milliseconds</param>
141 /// <param name="cancelEvent">Cancel wait handle</param> 183 /// <param name="cancelEvent">Cancel wait handle</param>
142 /// <returns>Returns true if the resource was granted</returns> 184 /// <returns>Returns true if the resource was granted</returns>
143 public WorkItem DequeueWorkItem( 185 public WorkItem DequeueWorkItem(
144 int millisecondsTimeout, 186 int millisecondsTimeout,
145 WaitHandle cancelEvent) 187 WaitHandle cancelEvent)
146 { 188 {
147 /// This method cause the caller to wait for a work item. 189 // This method cause the caller to wait for a work item.
148 /// If there is at least one waiting work item then the 190 // If there is at least one waiting work item then the
149 /// method returns immidiately with true. 191 // method returns immidiately with it.
150 /// 192 //
151 /// If there are no waiting work items then the caller 193 // If there are no waiting work items then the caller
152 /// is queued between other waiters for a work item to arrive. 194 // is queued between other waiters for a work item to arrive.
153 /// 195 //
154 /// If a work item didn't come within millisecondsTimeout or 196 // If a work item didn't come within millisecondsTimeout or
155 /// the user canceled the wait by signaling the cancelEvent 197 // the user canceled the wait by signaling the cancelEvent
156 /// then the method returns false to indicate that the caller 198 // then the method returns null to indicate that the caller
157 /// didn't get a work item. 199 // didn't get a work item.
158 200
159 WaiterEntry waiterEntry = null; 201 WaiterEntry waiterEntry;
160 WorkItem workItem = null; 202 WorkItem workItem = null;
161 203 lock (this)
162 lock(this) 204 {
163 {
164 ValidateNotDisposed(); 205 ValidateNotDisposed();
165 206
166 // If there are waiting work items then take one and return. 207 // If there are waiting work items then take one and return.
@@ -169,80 +210,79 @@ namespace Amib.Threading.Internal
169 workItem = _workItems.Dequeue() as WorkItem; 210 workItem = _workItems.Dequeue() as WorkItem;
170 return workItem; 211 return workItem;
171 } 212 }
172 // No waiting work items ...
173 else
174 {
175 // Get the wait entry for the waiters queue
176 waiterEntry = GetThreadWaiterEntry();
177
178 // Put the waiter with the other waiters
179 PushWaiter(waiterEntry);
180 }
181 }
182
183 // Prepare array of wait handle for the WaitHandle.WaitAny()
184 WaitHandle [] waitHandles = new WaitHandle [] {
185 waiterEntry.WaitHandle,
186 cancelEvent };
187
188 // Wait for an available resource, cancel event, or timeout.
189 213
190 // During the wait we are supposes to exit the synchronization 214 // No waiting work items ...
191 // domain. (Placing true as the third argument of the WaitAny()) 215
192 // It just doesn't work, I don't know why, so I have lock(this) 216 // Get the waiter entry for the waiters queue
193 // statments insted of one. 217 waiterEntry = GetThreadWaiterEntry();
194 218
195 int index = WaitHandle.WaitAny( 219 // Put the waiter with the other waiters
196 waitHandles, 220 PushWaiter(waiterEntry);
197 millisecondsTimeout, 221 }
198 true); 222
199 223 // Prepare array of wait handle for the WaitHandle.WaitAny()
200 lock(this) 224 WaitHandle [] waitHandles = new WaitHandle[] {
201 { 225 waiterEntry.WaitHandle,
202 // success is true if it got a work item. 226 cancelEvent };
203 bool success = (0 == index); 227
204 228 // Wait for an available resource, cancel event, or timeout.
205 // The timeout variable is used only for readability. 229
206 // (We treat cancel as timeout) 230 // During the wait we are supposes to exit the synchronization
207 bool timeout = !success; 231 // domain. (Placing true as the third argument of the WaitAny())
208 232 // It just doesn't work, I don't know why, so I have two lock(this)
209 // On timeout update the waiterEntry that it is timed out 233 // statments instead of one.
210 if (timeout) 234
211 { 235 int index = STPEventWaitHandle.WaitAny(
212 // The Timeout() fails if the waiter has already been signaled 236 waitHandles,
213 timeout = waiterEntry.Timeout(); 237 millisecondsTimeout,
214 238 true);
215 // On timeout remove the waiter from the queue. 239
216 // Note that the complexity is O(1). 240 lock(this)
217 if(timeout) 241 {
218 { 242 // success is true if it got a work item.
219 RemoveWaiter(waiterEntry, false); 243 bool success = (0 == index);
220 } 244
221 245 // The timeout variable is used only for readability.
222 // Again readability 246 // (We treat cancel as timeout)
223 success = !timeout; 247 bool timeout = !success;
224 } 248
225 249 // On timeout update the waiterEntry that it is timed out
226 // On success return the work item 250 if (timeout)
227 if (success) 251 {
228 { 252 // The Timeout() fails if the waiter has already been signaled
229 workItem = waiterEntry.WorkItem; 253 timeout = waiterEntry.Timeout();
230 254
231 if (null == workItem) 255 // On timeout remove the waiter from the queue.
232 { 256 // Note that the complexity is O(1).
233 workItem = _workItems.Dequeue() as WorkItem; 257 if(timeout)
234 } 258 {
235 } 259 RemoveWaiter(waiterEntry, false);
236 } 260 }
237 // On failure return null. 261
238 return workItem; 262 // Again readability
239 } 263 success = !timeout;
264 }
265
266 // On success return the work item
267 if (success)
268 {
269 workItem = waiterEntry.WorkItem;
270
271 if (null == workItem)
272 {
273 workItem = _workItems.Dequeue() as WorkItem;
274 }
275 }
276 }
277 // On failure return null.
278 return workItem;
279 }
240 280
241 /// <summary> 281 /// <summary>
242 /// Cleanup the work items queue, hence no more work 282 /// Cleanup the work items queue, hence no more work
243 /// items are allowed to be queue 283 /// items are allowed to be queue
244 /// </summary> 284 /// </summary>
245 protected virtual void Cleanup() 285 private void Cleanup()
246 { 286 {
247 lock(this) 287 lock(this)
248 { 288 {
@@ -271,301 +311,312 @@ namespace Amib.Threading.Internal
271 // Tell the waiters that they were timed out. 311 // Tell the waiters that they were timed out.
272 // It won't signal them to exit, but to ignore their 312 // It won't signal them to exit, but to ignore their
273 // next work item. 313 // next work item.
274 while(_waitersCount > 0) 314 while(_waitersCount > 0)
275 { 315 {
276 WaiterEntry waiterEntry = PopWaiter(); 316 WaiterEntry waiterEntry = PopWaiter();
277 waiterEntry.Timeout(); 317 waiterEntry.Timeout();
278 } 318 }
279 } 319 }
280 } 320 }
281 321
282 #endregion 322 public object[] GetStates()
283
284 #region Private methods
285
286 /// <summary>
287 /// Returns the WaiterEntry of the current thread
288 /// </summary>
289 /// <returns></returns>
290 /// In order to avoid creation and destuction of WaiterEntry
291 /// objects each thread has its own WaiterEntry object.
292 private WaiterEntry GetThreadWaiterEntry()
293 { 323 {
294 if (null == _waiterEntry) 324 lock (this)
295 { 325 {
296 _waiterEntry = new WaiterEntry(); 326 object[] states = new object[_workItems.Count];
327 int i = 0;
328 foreach (WorkItem workItem in _workItems)
329 {
330 states[i] = workItem.GetWorkItemResult().State;
331 ++i;
332 }
333 return states;
297 } 334 }
298 _waiterEntry.Reset();
299 return _waiterEntry;
300 } 335 }
301 336
302 #region Waiters stack methods 337 #endregion
303 338
304 /// <summary> 339 #region Private methods
305 /// Push a new waiter into the waiter's stack 340
306 /// </summary> 341 /// <summary>
307 /// <param name="newWaiterEntry">A waiter to put in the stack</param> 342 /// Returns the WaiterEntry of the current thread
308 public void PushWaiter(WaiterEntry newWaiterEntry) 343 /// </summary>
309 { 344 /// <returns></returns>
310 // Remove the waiter if it is already in the stack and 345 /// In order to avoid creation and destuction of WaiterEntry
311 // update waiter's count as needed 346 /// objects each thread has its own WaiterEntry object.
312 RemoveWaiter(newWaiterEntry, false); 347 private static WaiterEntry GetThreadWaiterEntry()
313 348 {
314 // If the stack is empty then newWaiterEntry is the new head of the stack 349 if (null == CurrentWaiterEntry)
315 if (null == _headWaiterEntry._nextWaiterEntry) 350 {
316 { 351 CurrentWaiterEntry = new WaiterEntry();
317 _headWaiterEntry._nextWaiterEntry = newWaiterEntry; 352 }
318 newWaiterEntry._prevWaiterEntry = _headWaiterEntry; 353 CurrentWaiterEntry.Reset();
319 354 return CurrentWaiterEntry;
320 } 355 }
321 // If the stack is not empty then put newWaiterEntry as the new head 356
322 // of the stack. 357 #region Waiters stack methods
323 else 358
324 { 359 /// <summary>
325 // Save the old first waiter entry 360 /// Push a new waiter into the waiter's stack
326 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; 361 /// </summary>
362 /// <param name="newWaiterEntry">A waiter to put in the stack</param>
363 public void PushWaiter(WaiterEntry newWaiterEntry)
364 {
365 // Remove the waiter if it is already in the stack and
366 // update waiter's count as needed
367 RemoveWaiter(newWaiterEntry, false);
368
369 // If the stack is empty then newWaiterEntry is the new head of the stack
370 if (null == _headWaiterEntry._nextWaiterEntry)
371 {
372 _headWaiterEntry._nextWaiterEntry = newWaiterEntry;
373 newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
374
375 }
376 // If the stack is not empty then put newWaiterEntry as the new head
377 // of the stack.
378 else
379 {
380 // Save the old first waiter entry
381 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
327 382
328 // Update the links 383 // Update the links
329 _headWaiterEntry._nextWaiterEntry = newWaiterEntry; 384 _headWaiterEntry._nextWaiterEntry = newWaiterEntry;
330 newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; 385 newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry;
331 newWaiterEntry._prevWaiterEntry = _headWaiterEntry; 386 newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
332 oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; 387 oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry;
333 } 388 }
334 389
335 // Increment the number of waiters 390 // Increment the number of waiters
336 ++_waitersCount; 391 ++_waitersCount;
337 } 392 }
338 393
339 /// <summary> 394 /// <summary>
340 /// Pop a waiter from the waiter's stack 395 /// Pop a waiter from the waiter's stack
341 /// </summary> 396 /// </summary>
342 /// <returns>Returns the first waiter in the stack</returns> 397 /// <returns>Returns the first waiter in the stack</returns>
343 private WaiterEntry PopWaiter() 398 private WaiterEntry PopWaiter()
344 { 399 {
345 // Store the current stack head 400 // Store the current stack head
346 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; 401 WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
347 402
348 // Store the new stack head 403 // Store the new stack head
349 WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; 404 WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry;
350 405
351 // Update the old stack head list links and decrement the number 406 // Update the old stack head list links and decrement the number
352 // waiters. 407 // waiters.
353 RemoveWaiter(oldFirstWaiterEntry, true); 408 RemoveWaiter(oldFirstWaiterEntry, true);
354 409
355 // Update the new stack head 410 // Update the new stack head
356 _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; 411 _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry;
357 if (null != newHeadWaiterEntry) 412 if (null != newHeadWaiterEntry)
358 { 413 {
359 newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; 414 newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry;
360 } 415 }
361 416
362 // Return the old stack head 417 // Return the old stack head
363 return oldFirstWaiterEntry; 418 return oldFirstWaiterEntry;
364 } 419 }
365 420
366 /// <summary> 421 /// <summary>
367 /// Remove a waiter from the stack 422 /// Remove a waiter from the stack
368 /// </summary> 423 /// </summary>
369 /// <param name="waiterEntry">A waiter entry to remove</param> 424 /// <param name="waiterEntry">A waiter entry to remove</param>
370 /// <param name="popDecrement">If true the waiter count is always decremented</param> 425 /// <param name="popDecrement">If true the waiter count is always decremented</param>
371 private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) 426 private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement)
372 { 427 {
373 // Store the prev entry in the list 428 // Store the prev entry in the list
374 WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; 429 WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry;
375 430
376 // Store the next entry in the list 431 // Store the next entry in the list
377 WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; 432 WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry;
378 433
379 // A flag to indicate if we need to decrement the waiters count. 434 // A flag to indicate if we need to decrement the waiters count.
380 // If we got here from PopWaiter then we must decrement. 435 // If we got here from PopWaiter then we must decrement.
381 // If we got here from PushWaiter then we decrement only if 436 // If we got here from PushWaiter then we decrement only if
382 // the waiter was already in the stack. 437 // the waiter was already in the stack.
383 bool decrementCounter = popDecrement; 438 bool decrementCounter = popDecrement;
384 439
385 // Null the waiter's entry links 440 // Null the waiter's entry links
386 waiterEntry._prevWaiterEntry = null; 441 waiterEntry._prevWaiterEntry = null;
387 waiterEntry._nextWaiterEntry = null; 442 waiterEntry._nextWaiterEntry = null;
388 443
389 // If the waiter entry had a prev link then update it. 444 // If the waiter entry had a prev link then update it.
390 // It also means that the waiter is already in the list and we 445 // It also means that the waiter is already in the list and we
391 // need to decrement the waiters count. 446 // need to decrement the waiters count.
392 if (null != prevWaiterEntry) 447 if (null != prevWaiterEntry)
393 { 448 {
394 prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; 449 prevWaiterEntry._nextWaiterEntry = nextWaiterEntry;
395 decrementCounter = true; 450 decrementCounter = true;
396 } 451 }
397 452
398 // If the waiter entry had a next link then update it. 453 // If the waiter entry had a next link then update it.
399 // It also means that the waiter is already in the list and we 454 // It also means that the waiter is already in the list and we
400 // need to decrement the waiters count. 455 // need to decrement the waiters count.
401 if (null != nextWaiterEntry) 456 if (null != nextWaiterEntry)
402 { 457 {
403 nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; 458 nextWaiterEntry._prevWaiterEntry = prevWaiterEntry;
404 decrementCounter = true; 459 decrementCounter = true;
405 } 460 }
406 461
407 // Decrement the waiters count if needed 462 // Decrement the waiters count if needed
408 if (decrementCounter) 463 if (decrementCounter)
409 { 464 {
410 --_waitersCount; 465 --_waitersCount;
411 } 466 }
412 } 467 }
413 468
414 #endregion 469 #endregion
415 470
416 #endregion 471 #endregion
417 472
418 #region WaiterEntry class 473 #region WaiterEntry class
419 474
420 // A waiter entry in the _waiters queue. 475 // A waiter entry in the _waiters queue.
421 public class WaiterEntry : IDisposable 476 public sealed class WaiterEntry : IDisposable
422 { 477 {
423 #region Member variables 478 #region Member variables
424 479
425 /// <summary> 480 /// <summary>
426 /// Event to signal the waiter that it got the work item. 481 /// Event to signal the waiter that it got the work item.
427 /// </summary> 482 /// </summary>
428 private AutoResetEvent _waitHandle = new AutoResetEvent(false); 483 //private AutoResetEvent _waitHandle = new AutoResetEvent(false);
429 484 private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();
430 /// <summary> 485
431 /// Flag to know if this waiter already quited from the queue 486 /// <summary>
432 /// because of a timeout. 487 /// Flag to know if this waiter already quited from the queue
433 /// </summary> 488 /// because of a timeout.
434 private bool _isTimedout = false; 489 /// </summary>
435 490 private bool _isTimedout = false;
436 /// <summary> 491
437 /// Flag to know if the waiter was signaled and got a work item. 492 /// <summary>
438 /// </summary> 493 /// Flag to know if the waiter was signaled and got a work item.
439 private bool _isSignaled = false; 494 /// </summary>
495 private bool _isSignaled = false;
440 496
441 /// <summary> 497 /// <summary>
442 /// A work item that passed directly to the waiter withou going 498 /// A work item that passed directly to the waiter withou going
443 /// through the queue 499 /// through the queue
444 /// </summary> 500 /// </summary>
445 private WorkItem _workItem = null; 501 private WorkItem _workItem = null;
446 502
447 private bool _isDisposed = false; 503 private bool _isDisposed = false;
448 504
449 // Linked list members 505 // Linked list members
450 internal WaiterEntry _nextWaiterEntry = null; 506 internal WaiterEntry _nextWaiterEntry = null;
451 internal WaiterEntry _prevWaiterEntry = null; 507 internal WaiterEntry _prevWaiterEntry = null;
452 508
453 #endregion 509 #endregion
454 510
455 #region Construction 511 #region Construction
456 512
457 public WaiterEntry() 513 public WaiterEntry()
458 { 514 {
459 Reset(); 515 Reset();
460 } 516 }
461 517
462 #endregion 518 #endregion
463 519
464 #region Public methods 520 #region Public methods
465 521
466 public WaitHandle WaitHandle 522 public WaitHandle WaitHandle
467 { 523 {
468 get { return _waitHandle; } 524 get { return _waitHandle; }
469 } 525 }
470 526
471 public WorkItem WorkItem 527 public WorkItem WorkItem
472 { 528 {
473 get 529 get
474 { 530 {
475 lock(this) 531 return _workItem;
476 { 532 }
477 return _workItem; 533 }
478 } 534
479 } 535 /// <summary>
480 } 536 /// Signal the waiter that it got a work item.
481 537 /// </summary>
482 /// <summary> 538 /// <returns>Return true on success</returns>
483 /// Signal the waiter that it got a work item. 539 /// The method fails if Timeout() preceded its call
484 /// </summary> 540 public bool Signal(WorkItem workItem)
485 /// <returns>Return true on success</returns> 541 {
486 /// The method fails if Timeout() preceded its call 542 lock(this)
487 public bool Signal(WorkItem workItem) 543 {
488 { 544 if (!_isTimedout)
489 lock(this) 545 {
490 { 546 _workItem = workItem;
491 if (!_isTimedout) 547 _isSignaled = true;
492 { 548 _waitHandle.Set();
493 _workItem = workItem; 549 return true;
494 _isSignaled = true; 550 }
495 _waitHandle.Set(); 551 }
496 return true; 552 return false;
497 } 553 }
498 } 554
499 return false; 555 /// <summary>
500 } 556 /// Mark the wait entry that it has been timed out
501 557 /// </summary>
502 /// <summary> 558 /// <returns>Return true on success</returns>
503 /// Mark the wait entry that it has been timed out 559 /// The method fails if Signal() preceded its call
504 /// </summary> 560 public bool Timeout()
505 /// <returns>Return true on success</returns> 561 {
506 /// The method fails if Signal() preceded its call 562 lock(this)
507 public bool Timeout() 563 {
508 { 564 // Time out can happen only if the waiter wasn't marked as
509 lock(this) 565 // signaled
566 if (!_isSignaled)
567 {
568 // We don't remove the waiter from the queue, the DequeueWorkItem
569 // method skips _waiters that were timed out.
570 _isTimedout = true;
571 return true;
572 }
573 }
574 return false;
575 }
576
577 /// <summary>
578 /// Reset the wait entry so it can be used again
579 /// </summary>
580 public void Reset()
581 {
582 _workItem = null;
583 _isTimedout = false;
584 _isSignaled = false;
585 _waitHandle.Reset();
586 }
587
588 /// <summary>
589 /// Free resources
590 /// </summary>
591 public void Close()
592 {
593 if (null != _waitHandle)
594 {
595 _waitHandle.Close();
596 _waitHandle = null;
597 }
598 }
599
600 #endregion
601
602 #region IDisposable Members
603
604 public void Dispose()
605 {
606 lock (this)
510 { 607 {
511 // Time out can happen only if the waiter wasn't marked as 608 if (!_isDisposed)
512 // signaled
513 if (!_isSignaled)
514 { 609 {
515 // We don't remove the waiter from the queue, the DequeueWorkItem 610 Close();
516 // method skips _waiters that were timed out.
517 _isTimedout = true;
518 return true;
519 } 611 }
520 }
521 return false;
522 }
523
524 /// <summary>
525 /// Reset the wait entry so it can be used again
526 /// </summary>
527 public void Reset()
528 {
529 _workItem = null;
530 _isTimedout = false;
531 _isSignaled = false;
532 _waitHandle.Reset();
533 }
534
535 /// <summary>
536 /// Free resources
537 /// </summary>
538 public void Close()
539 {
540 if (null != _waitHandle)
541 {
542 _waitHandle.Close();
543 _waitHandle = null;
544 }
545 }
546
547 #endregion
548
549 #region IDisposable Members
550
551 public void Dispose()
552 {
553 if (!_isDisposed)
554 {
555 Close();
556 _isDisposed = true; 612 _isDisposed = true;
557 } 613 }
558 } 614 }
559 615
560 ~WaiterEntry() 616 #endregion
561 { 617 }
562 Dispose();
563 }
564 618
565 #endregion 619 #endregion
566 }
567
568 #endregion
569 620
570 #region IDisposable Members 621 #region IDisposable Members
571 622
@@ -574,14 +625,8 @@ namespace Amib.Threading.Internal
574 if (!_isDisposed) 625 if (!_isDisposed)
575 { 626 {
576 Cleanup(); 627 Cleanup();
577 _isDisposed = true;
578 GC.SuppressFinalize(this);
579 } 628 }
580 } 629 _isDisposed = true;
581
582 ~WorkItemsQueue()
583 {
584 Cleanup();
585 } 630 }
586 631
587 private void ValidateNotDisposed() 632 private void ValidateNotDisposed()
@@ -595,6 +640,6 @@ namespace Amib.Threading.Internal
595 #endregion 640 #endregion
596 } 641 }
597 642
598 #endregion 643 #endregion
599} 644}
600 645