Merge branch 'master' into careminster

Conflicts:
	ThirdParty/SmartThreadPool/SmartThreadPool.cs
avinationmerge
Melanie 2013-05-01 21:39:49 +01:00
commit 8c5445b774
37 changed files with 6745 additions and 4828 deletions

View File

@ -158,7 +158,7 @@ namespace OpenSim.Framework.Capabilities
/// capabilities and their handler details. /// capabilities and their handler details.
/// </summary> /// </summary>
/// <param name="excludeSeed">If true, then exclude the seed cap.</param> /// <param name="excludeSeed">If true, then exclude the seed cap.</param>
public Hashtable GetCapsDetails(bool excludeSeed) public Hashtable GetCapsDetails(bool excludeSeed, List<string> requestedCaps)
{ {
Hashtable caps = new Hashtable(); Hashtable caps = new Hashtable();
string protocol = "http://"; string protocol = "http://";
@ -175,6 +175,9 @@ namespace OpenSim.Framework.Capabilities
if (excludeSeed && "SEED" == capsName) if (excludeSeed && "SEED" == capsName)
continue; continue;
if (requestedCaps != null && !requestedCaps.Contains(capsName))
continue;
caps[capsName] = baseUrl + m_capsHandlers[capsName].Path; caps[capsName] = baseUrl + m_capsHandlers[capsName].Path;
} }
} }

View File

@ -1852,7 +1852,7 @@ namespace OpenSim.Framework
case FireAndForgetMethod.SmartThreadPool: case FireAndForgetMethod.SmartThreadPool:
if (m_ThreadPool == null) if (m_ThreadPool == null)
InitThreadPool(15); InitThreadPool(15);
m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj);
break; break;
case FireAndForgetMethod.Thread: case FireAndForgetMethod.Thread:
Thread thread = new Thread(delegate(object o) { realCallback(o); }); Thread thread = new Thread(delegate(object o) { realCallback(o); });
@ -1922,15 +1922,15 @@ namespace OpenSim.Framework
return sb.ToString(); return sb.ToString();
} }
private static object SmartThreadPoolCallback(object o) // private static object SmartThreadPoolCallback(object o)
{ // {
object[] array = (object[])o; // object[] array = (object[])o;
WaitCallback callback = (WaitCallback)array[0]; // WaitCallback callback = (WaitCallback)array[0];
object obj = array[1]; // object obj = array[1];
//
callback(obj); // callback(obj);
return null; // return null;
} // }
#endregion FireAndForget Threading Pattern #endregion FireAndForget Threading Pattern

View File

@ -355,11 +355,22 @@ namespace OpenSim.Region.ClientStack.Linden
return string.Empty; return string.Empty;
} }
Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true); OSDArray capsRequested = (OSDArray)OSDParser.DeserializeLLSDXml(request);
List<string> validCaps = new List<string>();
foreach (OSD c in capsRequested)
validCaps.Add(c.AsString());
Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps);
// Add the external too // Add the external too
foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers) foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers)
{
if (!validCaps.Contains(kvp.Key))
continue;
caps[kvp.Key] = kvp.Value; caps[kvp.Key] = kvp.Value;
}
string result = LLSDHelpers.SerialiseLLSDReply(caps); string result = LLSDHelpers.SerialiseLLSDReply(caps);

View File

@ -252,7 +252,7 @@ namespace OpenSim.Region.CoreModules.Framework
{ {
caps.AppendFormat("** Circuit {0}:\n", kvp.Key); caps.AppendFormat("** Circuit {0}:\n", kvp.Key);
for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false).GetEnumerator(); kvp2.MoveNext(); ) for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); )
{ {
Uri uri = new Uri(kvp2.Value.ToString()); Uri uri = new Uri(kvp2.Value.ToString());
caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery);

View File

@ -676,11 +676,12 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
} }
else else
{ {
if (remoteClient == null || so.OwnerID != remoteClient.AgentId) if (remoteClient == null || so.RootPart.OwnerID != remoteClient.AgentId)
{ {
// Taking copy of another person's item. Take to // Taking copy of another person's item. Take to
// Objects folder. // Objects folder.
folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object); folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object);
so.FromFolderID = UUID.Zero;
} }
else else
{ {
@ -696,7 +697,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
// //
if (action == DeRezAction.Take || action == DeRezAction.TakeCopy) if (action == DeRezAction.Take || action == DeRezAction.TakeCopy)
{ {
if (so.FromFolderID != UUID.Zero && userID == remoteClient.AgentId) if (so.FromFolderID != UUID.Zero && so.RootPart.OwnerID == remoteClient.AgentId)
{ {
InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID); InventoryFolderBase f = new InventoryFolderBase(so.FromFolderID, userID);
if (f != null) if (f != null)

View File

@ -51,7 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
public interface IScriptWorkItem public interface IScriptWorkItem
{ {
bool Cancel(); bool Cancel();
void Abort(); bool Abort();
/// <summary> /// <summary>
/// Wait for the work item to complete. /// Wait for the work item to complete.

View File

@ -572,9 +572,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public bool Stop(int timeout) public bool Stop(int timeout)
{ {
// m_log.DebugFormat( if (DebugLevel >= 1)
// "[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}", m_log.DebugFormat(
// ScriptName, ItemID, PrimName, ObjectID, timeout, m_InSelfDelete, DateTime.Now.Ticks); "[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}",
ScriptName, ItemID, PrimName, ObjectID, timeout, m_InSelfDelete, DateTime.Now.Ticks);
IScriptWorkItem workItem; IScriptWorkItem workItem;

View File

@ -551,7 +551,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
/// <param name="instance"></param> /// <param name="instance"></param>
/// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</param> /// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</param>
private void HandleScriptsAction<TKey>( private void HandleScriptsAction<TKey>(
string[] cmdparams, Action<IScriptInstance> action, Func<IScriptInstance, TKey> keySelector) string[] cmdparams, Action<IScriptInstance> action, System.Func<IScriptInstance, TKey> keySelector)
{ {
if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene)) if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene))
return; return;
@ -1599,7 +1599,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
startInfo.MaxWorkerThreads = maxThreads; startInfo.MaxWorkerThreads = maxThreads;
startInfo.MinWorkerThreads = minThreads; startInfo.MinWorkerThreads = minThreads;
startInfo.ThreadPriority = threadPriority;; startInfo.ThreadPriority = threadPriority;;
startInfo.StackSize = stackSize; startInfo.MaxStackSize = stackSize;
startInfo.StartSuspended = true; startInfo.StartSuspended = true;
m_ThreadPool = new SmartThreadPool(startInfo); m_ThreadPool = new SmartThreadPool(startInfo);

View File

@ -52,16 +52,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine
return wr.Cancel(); return wr.Cancel();
} }
public void Abort() public bool Abort()
{ {
wr.Abort(); return wr.Cancel(true);
} }
public bool Wait(int t) public bool Wait(int t)
{ {
// We use the integer version of WaitAll because the current version of SmartThreadPool has a bug with the // We use the integer version of WaitAll because the current version of SmartThreadPool has a bug with the
// TimeSpan version. The number of milliseconds in TimeSpan is an int64 so when STP casts it down to an // TimeSpan version. The number of milliseconds in TimeSpan is an int64 so when STP casts it down to an
// int (32-bit) we can end up with bad values. This occurs on Windows though curious not on Mono 2.10.8 // int (32-bit) we can end up with bad values. This occurs on Windows though curiously not on Mono 2.10.8
// (or very likely other versions of Mono at least up until 3.0.3). // (or very likely other versions of Mono at least up until 3.0.3).
return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false); return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false);
} }

View File

@ -1,61 +0,0 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
//
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
//
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: CLSCompliant(true)]
//
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("0.7.6.*")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]

View File

@ -1,3 +1,6 @@
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@ -6,7 +9,7 @@ using System.Web;
using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Messaging;
namespace Amib.Threading namespace Amib.Threading.Internal
{ {
#region CallerThreadContext class #region CallerThreadContext class
@ -19,10 +22,10 @@ namespace Amib.Threading
#region Prepare reflection information #region Prepare reflection information
// Cached type information. // Cached type information.
private static MethodInfo getLogicalCallContextMethodInfo = private static readonly MethodInfo getLogicalCallContextMethodInfo =
typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
private static MethodInfo setLogicalCallContextMethodInfo = private static readonly MethodInfo setLogicalCallContextMethodInfo =
typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
private static string HttpContextSlotName = GetHttpContextSlotName(); private static string HttpContextSlotName = GetHttpContextSlotName();
@ -32,8 +35,10 @@ namespace Amib.Threading
FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
if (fi != null) if (fi != null)
{
return (string) fi.GetValue(null); return (string) fi.GetValue(null);
else // Use the default "HttpContext" slot name }
return "HttpContext"; return "HttpContext";
} }
@ -41,8 +46,8 @@ namespace Amib.Threading
#region Private fields #region Private fields
private HttpContext _httpContext = null; private HttpContext _httpContext;
private LogicalCallContext _callContext = null; private LogicalCallContext _callContext;
#endregion #endregion
@ -122,102 +127,12 @@ namespace Amib.Threading
// Restore HttpContext // Restore HttpContext
if (callerThreadContext._httpContext != null) if (callerThreadContext._httpContext != null)
{ {
CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); HttpContext.Current = callerThreadContext._httpContext;
//CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
} }
} }
} }
#endregion #endregion
} }
#endif
/*
// Ami Bar
// amibar@gmail.com
using System;
using System.Threading;
using System.Globalization;
using System.Security.Principal;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
namespace Amib.Threading.Internal
{
#region CallerThreadContext class
/// <summary>
/// This class stores the caller thread context in order to restore
/// it when the work item is executed in the context of the thread
/// from the pool.
/// Note that we can't store the thread's CompressedStack, because
/// it throws a security exception
/// </summary>
public class CallerThreadContext
{
private CultureInfo _culture = null;
private CultureInfo _cultureUI = null;
private IPrincipal _principal;
private System.Runtime.Remoting.Contexts.Context _context;
private static FieldInfo _fieldInfo = GetFieldInfo();
private static FieldInfo GetFieldInfo()
{
Type threadType = typeof(Thread);
return threadType.GetField(
"m_Context",
BindingFlags.Instance | BindingFlags.NonPublic);
}
/// <summary>
/// Constructor
/// </summary>
private CallerThreadContext()
{
}
/// <summary>
/// Captures the current thread context
/// </summary>
/// <returns></returns>
public static CallerThreadContext Capture()
{
CallerThreadContext callerThreadContext = new CallerThreadContext();
Thread thread = Thread.CurrentThread;
callerThreadContext._culture = thread.CurrentCulture;
callerThreadContext._cultureUI = thread.CurrentUICulture;
callerThreadContext._principal = Thread.CurrentPrincipal;
callerThreadContext._context = Thread.CurrentContext;
return callerThreadContext;
}
/// <summary>
/// Applies the thread context stored earlier
/// </summary>
/// <param name="callerThreadContext"></param>
public static void Apply(CallerThreadContext callerThreadContext)
{
Thread thread = Thread.CurrentThread;
thread.CurrentCulture = callerThreadContext._culture;
thread.CurrentUICulture = callerThreadContext._cultureUI;
Thread.CurrentPrincipal = callerThreadContext._principal;
// Uncomment the following block to enable the Thread.CurrentThread
/*
if (null != _fieldInfo)
{
_fieldInfo.SetValue(
Thread.CurrentThread,
callerThreadContext._context);
}
* /
}
}
#endregion
}
*/

View File

@ -0,0 +1,14 @@
namespace Amib.Threading.Internal
{
internal class CanceledWorkItemsGroup
{
public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup();
public CanceledWorkItemsGroup()
{
IsCanceled = false;
}
public bool IsCanceled { get; set; }
}
}

View File

@ -0,0 +1,104 @@
#if (_WINDOWS_CE)
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Amib.Threading.Internal
{
/// <summary>
/// EventWaitHandle class
/// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation.
/// So I wrote this class to implement these two methods with some of their overloads.
/// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny.
/// Note that this class doesn't even inherit from WaitHandle!
/// </summary>
public class STPEventWaitHandle
{
#region Public Constants
public const int WaitTimeout = Timeout.Infinite;
#endregion
#region Private External Constants
private const Int32 WAIT_FAILED = -1;
private const Int32 WAIT_TIMEOUT = 0x102;
private const UInt32 INFINITE = 0xFFFFFFFF;
#endregion
#region WaitAll and WaitAny
internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
{
return waitHandle.WaitOne(millisecondsTimeout, exitContext);
}
private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles)
{
IntPtr[] nativeHandles = new IntPtr[waitHandles.Length];
for (int i = 0; i < waitHandles.Length; i++)
{
nativeHandles[i] = waitHandles[i].Handle;
}
return nativeHandles;
}
public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
{
uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;
IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);
int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout);
if (result == WAIT_TIMEOUT || result == WAIT_FAILED)
{
return false;
}
return true;
}
public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
{
uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;
IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);
int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout);
if (result >= 0 && result < waitHandles.Length)
{
return result;
}
return -1;
}
public static int WaitAny(WaitHandle[] waitHandles)
{
return WaitAny(waitHandles, Timeout.Infinite, false);
}
public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext)
{
int millisecondsTimeout = (int)timeout.TotalMilliseconds;
return WaitAny(waitHandles, millisecondsTimeout, false);
}
#endregion
#region External methods
[DllImport("coredll.dll", SetLastError = true)]
public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds);
#endregion
}
}
#endif

View File

@ -0,0 +1,82 @@
using System.Threading;
#if (_WINDOWS_CE)
using System;
using System.Runtime.InteropServices;
#endif
namespace Amib.Threading.Internal
{
/// <summary>
/// EventWaitHandleFactory class.
/// This is a static class that creates AutoResetEvent and ManualResetEvent objects.
/// In WindowCE the WaitForMultipleObjects API fails to use the Handle property
/// of XxxResetEvent. It can use only handles that were created by the CreateEvent API.
/// Consequently this class creates the needed XxxResetEvent and replaces the handle if
/// it's a WindowsCE OS.
/// </summary>
public static class EventWaitHandleFactory
{
/// <summary>
/// Create a new AutoResetEvent object
/// </summary>
/// <returns>Return a new AutoResetEvent object</returns>
public static AutoResetEvent CreateAutoResetEvent()
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
#if (_WINDOWS_CE)
ReplaceEventHandle(waitHandle, false, false);
#endif
return waitHandle;
}
/// <summary>
/// Create a new ManualResetEvent object
/// </summary>
/// <returns>Return a new ManualResetEvent object</returns>
public static ManualResetEvent CreateManualResetEvent(bool initialState)
{
ManualResetEvent waitHandle = new ManualResetEvent(initialState);
#if (_WINDOWS_CE)
ReplaceEventHandle(waitHandle, true, initialState);
#endif
return waitHandle;
}
#if (_WINDOWS_CE)
/// <summary>
/// Replace the event handle
/// </summary>
/// <param name="waitHandle">The WaitHandle object which its handle needs to be replaced.</param>
/// <param name="manualReset">Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false)</param>
/// <param name="initialState">The initial state of the event</param>
private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState)
{
// Store the old handle
IntPtr oldHandle = waitHandle.Handle;
// Create a new event
IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null);
// Replace the old event with the new event
waitHandle.Handle = newHandle;
// Close the old event
CloseHandle (oldHandle);
}
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
//Handle
[DllImport("coredll.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
#endif
}
}

View File

@ -1,32 +1,82 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
#if !(_WINDOWS_CE)
using System.Runtime.Serialization; using System.Runtime.Serialization;
#endif
namespace Amib.Threading namespace Amib.Threading
{ {
#region Exceptions #region Exceptions
/// <summary>
/// Represents an exception in case IWorkItemResult.GetResult has been canceled
/// </summary>
public sealed partial class WorkItemCancelException : Exception
{
public WorkItemCancelException()
{
}
public WorkItemCancelException(string message)
: base(message)
{
}
public WorkItemCancelException(string message, Exception e)
: base(message, e)
{
}
}
/// <summary>
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
/// </summary>
public sealed partial class WorkItemTimeoutException : Exception
{
public WorkItemTimeoutException()
{
}
public WorkItemTimeoutException(string message)
: base(message)
{
}
public WorkItemTimeoutException(string message, Exception e)
: base(message, e)
{
}
}
/// <summary>
/// Represents an exception in case IWorkItemResult.GetResult has been timed out
/// </summary>
public sealed partial class WorkItemResultException : Exception
{
public WorkItemResultException()
{
}
public WorkItemResultException(string message)
: base(message)
{
}
public WorkItemResultException(string message, Exception e)
: base(message, e)
{
}
}
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
/// <summary> /// <summary>
/// Represents an exception in case IWorkItemResult.GetResult has been canceled /// Represents an exception in case IWorkItemResult.GetResult has been canceled
/// </summary> /// </summary>
[Serializable] [Serializable]
public sealed class WorkItemCancelException : ApplicationException public sealed partial class WorkItemCancelException
{ {
public WorkItemCancelException() : base() public WorkItemCancelException(SerializationInfo si, StreamingContext sc)
{ : base(si, sc)
}
public WorkItemCancelException(string message) : base(message)
{
}
public WorkItemCancelException(string message, Exception e) : base(message, e)
{
}
public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc)
{ {
} }
} }
@ -35,21 +85,10 @@ namespace Amib.Threading
/// Represents an exception in case IWorkItemResult.GetResult has been timed out /// Represents an exception in case IWorkItemResult.GetResult has been timed out
/// </summary> /// </summary>
[Serializable] [Serializable]
public sealed class WorkItemTimeoutException : ApplicationException public sealed partial class WorkItemTimeoutException
{ {
public WorkItemTimeoutException() : base() public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc)
{ : base(si, sc)
}
public WorkItemTimeoutException(string message) : base(message)
{
}
public WorkItemTimeoutException(string message, Exception e) : base(message, e)
{
}
public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc)
{ {
} }
} }
@ -58,24 +97,15 @@ namespace Amib.Threading
/// Represents an exception in case IWorkItemResult.GetResult has been timed out /// Represents an exception in case IWorkItemResult.GetResult has been timed out
/// </summary> /// </summary>
[Serializable] [Serializable]
public sealed class WorkItemResultException : ApplicationException public sealed partial class WorkItemResultException
{ {
public WorkItemResultException() : base() public WorkItemResultException(SerializationInfo si, StreamingContext sc)
: base(si, sc)
{ {
} }
}
public WorkItemResultException(string message) : base(message) #endif
{
}
public WorkItemResultException(string message, Exception e) : base(message, e)
{
}
public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc)
{
}
}
#endregion #endregion
} }

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Threading; using System.Threading;
@ -20,16 +17,39 @@ namespace Amib.Threading
/// <param name="wir">The work item result object</param> /// <param name="wir">The work item result object</param>
public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
/// <summary>
/// A delegate to call after the WorkItemCallback completed
/// </summary>
/// <param name="wir">The work item result object</param>
public delegate void PostExecuteWorkItemCallback<TResult>(IWorkItemResult<TResult> wir);
/// <summary> /// <summary>
/// A delegate to call when a WorkItemsGroup becomes idle /// A delegate to call when a WorkItemsGroup becomes idle
/// </summary> /// </summary>
/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param> /// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
/// <summary>
/// A delegate to call after a thread is created, but before
/// it's first use.
/// </summary>
public delegate void ThreadInitializationHandler();
/// <summary>
/// A delegate to call when a thread is about to exit, after
/// it is no longer belong to the pool.
/// </summary>
public delegate void ThreadTerminationHandler();
#endregion #endregion
#region WorkItem Priority #region WorkItem Priority
/// <summary>
/// Defines the availeable priorities of a work item.
/// The higher the priority a work item has, the sooner
/// it will be executed.
/// </summary>
public enum WorkItemPriority public enum WorkItemPriority
{ {
Lowest, Lowest,
@ -41,19 +61,11 @@ namespace Amib.Threading
#endregion #endregion
#region IHasWorkItemPriority interface
public interface IHasWorkItemPriority
{
WorkItemPriority WorkItemPriority { get; }
}
#endregion
#region IWorkItemsGroup interface #region IWorkItemsGroup interface
/// <summary> /// <summary>
/// IWorkItemsGroup interface /// IWorkItemsGroup interface
/// Created by SmartThreadPool.CreateWorkItemsGroup()
/// </summary> /// </summary>
public interface IWorkItemsGroup public interface IWorkItemsGroup
{ {
@ -62,27 +74,293 @@ namespace Amib.Threading
/// </summary> /// </summary>
string Name { get; set; } string Name { get; set; }
IWorkItemResult QueueWorkItem(WorkItemCallback callback); /// <summary>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); /// </summary>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); int Concurrency { get; set; }
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); /// <summary>
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); /// Get the number of work items waiting in the queue.
/// </summary>
int WaitingCallbacks { get; }
/// <summary>
/// Get an array with all the state objects of the currently running items.
/// The array represents a snap shot and impact performance.
/// </summary>
object[] GetStates();
/// <summary>
/// Get the WorkItemsGroup start information
/// </summary>
WIGStartInfo WIGStartInfo { get; }
/// <summary>
/// Starts to execute work items
/// </summary>
void Start();
/// <summary>
/// Cancel all the work items.
/// Same as Cancel(false)
/// </summary>
void Cancel();
/// <summary>
/// Cancel all work items using thread abortion
/// </summary>
/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
void Cancel(bool abortExecution);
/// <summary>
/// Wait for all work item to complete.
/// </summary>
void WaitForIdle(); void WaitForIdle();
/// <summary>
/// Wait for all work item to complete, until timeout expired
/// </summary>
/// <param name="timeout">How long to wait for the work items to complete</param>
/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
bool WaitForIdle(TimeSpan timeout); bool WaitForIdle(TimeSpan timeout);
/// <summary>
/// Wait for all work item to complete, until timeout expired
/// </summary>
/// <param name="millisecondsTimeout">How long to wait for the work items to complete in milliseconds</param>
/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
bool WaitForIdle(int millisecondsTimeout); bool WaitForIdle(int millisecondsTimeout);
int WaitingCallbacks { get; } /// <summary>
/// IsIdle is true when there are no work items running or queued.
/// </summary>
bool IsIdle { get; }
/// <summary>
/// This event is fired when all work items are completed.
/// (When IsIdle changes to true)
/// This event only work on WorkItemsGroup. On SmartThreadPool
/// it throws the NotImplementedException.
/// </summary>
event WorkItemsGroupIdleHandler OnIdle; event WorkItemsGroupIdleHandler OnIdle;
void Cancel(); #region QueueWorkItem
void Start();
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="workItemPriority">The priority of the work item</param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="workItemInfo">Work item info</param>
/// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="workItemInfo">Work item information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <returns>Returns a work item result</returns>
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
#endregion
#region QueueWorkItem(Action<...>)
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem(Action action);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);
#endregion
#region QueueWorkItem(Func<...>)
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
/// its GetResult() returns a TResult object</returns>
IWorkItemResult<TResult> QueueWorkItem<TResult>(Func<TResult> func);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
/// its GetResult() returns a TResult object</returns>
IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> func, T arg);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
/// its GetResult() returns a TResult object</returns>
IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 arg1, T2 arg2);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
/// its GetResult() returns a TResult object</returns>
IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3);
/// <summary>
/// Queue a work item.
/// </summary>
/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object.
/// its GetResult() returns a TResult object</returns>
IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
#endregion
} }
#endregion #endregion
@ -92,9 +370,24 @@ namespace Amib.Threading
[Flags] [Flags]
public enum CallToPostExecute public enum CallToPostExecute
{ {
/// <summary>
/// Never call to the PostExecute call back
/// </summary>
Never = 0x00, Never = 0x00,
/// <summary>
/// Call to the PostExecute only when the work item is cancelled
/// </summary>
WhenWorkItemCanceled = 0x01, WhenWorkItemCanceled = 0x01,
/// <summary>
/// Call to the PostExecute only when the work item is not cancelled
/// </summary>
WhenWorkItemNotCanceled = 0x02, WhenWorkItemNotCanceled = 0x02,
/// <summary>
/// Always call to the PostExecute
/// </summary>
Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
} }
@ -103,16 +396,43 @@ namespace Amib.Threading
#region IWorkItemResult interface #region IWorkItemResult interface
/// <summary> /// <summary>
/// IWorkItemResult interface /// The common interface of IWorkItemResult and IWorkItemResult&lt;T&gt;
/// </summary> /// </summary>
public interface IWorkItemResult public interface IWaitableResult
{
/// <summary>
/// This method intent is for internal use.
/// </summary>
/// <returns></returns>
IWorkItemResult GetWorkItemResult();
/// <summary>
/// This method intent is for internal use.
/// </summary>
/// <returns></returns>
IWorkItemResult<TResult> GetWorkItemResultT<TResult>();
}
/// <summary>
/// IWorkItemResult interface.
/// Created when a WorkItemCallback work item is queued.
/// </summary>
public interface IWorkItemResult : IWorkItemResult<object>
{
}
/// <summary>
/// IWorkItemResult&lt;TResult&gt; interface.
/// Created when a Func&lt;TResult&gt; work item is queued.
/// </summary>
public interface IWorkItemResult<TResult> : IWaitableResult
{ {
/// <summary> /// <summary>
/// Get the result of the work item. /// Get the result of the work item.
/// If the work item didn't run yet then the caller waits. /// If the work item didn't run yet then the caller waits.
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
object GetResult(); TResult GetResult();
/// <summary> /// <summary>
/// Get the result of the work item. /// Get the result of the work item.
@ -120,7 +440,7 @@ namespace Amib.Threading
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext); bool exitContext);
@ -130,12 +450,10 @@ namespace Amib.Threading
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext); bool exitContext);
void Abort();
/// <summary> /// <summary>
/// Get the result of the work item. /// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
@ -148,7 +466,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle); WaitHandle cancelWaitHandle);
@ -160,7 +478,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle); WaitHandle cancelWaitHandle);
@ -171,16 +489,18 @@ namespace Amib.Threading
/// </summary> /// </summary>
/// <param name="e">Filled with the exception if one was thrown</param> /// <param name="e">Filled with the exception if one was thrown</param>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
object GetResult(out Exception e); TResult GetResult(out Exception e);
/// <summary> /// <summary>
/// Get the result of the work item. /// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout. /// If the work item didn't run yet then the caller waits until timeout.
/// </summary> /// </summary>
/// <param name="millisecondsTimeout"></param>
/// <param name="exitContext"></param>
/// <param name="e">Filled with the exception if one was thrown</param> /// <param name="e">Filled with the exception if one was thrown</param>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
out Exception e); out Exception e);
@ -189,10 +509,12 @@ namespace Amib.Threading
/// Get the result of the work item. /// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout. /// If the work item didn't run yet then the caller waits until timeout.
/// </summary> /// </summary>
/// <param name="exitContext"></param>
/// <param name="e">Filled with the exception if one was thrown</param> /// <param name="e">Filled with the exception if one was thrown</param>
/// <param name="timeout"></param>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext, bool exitContext,
out Exception e); out Exception e);
@ -210,7 +532,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle, WaitHandle cancelWaitHandle,
@ -221,10 +543,13 @@ namespace Amib.Threading
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
/// </summary> /// </summary>
/// <returns>The result of the work item</returns> /// <returns>The result of the work item</returns>
/// <param name="cancelWaitHandle"></param>
/// <param name="e">Filled with the exception if one was thrown</param> /// <param name="e">Filled with the exception if one was thrown</param>
/// <param name="timeout"></param>
/// <param name="exitContext"></param>
/// On timeout throws WorkItemTimeoutException /// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException /// On cancel throws WorkItemCancelException
object GetResult( TResult GetResult(
TimeSpan timeout, TimeSpan timeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle, WaitHandle cancelWaitHandle,
@ -241,16 +566,30 @@ namespace Amib.Threading
bool IsCanceled { get; } bool IsCanceled { get; }
/// <summary> /// <summary>
/// Gets a user-defined object that qualifies or contains information about an asynchronous operation. /// Gets the user-defined object that contains context data
/// for the work item method.
/// </summary> /// </summary>
object State { get; } object State { get; }
/// <summary> /// <summary>
/// Cancel the work item if it didn't start running yet. /// Same as Cancel(false).
/// </summary> /// </summary>
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
bool Cancel(); bool Cancel();
/// <summary>
/// Cancel the work item execution.
/// If the work item is in the queue then it won't execute
/// If the work item is completed, it will remain completed
/// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled
/// property to check if the work item has been cancelled. If the abortExecution is set to true then
/// the Smart Thread Pool will send an AbortException to the running thread to stop the execution
/// of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException.
/// If the work item is already cancelled it will remain cancelled
/// </summary>
/// <param name="abortExecution">When true send an AbortException to the executing thread.</param>
/// <returns>Returns true if the work item was not completed, otherwise false.</returns>
bool Cancel(bool abortExecution);
/// <summary> /// <summary>
/// Get the work item's priority /// Get the work item's priority
/// </summary> /// </summary>
@ -259,7 +598,7 @@ namespace Amib.Threading
/// <summary> /// <summary>
/// Return the result, same as GetResult() /// Return the result, same as GetResult()
/// </summary> /// </summary>
object Result { get; } TResult Result { get; }
/// <summary> /// <summary>
/// Returns the exception if occured otherwise returns null. /// Returns the exception if occured otherwise returns null.
@ -267,5 +606,23 @@ namespace Amib.Threading
object Exception { get; } object Exception { get; }
} }
#endregion
#region .NET 3.5
// All these delegate are built-in .NET 3.5
// Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity.
public delegate void Action();
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
public delegate TResult Func<TResult>();
public delegate TResult Func<T, TResult>(T arg1);
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
#endregion #endregion
} }

View File

@ -0,0 +1,27 @@

namespace Amib.Threading.Internal
{
/// <summary>
/// An internal delegate to call when the WorkItem starts or completes
/// </summary>
internal delegate void WorkItemStateCallback(WorkItem workItem);
internal interface IInternalWorkItemResult
{
event WorkItemStateCallback OnWorkItemStarted;
event WorkItemStateCallback OnWorkItemCompleted;
}
internal interface IInternalWaitableResult
{
/// <summary>
/// This method is intent for internal use.
/// </summary>
IWorkItemResult GetWorkItemResult();
}
public interface IHasWorkItemPriority
{
WorkItemPriority WorkItemPriority { get; }
}
}

View File

@ -1,8 +1,6 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
@ -25,17 +23,17 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Work items queues. There is one for each type of priority /// Work items queues. There is one for each type of priority
/// </summary> /// </summary>
private Queue [] _queues = new Queue[_queuesCount]; private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];
/// <summary> /// <summary>
/// The total number of work items within the queues /// The total number of work items within the queues
/// </summary> /// </summary>
private int _workItemsCount = 0; private int _workItemsCount;
/// <summary> /// <summary>
/// Use with IEnumerable interface /// Use with IEnumerable interface
/// </summary> /// </summary>
private int _version = 0; private int _version;
#endregion #endregion
@ -45,7 +43,7 @@ namespace Amib.Threading.Internal
{ {
for(int i = 0; i < _queues.Length; ++i) for(int i = 0; i < _queues.Length; ++i)
{ {
_queues[i] = new Queue(); _queues[i] = new LinkedList<IHasWorkItemPriority>();
} }
} }
@ -65,7 +63,7 @@ namespace Amib.Threading.Internal
Debug.Assert(queueIndex >= 0); Debug.Assert(queueIndex >= 0);
Debug.Assert(queueIndex < _queuesCount); Debug.Assert(queueIndex < _queuesCount);
_queues[queueIndex].Enqueue(workItem); _queues[queueIndex].AddLast(workItem);
++_workItemsCount; ++_workItemsCount;
++_version; ++_version;
} }
@ -82,7 +80,8 @@ namespace Amib.Threading.Internal
{ {
int queueIndex = GetNextNonEmptyQueue(-1); int queueIndex = GetNextNonEmptyQueue(-1);
Debug.Assert(queueIndex >= 0); Debug.Assert(queueIndex >= 0);
workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority; workItem = _queues[queueIndex].First.Value;
_queues[queueIndex].RemoveFirst();
Debug.Assert(null != workItem); Debug.Assert(null != workItem);
--_workItemsCount; --_workItemsCount;
++_version; ++_version;
@ -128,7 +127,7 @@ namespace Amib.Threading.Internal
{ {
if (_workItemsCount > 0) if (_workItemsCount > 0)
{ {
foreach(Queue queue in _queues) foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
{ {
queue.Clear(); queue.Clear();
} }
@ -159,7 +158,7 @@ namespace Amib.Threading.Internal
/// </summary> /// </summary>
private class PriorityQueueEnumerator : IEnumerator private class PriorityQueueEnumerator : IEnumerator
{ {
private PriorityQueue _priorityQueue; private readonly PriorityQueue _priorityQueue;
private int _version; private int _version;
private int _queueIndex; private int _queueIndex;
private IEnumerator _enumerator; private IEnumerator _enumerator;

View File

@ -0,0 +1,23 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Amib.Threading")]
[assembly: AssemblyDescription("Smart Thread Pool")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Amib.Threading")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")]
[assembly: AssemblyVersion("2.2.3.0")]
#if (_PUBLISH)
[assembly: InternalsVisibleTo("STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad")]
#else
[assembly: InternalsVisibleTo("STPTests")]
#endif

16
ThirdParty/SmartThreadPool/SLExt.cs vendored Normal file
View File

@ -0,0 +1,16 @@
#if _SILVERLIGHT
using System.Threading;
namespace Amib.Threading
{
public enum ThreadPriority
{
Lowest,
BelowNormal,
Normal,
AboveNormal,
Highest,
}
}
#endif

View File

@ -0,0 +1,62 @@
#if !(_WINDOWS_CE)
using System;
using System.Threading;
namespace Amib.Threading.Internal
{
#if _WINDOWS || WINDOWS_PHONE
internal static class STPEventWaitHandle
{
public const int WaitTimeout = Timeout.Infinite;
internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
{
return WaitHandle.WaitAll(waitHandles, millisecondsTimeout);
}
internal static int WaitAny(WaitHandle[] waitHandles)
{
return WaitHandle.WaitAny(waitHandles);
}
internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
{
return WaitHandle.WaitAny(waitHandles, millisecondsTimeout);
}
internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
{
return waitHandle.WaitOne(millisecondsTimeout);
}
}
#else
internal static class STPEventWaitHandle
{
public const int WaitTimeout = Timeout.Infinite;
internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
{
return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
}
internal static int WaitAny(WaitHandle[] waitHandles)
{
return WaitHandle.WaitAny(waitHandles);
}
internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
{
return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
}
internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
{
return waitHandle.WaitOne(millisecondsTimeout, exitContext);
}
}
#endif
}
#endif

View File

@ -1,8 +1,30 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
namespace Amib.Threading
{
public interface ISTPPerformanceCountersReader
{
long InUseThreads { get; }
long ActiveThreads { get; }
long WorkItemsQueued { get; }
long WorkItemsProcessed { get; }
}
}
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
{ {
internal interface ISTPInstancePerformanceCounters : IDisposable
{
void Close();
void SampleThreads(long activeThreads, long inUseThreads);
void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
}
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
internal enum STPPerformanceCounterType internal enum STPPerformanceCounterType
{ {
// Fields // Fields
@ -37,7 +59,7 @@ namespace Amib.Threading.Internal
internal class STPPerformanceCounter internal class STPPerformanceCounter
{ {
// Fields // Fields
private PerformanceCounterType _pcType; private readonly PerformanceCounterType _pcType;
protected string _counterHelp; protected string _counterHelp;
protected string _counterName; protected string _counterName;
@ -47,9 +69,9 @@ namespace Amib.Threading.Internal
string counterHelp, string counterHelp,
PerformanceCounterType pcType) PerformanceCounterType pcType)
{ {
this._counterName = counterName; _counterName = counterName;
this._counterHelp = counterHelp; _counterHelp = counterHelp;
this._pcType = pcType; _pcType = pcType;
} }
public void AddCounterToCollection(CounterCreationDataCollection counterData) public void AddCounterToCollection(CounterCreationDataCollection counterData)
@ -76,7 +98,7 @@ namespace Amib.Threading.Internal
{ {
// Fields // Fields
internal STPPerformanceCounter[] _stpPerformanceCounters; internal STPPerformanceCounter[] _stpPerformanceCounters;
private static STPPerformanceCounters _instance; private static readonly STPPerformanceCounters _instance;
internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
internal const string _stpCategoryName = "SmartThreadPool"; internal const string _stpCategoryName = "SmartThreadPool";
@ -127,19 +149,12 @@ namespace Amib.Threading.Internal
_stpPerformanceCounters[i].AddCounterToCollection(counters); _stpPerformanceCounters[i].AddCounterToCollection(counters);
} }
// *********** Remark for .NET 2.0 ***********
// If you are here, it means you got the warning that this overload
// of the method is deprecated in .NET 2.0. To use the correct
// method overload, uncomment the third argument of
// the method.
#pragma warning disable 0618
PerformanceCounterCategory.Create( PerformanceCounterCategory.Create(
_stpCategoryName, _stpCategoryName,
_stpCategoryHelp, _stpCategoryHelp,
//PerformanceCounterCategoryType.MultiInstance, PerformanceCounterCategoryType.MultiInstance,
counters); counters);
#pragma warning restore 0618
} }
} }
@ -156,16 +171,18 @@ namespace Amib.Threading.Internal
internal class STPInstancePerformanceCounter : IDisposable internal class STPInstancePerformanceCounter : IDisposable
{ {
// Fields // Fields
private bool _isDisposed;
private PerformanceCounter _pcs; private PerformanceCounter _pcs;
// Methods // Methods
protected STPInstancePerformanceCounter() protected STPInstancePerformanceCounter()
{ {
_isDisposed = false;
} }
public STPInstancePerformanceCounter( public STPInstancePerformanceCounter(
string instance, string instance,
STPPerformanceCounterType spcType) STPPerformanceCounterType spcType) : this()
{ {
STPPerformanceCounters counters = STPPerformanceCounters.Instance; STPPerformanceCounters counters = STPPerformanceCounters.Instance;
_pcs = new PerformanceCounter( _pcs = new PerformanceCounter(
@ -176,10 +193,6 @@ namespace Amib.Threading.Internal
_pcs.RawValue = _pcs.RawValue; _pcs.RawValue = _pcs.RawValue;
} }
~STPInstancePerformanceCounter()
{
Close();
}
public void Close() public void Close()
{ {
@ -192,9 +205,20 @@ namespace Amib.Threading.Internal
} }
public void Dispose() public void Dispose()
{
Dispose(true);
}
public virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{ {
Close(); Close();
GC.SuppressFinalize(this); }
}
_isDisposed = true;
} }
public virtual void Increment() public virtual void Increment()
@ -216,27 +240,19 @@ namespace Amib.Threading.Internal
internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
{ {
// Methods // Methods
public STPInstanceNullPerformanceCounter() {}
public override void Increment() {} public override void Increment() {}
public override void IncrementBy(long value) {} public override void IncrementBy(long value) {}
public override void Set(long val) {} public override void Set(long val) {}
} }
internal interface ISTPInstancePerformanceCounters : IDisposable
{
void Close();
void SampleThreads(long activeThreads, long inUseThreads);
void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
}
internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters
{ {
private bool _isDisposed;
// Fields // Fields
private STPInstancePerformanceCounter[] _pcs; private STPInstancePerformanceCounter[] _pcs;
private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter; private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
// Methods // Methods
static STPInstancePerformanceCounters() static STPInstancePerformanceCounters()
@ -246,8 +262,13 @@ namespace Amib.Threading.Internal
public STPInstancePerformanceCounters(string instance) public STPInstancePerformanceCounters(string instance)
{ {
_isDisposed = false;
_pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter]; _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
// STPPerformanceCounters counters = STPPerformanceCounters.Instance;
// Call the STPPerformanceCounters.Instance so the static constructor will
// intialize the STPPerformanceCounters singleton.
STPPerformanceCounters.Instance.GetHashCode();
for (int i = 0; i < _pcs.Length; i++) for (int i = 0; i < _pcs.Length; i++)
{ {
if (instance != null) if (instance != null)
@ -272,22 +293,28 @@ namespace Amib.Threading.Internal
{ {
if (null != _pcs[i]) if (null != _pcs[i])
{ {
_pcs[i].Close(); _pcs[i].Dispose();
} }
} }
_pcs = null; _pcs = null;
} }
} }
~STPInstancePerformanceCounters() public void Dispose()
{
Dispose(true);
}
public virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{ {
Close(); Close();
} }
}
public void Dispose() _isDisposed = true;
{
Close();
GC.SuppressFinalize(this);
} }
private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType) private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
@ -327,21 +354,17 @@ namespace Amib.Threading.Internal
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment(); GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
} }
} }
#endif
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
{ {
static NullSTPInstancePerformanceCounters() private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();
{
}
private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null);
public static NullSTPInstancePerformanceCounters Instance public static NullSTPInstancePerformanceCounters Instance
{ {
get { return _instance; } get { return _instance; }
} }
public NullSTPInstancePerformanceCounters(string instance) {}
public void Close() {} public void Close() {}
public void Dispose() {} public void Dispose() {}
@ -349,6 +372,77 @@ namespace Amib.Threading.Internal
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {} public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {} public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {} public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
public long InUseThreads
{
get { return 0; }
} }
public long ActiveThreads
{
get { return 0; }
}
public long WorkItemsQueued
{
get { return 0; }
}
public long WorkItemsProcessed
{
get { return 0; }
}
}
internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
{
public void Close() { }
public void Dispose() { }
private long _activeThreads;
private long _inUseThreads;
private long _workItemsQueued;
private long _workItemsProcessed;
public long InUseThreads
{
get { return _inUseThreads; }
}
public long ActiveThreads
{
get { return _activeThreads; }
}
public long WorkItemsQueued
{
get { return _workItemsQueued; }
}
public long WorkItemsProcessed
{
get { return _workItemsProcessed; }
}
public void SampleThreads(long activeThreads, long inUseThreads)
{
_activeThreads = activeThreads;
_inUseThreads = inUseThreads;
}
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
{
_workItemsQueued = workItemsQueued;
_workItemsProcessed = workItemsProcessed;
}
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
{
// Not supported
}
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
{
// Not supported
}
}
} }

View File

@ -1,6 +1,4 @@
// Ami Bar using System;
// amibar@gmail.com
using System.Threading; using System.Threading;
namespace Amib.Threading namespace Amib.Threading
@ -10,104 +8,205 @@ namespace Amib.Threading
/// </summary> /// </summary>
public class STPStartInfo : WIGStartInfo public class STPStartInfo : WIGStartInfo
{ {
/// <summary> private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
/// Idle timeout in milliseconds. private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
/// If a thread is idle for _idleTimeout milliseconds then private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
/// it may quit. #if !(WINDOWS_PHONE)
/// </summary> private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
private int _idleTimeout; #endif
private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground;
private bool _enableLocalPerformanceCounters;
private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName;
private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize;
/// <summary> public STPStartInfo()
/// The lower limit of threads in the pool.
/// </summary>
private int _minWorkerThreads;
/// <summary>
/// The upper limit of threads in the pool.
/// </summary>
private int _maxWorkerThreads;
/// <summary>
/// The priority of the threads in the pool
/// </summary>
private ThreadPriority _threadPriority;
/// <summary>
/// The thread pool name. Threads will get names depending on this.
/// </summary>
private string _threadPoolName;
/// <summary>
/// If this field is not null then the performance counters are enabled
/// and use the string as the name of the instance.
/// </summary>
private string _pcInstanceName;
private int _stackSize;
public STPStartInfo() : base()
{ {
_performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
#if !(WINDOWS_PHONE)
_threadPriority = SmartThreadPool.DefaultThreadPriority;
#endif
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
_idleTimeout = SmartThreadPool.DefaultIdleTimeout; _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
_minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
_threadPriority = SmartThreadPool.DefaultThreadPriority;
_threadPoolName = SmartThreadPool.DefaultThreadPoolName;
_pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
_stackSize = SmartThreadPool.DefaultStackSize;
} }
public STPStartInfo(STPStartInfo stpStartInfo) : base(stpStartInfo) public STPStartInfo(STPStartInfo stpStartInfo)
: base(stpStartInfo)
{ {
_idleTimeout = stpStartInfo._idleTimeout; _idleTimeout = stpStartInfo.IdleTimeout;
_minWorkerThreads = stpStartInfo._minWorkerThreads; _minWorkerThreads = stpStartInfo.MinWorkerThreads;
_maxWorkerThreads = stpStartInfo._maxWorkerThreads; _maxWorkerThreads = stpStartInfo.MaxWorkerThreads;
_threadPriority = stpStartInfo._threadPriority; #if !(WINDOWS_PHONE)
_threadPriority = stpStartInfo.ThreadPriority;
#endif
_performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName;
_enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters;
_threadPoolName = stpStartInfo._threadPoolName; _threadPoolName = stpStartInfo._threadPoolName;
_pcInstanceName = stpStartInfo._pcInstanceName; _areThreadsBackground = stpStartInfo.AreThreadsBackground;
_stackSize = stpStartInfo._stackSize; #if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
_apartmentState = stpStartInfo._apartmentState;
#endif
} }
public int IdleTimeout /// <summary>
/// Get/Set the idle timeout in milliseconds.
/// If a thread is idle (starved) longer than IdleTimeout then it may quit.
/// </summary>
public virtual int IdleTimeout
{ {
get { return _idleTimeout; } get { return _idleTimeout; }
set { _idleTimeout = value; } set
{
ThrowIfReadOnly();
_idleTimeout = value;
}
} }
public int MinWorkerThreads
/// <summary>
/// Get/Set the lower limit of threads in the pool.
/// </summary>
public virtual int MinWorkerThreads
{ {
get { return _minWorkerThreads; } get { return _minWorkerThreads; }
set { _minWorkerThreads = value; } set
{
ThrowIfReadOnly();
_minWorkerThreads = value;
}
} }
public int MaxWorkerThreads
/// <summary>
/// Get/Set the upper limit of threads in the pool.
/// </summary>
public virtual int MaxWorkerThreads
{ {
get { return _maxWorkerThreads; } get { return _maxWorkerThreads; }
set { _maxWorkerThreads = value; } set
{
ThrowIfReadOnly();
_maxWorkerThreads = value;
}
} }
public ThreadPriority ThreadPriority #if !(WINDOWS_PHONE)
/// <summary>
/// Get/Set the scheduling priority of the threads in the pool.
/// The Os handles the scheduling.
/// </summary>
public virtual ThreadPriority ThreadPriority
{ {
get { return _threadPriority; } get { return _threadPriority; }
set { _threadPriority = value; } set
}
public virtual string ThreadPoolName
{ {
ThrowIfReadOnly();
_threadPriority = value;
}
}
#endif
/// <summary>
/// Get/Set the thread pool name. Threads will get names depending on this.
/// </summary>
public virtual string ThreadPoolName {
get { return _threadPoolName; } get { return _threadPoolName; }
set { _threadPoolName = value; } set
}
public string PerformanceCounterInstanceName
{ {
get { return _pcInstanceName; } ThrowIfReadOnly ();
set { _pcInstanceName = value; } _threadPoolName = value;
}
} }
public int StackSize /// <summary>
/// Get/Set the performance counter instance name of this SmartThreadPool
/// The default is null which indicate not to use performance counters at all.
/// </summary>
public virtual string PerformanceCounterInstanceName
{ {
get { return _stackSize; } get { return _performanceCounterInstanceName; }
set { _stackSize = value; } set
{
ThrowIfReadOnly();
_performanceCounterInstanceName = value;
} }
} }
/// <summary>
/// Enable/Disable the local performance counter.
/// This enables the user to get some performance information about the SmartThreadPool
/// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.)
/// The default is false.
/// </summary>
public virtual bool EnableLocalPerformanceCounters
{
get { return _enableLocalPerformanceCounters; }
set
{
ThrowIfReadOnly();
_enableLocalPerformanceCounters = value;
}
}
/// <summary>
/// Get/Set backgroundness of thread in thread pool.
/// </summary>
public virtual bool AreThreadsBackground
{
get { return _areThreadsBackground; }
set
{
ThrowIfReadOnly ();
_areThreadsBackground = value;
}
}
/// <summary>
/// Get a readonly version of this STPStartInfo.
/// </summary>
/// <returns>Returns a readonly reference to this STPStartInfo</returns>
public new STPStartInfo AsReadOnly()
{
return new STPStartInfo(this) { _readOnly = true };
}
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState;
/// <summary>
/// Get/Set the apartment state of threads in the thread pool
/// </summary>
public ApartmentState ApartmentState
{
get { return _apartmentState; }
set
{
ThrowIfReadOnly();
_apartmentState = value;
}
}
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
/// <summary>
/// Get/Set the max stack size of threads in the thread pool
/// </summary>
public int? MaxStackSize
{
get { return _maxStackSize; }
set
{
ThrowIfReadOnly();
if (value.HasValue && value.Value < 0)
{
throw new ArgumentOutOfRangeException("value", "Value must be greater than 0.");
}
_maxStackSize = value;
}
}
#endif
#endif
}
} }

View File

@ -0,0 +1,60 @@

using System;
using Amib.Threading.Internal;
namespace Amib.Threading
{
public partial class SmartThreadPool
{
#region ThreadEntry class
internal class ThreadEntry
{
/// <summary>
/// The thread creation time
/// The value is stored as UTC value.
/// </summary>
private readonly DateTime _creationTime;
/// <summary>
/// The last time this thread has been running
/// It is updated by IAmAlive() method
/// The value is stored as UTC value.
/// </summary>
private DateTime _lastAliveTime;
/// <summary>
/// A reference from each thread in the thread pool to its SmartThreadPool
/// object container.
/// With this variable a thread can know whatever it belongs to a
/// SmartThreadPool.
/// </summary>
private readonly SmartThreadPool _associatedSmartThreadPool;
/// <summary>
/// A reference to the current work item a thread from the thread pool
/// is executing.
/// </summary>
public WorkItem CurrentWorkItem { get; set; }
public ThreadEntry(SmartThreadPool stp)
{
_associatedSmartThreadPool = stp;
_creationTime = DateTime.UtcNow;
_lastAliveTime = DateTime.MinValue;
}
public SmartThreadPool AssociatedSmartThreadPool
{
get { return _associatedSmartThreadPool; }
}
public void IAmAlive()
{
_lastAliveTime = DateTime.UtcNow;
}
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
using System.Collections.Generic;
namespace Amib.Threading.Internal
{
internal class SynchronizedDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> _dictionary;
private readonly object _lock;
public SynchronizedDictionary()
{
_lock = new object();
_dictionary = new Dictionary<TKey, TValue>();
}
public int Count
{
get { return _dictionary.Count; }
}
public bool Contains(TKey key)
{
lock (_lock)
{
return _dictionary.ContainsKey(key);
}
}
public void Remove(TKey key)
{
lock (_lock)
{
_dictionary.Remove(key);
}
}
public object SyncRoot
{
get { return _lock; }
}
public TValue this[TKey key]
{
get
{
lock (_lock)
{
return _dictionary[key];
}
}
set
{
lock (_lock)
{
_dictionary[key] = value;
}
}
}
public Dictionary<TKey, TValue>.KeyCollection Keys
{
get
{
lock (_lock)
{
return _dictionary.Keys;
}
}
}
public Dictionary<TKey, TValue>.ValueCollection Values
{
get
{
lock (_lock)
{
return _dictionary.Values;
}
}
}
public void Clear()
{
lock (_lock)
{
_dictionary.Clear();
}
}
}
}

View File

@ -1,5 +1,4 @@
// Ami Bar using System;
// amibar@gmail.com
namespace Amib.Threading namespace Amib.Threading
{ {
@ -8,92 +7,165 @@ namespace Amib.Threading
/// </summary> /// </summary>
public class WIGStartInfo public class WIGStartInfo
{ {
/// <summary>
/// Use the caller's security context
/// </summary>
private bool _useCallerCallContext; private bool _useCallerCallContext;
/// <summary>
/// Use the caller's HTTP context
/// </summary>
private bool _useCallerHttpContext; private bool _useCallerHttpContext;
/// <summary>
/// Dispose of the state object of a work item
/// </summary>
private bool _disposeOfStateObjects; private bool _disposeOfStateObjects;
/// <summary>
/// The option to run the post execute
/// </summary>
private CallToPostExecute _callToPostExecute; private CallToPostExecute _callToPostExecute;
/// <summary>
/// A post execute callback to call when none is provided in
/// the QueueWorkItem method.
/// </summary>
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
/// <summary>
/// Indicate the WorkItemsGroup to suspend the handling of the work items
/// until the Start() method is called.
/// </summary>
private bool _startSuspended; private bool _startSuspended;
private WorkItemPriority _workItemPriority;
private bool _fillStateWithArgs;
protected bool _readOnly;
public WIGStartInfo() public WIGStartInfo()
{ {
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; _workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_startSuspended = SmartThreadPool.DefaultStartSuspended; _startSuspended = SmartThreadPool.DefaultStartSuspended;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
} }
public WIGStartInfo(WIGStartInfo wigStartInfo) public WIGStartInfo(WIGStartInfo wigStartInfo)
{ {
_useCallerCallContext = wigStartInfo._useCallerCallContext; _useCallerCallContext = wigStartInfo.UseCallerCallContext;
_useCallerHttpContext = wigStartInfo._useCallerHttpContext; _useCallerHttpContext = wigStartInfo.UseCallerHttpContext;
_disposeOfStateObjects = wigStartInfo._disposeOfStateObjects; _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
_callToPostExecute = wigStartInfo._callToPostExecute; _callToPostExecute = wigStartInfo.CallToPostExecute;
_postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback; _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
_startSuspended = wigStartInfo._startSuspended; _workItemPriority = wigStartInfo.WorkItemPriority;
_startSuspended = wigStartInfo.StartSuspended;
_fillStateWithArgs = wigStartInfo.FillStateWithArgs;
} }
public bool UseCallerCallContext protected void ThrowIfReadOnly()
{
if (_readOnly)
{
throw new NotSupportedException("This is a readonly instance and set is not supported");
}
}
/// <summary>
/// Get/Set if to use the caller's security context
/// </summary>
public virtual bool UseCallerCallContext
{ {
get { return _useCallerCallContext; } get { return _useCallerCallContext; }
set { _useCallerCallContext = value; } set
{
ThrowIfReadOnly();
_useCallerCallContext = value;
}
} }
public bool UseCallerHttpContext
/// <summary>
/// Get/Set if to use the caller's HTTP context
/// </summary>
public virtual bool UseCallerHttpContext
{ {
get { return _useCallerHttpContext; } get { return _useCallerHttpContext; }
set { _useCallerHttpContext = value; } set
{
ThrowIfReadOnly();
_useCallerHttpContext = value;
}
} }
public bool DisposeOfStateObjects
/// <summary>
/// Get/Set if to dispose of the state object of a work item
/// </summary>
public virtual bool DisposeOfStateObjects
{ {
get { return _disposeOfStateObjects; } get { return _disposeOfStateObjects; }
set { _disposeOfStateObjects = value; } set
{
ThrowIfReadOnly();
_disposeOfStateObjects = value;
}
} }
public CallToPostExecute CallToPostExecute
/// <summary>
/// Get/Set the run the post execute options
/// </summary>
public virtual CallToPostExecute CallToPostExecute
{ {
get { return _callToPostExecute; } get { return _callToPostExecute; }
set { _callToPostExecute = value; } set
{
ThrowIfReadOnly();
_callToPostExecute = value;
}
} }
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
/// <summary>
/// Get/Set the default post execute callback
/// </summary>
public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback
{ {
get { return _postExecuteWorkItemCallback; } get { return _postExecuteWorkItemCallback; }
set { _postExecuteWorkItemCallback = value; } set
{
ThrowIfReadOnly();
_postExecuteWorkItemCallback = value;
}
} }
public bool StartSuspended
/// <summary>
/// Get/Set if the work items execution should be suspended until the Start()
/// method is called.
/// </summary>
public virtual bool StartSuspended
{ {
get { return _startSuspended; } get { return _startSuspended; }
set { _startSuspended = value; } set
{
ThrowIfReadOnly();
_startSuspended = value;
}
}
/// <summary>
/// Get/Set the default priority that a work item gets when it is enqueued
/// </summary>
public virtual WorkItemPriority WorkItemPriority
{
get { return _workItemPriority; }
set { _workItemPriority = value; }
}
/// <summary>
/// Get/Set the if QueueWorkItem of Action&lt;...&gt;/Func&lt;...&gt; fill the
/// arguments as an object array into the state of the work item.
/// The arguments can be access later by IWorkItemResult.State.
/// </summary>
public virtual bool FillStateWithArgs
{
get { return _fillStateWithArgs; }
set
{
ThrowIfReadOnly();
_fillStateWithArgs = value;
}
}
/// <summary>
/// Get a readonly version of this WIGStartInfo
/// </summary>
/// <returns>Returns a readonly reference to this WIGStartInfoRO</returns>
public WIGStartInfo AsReadOnly()
{
return new WIGStartInfo(this) { _readOnly = true };
} }
} }
} }

View File

@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Amib.Threading.Internal
{
public partial class WorkItem
{
#region WorkItemResult class
private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult
{
/// <summary>
/// A back reference to the work item
/// </summary>
private readonly WorkItem _workItem;
public WorkItemResult(WorkItem workItem)
{
_workItem = workItem;
}
internal WorkItem GetWorkItem()
{
return _workItem;
}
#region IWorkItemResult Members
public bool IsCompleted
{
get
{
return _workItem.IsCompleted;
}
}
public bool IsCanceled
{
get
{
return _workItem.IsCanceled;
}
}
public object GetResult()
{
return _workItem.GetResult(Timeout.Infinite, true, null);
}
public object GetResult(int millisecondsTimeout, bool exitContext)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, null);
}
public object GetResult(TimeSpan timeout, bool exitContext)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null);
}
public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
}
public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
}
public object GetResult(out Exception e)
{
return _workItem.GetResult(Timeout.Infinite, true, null, out e);
}
public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e);
}
public object GetResult(TimeSpan timeout, bool exitContext, out Exception e)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e);
}
public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
}
public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e);
}
public bool Cancel()
{
return Cancel(false);
}
public bool Cancel(bool abortExecution)
{
return _workItem.Cancel(abortExecution);
}
public object State
{
get
{
return _workItem._state;
}
}
public WorkItemPriority WorkItemPriority
{
get
{
return _workItem._workItemInfo.WorkItemPriority;
}
}
/// <summary>
/// Return the result, same as GetResult()
/// </summary>
public object Result
{
get { return GetResult(); }
}
/// <summary>
/// Returns the exception if occured otherwise returns null.
/// This value is valid only after the work item completed,
/// before that it is always null.
/// </summary>
public object Exception
{
get { return _workItem._exception; }
}
#endregion
#region IInternalWorkItemResult Members
public event WorkItemStateCallback OnWorkItemStarted
{
add
{
_workItem.OnWorkItemStarted += value;
}
remove
{
_workItem.OnWorkItemStarted -= value;
}
}
public event WorkItemStateCallback OnWorkItemCompleted
{
add
{
_workItem.OnWorkItemCompleted += value;
}
remove
{
_workItem.OnWorkItemCompleted -= value;
}
}
#endregion
#region IInternalWorkItemResult Members
public IWorkItemResult GetWorkItemResult()
{
return this;
}
public IWorkItemResult<TResult> GetWorkItemResultT<TResult>()
{
return new WorkItemResultTWrapper<TResult>(this);
}
#endregion
}
#endregion
}
}

View File

@ -1,58 +1,13 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Threading; using System.Threading;
using System.Diagnostics; using System.Diagnostics;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
{ {
#region WorkItem Delegate
/// <summary>
/// An internal delegate to call when the WorkItem starts or completes
/// </summary>
internal delegate void WorkItemStateCallback(WorkItem workItem);
#endregion
#region IInternalWorkItemResult interface
public class CanceledWorkItemsGroup
{
public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup();
private bool _isCanceled = false;
public bool IsCanceled
{
get { return _isCanceled; }
set { _isCanceled = value; }
}
}
internal interface IInternalWorkItemResult
{
event WorkItemStateCallback OnWorkItemStarted;
event WorkItemStateCallback OnWorkItemCompleted;
}
#endregion
#region IWorkItem interface
public interface IWorkItem
{
}
#endregion
#region WorkItem class
/// <summary> /// <summary>
/// Holds a callback delegate and the state for that delegate. /// Holds a callback delegate and the state for that delegate.
/// </summary> /// </summary>
public class WorkItem : IHasWorkItemPriority, IWorkItem public partial class WorkItem : IHasWorkItemPriority
{ {
#region WorkItemState enum #region WorkItemState enum
@ -61,33 +16,57 @@ namespace Amib.Threading.Internal
/// </summary> /// </summary>
private enum WorkItemState private enum WorkItemState
{ {
InQueue, InQueue = 0, // Nexts: InProgress, Canceled
InProgress, InProgress = 1, // Nexts: Completed, Canceled
Completed, Completed = 2, // Stays Completed
Canceled, Canceled = 3, // Stays Canceled
}
private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState)
{
bool valid = false;
switch (currentState)
{
case WorkItemState.InQueue:
valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState);
break;
case WorkItemState.InProgress:
valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState);
break;
case WorkItemState.Completed:
case WorkItemState.Canceled:
// Cannot be changed
break;
default:
// Unknown state
Debug.Assert(false);
break;
}
return valid;
} }
#endregion #endregion
#region Member Variables #region Fields
public Thread currentThread;
/// <summary> /// <summary>
/// Callback delegate for the callback. /// Callback delegate for the callback.
/// </summary> /// </summary>
private WorkItemCallback _callback; private readonly WorkItemCallback _callback;
/// <summary> /// <summary>
/// State with which to call the callback delegate. /// State with which to call the callback delegate.
/// </summary> /// </summary>
private object _state; private object _state;
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
/// <summary> /// <summary>
/// Stores the caller's context /// Stores the caller's context
/// </summary> /// </summary>
private CallerThreadContext _callerContext; private readonly CallerThreadContext _callerContext;
#endif
/// <summary> /// <summary>
/// Holds the result of the mehtod /// Holds the result of the mehtod
/// </summary> /// </summary>
@ -117,12 +96,12 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Represents the result state of the work item /// Represents the result state of the work item
/// </summary> /// </summary>
private WorkItemResult _workItemResult; private readonly WorkItemResult _workItemResult;
/// <summary> /// <summary>
/// Work item info /// Work item info
/// </summary> /// </summary>
private WorkItemInfo _workItemInfo; private readonly WorkItemInfo _workItemInfo;
/// <summary> /// <summary>
/// Called when the WorkItem starts /// Called when the WorkItem starts
@ -141,30 +120,41 @@ namespace Amib.Threading.Internal
private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
/// <summary> /// <summary>
/// The work item group this work item belong to. /// A reference to an object that indicates whatever the
/// /// SmartThreadPool has been canceled
/// </summary> /// </summary>
private IWorkItemsGroup _workItemsGroup; private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
/// <summary>
/// The work item group this work item belong to.
/// </summary>
private readonly IWorkItemsGroup _workItemsGroup;
/// <summary>
/// The thread that executes this workitem.
/// This field is available for the period when the work item is executed, before and after it is null.
/// </summary>
private Thread _executingThread;
/// <summary>
/// The absulote time when the work item will be timeout
/// </summary>
private long _expirationTime;
#region Performance Counter fields #region Performance Counter fields
/// <summary>
/// The time when the work items is queued.
/// Used with the performance counter.
/// </summary>
private DateTime _queuedTime;
/// <summary> /// <summary>
/// The time when the work items starts its execution. /// Stores how long the work item waited on the stp queue
/// Used with the performance counter.
/// </summary> /// </summary>
private DateTime _beginProcessTime; private Stopwatch _waitingOnQueueStopwatch;
/// <summary> /// <summary>
/// The time when the work items ends its execution. /// Stores how much time it took the work item to execute after it went out of the queue
/// Used with the performance counter.
/// </summary> /// </summary>
private DateTime _endProcessTime; private Stopwatch _processingStopwatch;
#endregion #endregion
@ -176,7 +166,7 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
return (_beginProcessTime - _queuedTime); return _waitingOnQueueStopwatch.Elapsed;
} }
} }
@ -184,7 +174,15 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
return (_endProcessTime - _beginProcessTime); return _processingStopwatch.Elapsed;
}
}
internal WorkItemInfo WorkItemInfo
{
get
{
return _workItemInfo;
} }
} }
@ -195,6 +193,8 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Initialize the callback holding object. /// Initialize the callback holding object.
/// </summary> /// </summary>
/// <param name="workItemsGroup">The workItemGroup of the workitem</param>
/// <param name="workItemInfo">The WorkItemInfo of te workitem</param>
/// <param name="callback">Callback delegate for the callback.</param> /// <param name="callback">Callback delegate for the callback.</param>
/// <param name="state">State with which to call the callback delegate.</param> /// <param name="state">State with which to call the callback delegate.</param>
/// ///
@ -209,10 +209,12 @@ namespace Amib.Threading.Internal
_workItemsGroup = workItemsGroup; _workItemsGroup = workItemsGroup;
_workItemInfo = workItemInfo; _workItemInfo = workItemInfo;
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
{ {
_callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
} }
#endif
_callback = callback; _callback = callback;
_state = state; _state = state;
@ -222,9 +224,18 @@ namespace Amib.Threading.Internal
internal void Initialize() internal void Initialize()
{ {
// The _workItemState is changed directly instead of using the SetWorkItemState
// method since we don't want to go throught IsValidStateTransition.
_workItemState = WorkItemState.InQueue; _workItemState = WorkItemState.InQueue;
_workItemCompleted = null; _workItemCompleted = null;
_workItemCompletedRefCount = 0; _workItemCompletedRefCount = 0;
_waitingOnQueueStopwatch = new Stopwatch();
_processingStopwatch = new Stopwatch();
_expirationTime =
_workItemInfo.Timeout > 0 ?
DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond :
long.MaxValue;
} }
internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup)
@ -237,17 +248,16 @@ namespace Amib.Threading.Internal
#region Methods #region Methods
public CanceledWorkItemsGroup CanceledWorkItemsGroup internal CanceledWorkItemsGroup CanceledWorkItemsGroup
{ {
get get { return _canceledWorkItemsGroup; }
{ set { _canceledWorkItemsGroup = value; }
return _canceledWorkItemsGroup;
} }
set internal CanceledWorkItemsGroup CanceledSmartThreadPool
{ {
_canceledWorkItemsGroup = value; get { return _canceledSmartThreadPool; }
} set { _canceledSmartThreadPool = value; }
} }
/// <summary> /// <summary>
@ -259,7 +269,8 @@ namespace Amib.Threading.Internal
/// </returns> /// </returns>
public bool StartingWorkItem() public bool StartingWorkItem()
{ {
_beginProcessTime = DateTime.Now; _waitingOnQueueStopwatch.Stop();
_processingStopwatch.Start();
lock (this) lock (this)
{ {
@ -277,6 +288,9 @@ namespace Amib.Threading.Internal
Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); Debug.Assert(WorkItemState.InQueue == GetWorkItemState());
// No need for a lock yet, only after the state has changed to InProgress
_executingThread = Thread.CurrentThread;
SetWorkItemState(WorkItemState.InProgress); SetWorkItemState(WorkItemState.InProgress);
} }
@ -311,7 +325,7 @@ namespace Amib.Threading.Internal
PostExecute(); PostExecute();
} }
_endProcessTime = DateTime.Now; _processingStopwatch.Stop();
} }
internal void FireWorkItemCompleted() internal void FireWorkItemCompleted()
@ -323,7 +337,20 @@ namespace Amib.Threading.Internal
_workItemCompletedEvent(this); _workItemCompletedEvent(this);
} }
} }
catch // Ignore exceptions catch // Suppress exceptions
{ }
}
internal void FireWorkItemStarted()
{
try
{
if (null != _workItemStartedEvent)
{
_workItemStartedEvent(this);
}
}
catch // Suppress exceptions
{ } { }
} }
@ -332,16 +359,21 @@ namespace Amib.Threading.Internal
/// </summary> /// </summary>
private void ExecuteWorkItem() private void ExecuteWorkItem()
{ {
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
CallerThreadContext ctc = null; CallerThreadContext ctc = null;
if (null != _callerContext) if (null != _callerContext)
{ {
ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
CallerThreadContext.Apply(_callerContext); CallerThreadContext.Apply(_callerContext);
} }
#endif
Exception exception = null; Exception exception = null;
object result = null; object result = null;
try
{
try try
{ {
result = _callback(_state); result = _callback(_state);
@ -352,13 +384,46 @@ namespace Amib.Threading.Internal
exception = e; exception = e;
} }
// Remove the value of the execution thread, so it will be impossible to cancel the work item,
// since it is already completed.
// Cancelling a work item that already completed may cause the abortion of the next work item!!!
Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread);
if (null == executionThread)
{
// Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException
Thread.Sleep(60 * 1000);
// If after 1 minute this thread was not aborted then let it continue working.
}
}
// We must treat the ThreadAbortException or else it will be stored in the exception variable
catch (ThreadAbortException tae)
{
tae.GetHashCode();
// Check if the work item was cancelled
// If we got a ThreadAbortException and the STP is not shutting down, it means the
// work items was cancelled.
if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown)
{
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
Thread.ResetAbort();
#endif
}
}
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
if (null != _callerContext) if (null != _callerContext)
{ {
CallerThreadContext.Apply(ctc); CallerThreadContext.Apply(ctc);
} }
#endif
if (!SmartThreadPool.IsWorkItemCanceled)
{
SetResult(result, exception); SetResult(result, exception);
} }
}
/// <summary> /// <summary>
/// Runs the post execute callback /// Runs the post execute callback
@ -369,7 +434,7 @@ namespace Amib.Threading.Internal
{ {
try try
{ {
_workItemInfo.PostExecuteWorkItemCallback(this._workItemResult); _workItemInfo.PostExecuteWorkItemCallback(_workItemResult);
} }
catch (Exception e) catch (Exception e)
{ {
@ -382,6 +447,8 @@ namespace Amib.Threading.Internal
/// Set the result of the work item to return /// Set the result of the work item to return
/// </summary> /// </summary>
/// <param name="result">The result of the work item</param> /// <param name="result">The result of the work item</param>
/// <param name="exception">The exception that was throw while the workitem executed, null
/// if there was no exception.</param>
internal void SetResult(object result, Exception exception) internal void SetResult(object result, Exception exception)
{ {
_result = result; _result = result;
@ -401,39 +468,39 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Wait for all work items to complete /// Wait for all work items to complete
/// </summary> /// </summary>
/// <param name="workItemResults">Array of work item result objects</param> /// <param name="waitableResults">Array of work item result objects</param>
/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
/// <param name="exitContext"> /// <param name="exitContext">
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
/// </param> /// </param>
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param> /// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
/// <returns> /// <returns>
/// true when every work item in workItemResults has completed; otherwise false. /// true when every work item in waitableResults has completed; otherwise false.
/// </returns> /// </returns>
internal static bool WaitAll( internal static bool WaitAll(
IWorkItemResult [] workItemResults, IWaitableResult[] waitableResults,
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle) WaitHandle cancelWaitHandle)
{ {
if (0 == workItemResults.Length) if (0 == waitableResults.Length)
{ {
return true; return true;
} }
bool success; bool success;
WaitHandle [] waitHandles = new WaitHandle[workItemResults.Length];; WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length];
GetWaitHandles(workItemResults, waitHandles); GetWaitHandles(waitableResults, waitHandles);
if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) if ((null == cancelWaitHandle) && (waitHandles.Length <= 64))
{ {
success = WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
} }
else else
{ {
success = true; success = true;
int millisecondsLeft = millisecondsTimeout; int millisecondsLeft = millisecondsTimeout;
DateTime start = DateTime.Now; Stopwatch stopwatch = Stopwatch.StartNew();
WaitHandle[] whs; WaitHandle[] whs;
if (null != cancelWaitHandle) if (null != cancelWaitHandle)
@ -450,7 +517,7 @@ namespace Amib.Threading.Internal
// We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle
// won't affect it. // won't affect it.
// Each iteration we update the time left for the timeout. // Each iteration we update the time left for the timeout.
for(int i = 0; i < workItemResults.Length; ++i) for (int i = 0; i < waitableResults.Length; ++i)
{ {
// WaitAny don't work with negative numbers // WaitAny don't work with negative numbers
if (!waitInfinitely && (millisecondsLeft < 0)) if (!waitInfinitely && (millisecondsLeft < 0))
@ -460,8 +527,8 @@ namespace Amib.Threading.Internal
} }
whs[0] = waitHandles[i]; whs[0] = waitHandles[i];
int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext); int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
if((result > 0) || (WaitHandle.WaitTimeout == result)) if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result))
{ {
success = false; success = false;
break; break;
@ -470,13 +537,12 @@ namespace Amib.Threading.Internal
if (!waitInfinitely) if (!waitInfinitely)
{ {
// Update the time left to wait // Update the time left to wait
TimeSpan ts = DateTime.Now - start; millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
} }
} }
} }
// Release the wait handles // Release the wait handles
ReleaseWaitHandles(workItemResults); ReleaseWaitHandles(waitableResults);
return success; return success;
} }
@ -484,7 +550,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout /// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </summary> /// </summary>
/// <param name="workItemResults">Array of work item result objects</param> /// <param name="waitableResults">Array of work item result objects</param>
/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
/// <param name="exitContext"> /// <param name="exitContext">
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@ -494,37 +560,37 @@ namespace Amib.Threading.Internal
/// 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. /// 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.
/// </returns> /// </returns>
internal static int WaitAny( internal static int WaitAny(
IWorkItemResult [] workItemResults, IWaitableResult[] waitableResults,
int millisecondsTimeout, int millisecondsTimeout,
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle) WaitHandle cancelWaitHandle)
{ {
WaitHandle [] waitHandles = null; WaitHandle[] waitHandles;
if (null != cancelWaitHandle) if (null != cancelWaitHandle)
{ {
waitHandles = new WaitHandle[workItemResults.Length+1]; waitHandles = new WaitHandle[waitableResults.Length + 1];
GetWaitHandles(workItemResults, waitHandles); GetWaitHandles(waitableResults, waitHandles);
waitHandles[workItemResults.Length] = cancelWaitHandle; waitHandles[waitableResults.Length] = cancelWaitHandle;
} }
else else
{ {
waitHandles = new WaitHandle[workItemResults.Length]; waitHandles = new WaitHandle[waitableResults.Length];
GetWaitHandles(workItemResults, waitHandles); GetWaitHandles(waitableResults, waitHandles);
} }
int result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
// Treat cancel as timeout // Treat cancel as timeout
if (null != cancelWaitHandle) if (null != cancelWaitHandle)
{ {
if (result == workItemResults.Length) if (result == waitableResults.Length)
{ {
result = WaitHandle.WaitTimeout; result = STPEventWaitHandle.WaitTimeout;
} }
} }
ReleaseWaitHandles(workItemResults); ReleaseWaitHandles(waitableResults);
return result; return result;
} }
@ -532,16 +598,16 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Fill an array of wait handles with the work items wait handles. /// Fill an array of wait handles with the work items wait handles.
/// </summary> /// </summary>
/// <param name="workItemResults">An array of work item results</param> /// <param name="waitableResults">An array of work item results</param>
/// <param name="waitHandles">An array of wait handles to fill</param> /// <param name="waitHandles">An array of wait handles to fill</param>
private static void GetWaitHandles( private static void GetWaitHandles(
IWorkItemResult [] workItemResults, IWaitableResult[] waitableResults,
WaitHandle[] waitHandles) WaitHandle[] waitHandles)
{ {
for(int i = 0; i < workItemResults.Length; ++i) for (int i = 0; i < waitableResults.Length; ++i)
{ {
WorkItemResult wir = workItemResults[i] as WorkItemResult; WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult;
Debug.Assert(null != wir, "All workItemResults must be WorkItemResult objects"); Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects");
waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); waitHandles[i] = wir.GetWorkItem().GetWaitHandle();
} }
@ -550,31 +616,52 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Release the work items' wait handles /// Release the work items' wait handles
/// </summary> /// </summary>
/// <param name="workItemResults">An array of work item results</param> /// <param name="waitableResults">An array of work item results</param>
private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults) private static void ReleaseWaitHandles(IWaitableResult[] waitableResults)
{ {
for(int i = 0; i < workItemResults.Length; ++i) for (int i = 0; i < waitableResults.Length; ++i)
{ {
WorkItemResult wir = workItemResults[i] as WorkItemResult; WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult();
wir.GetWorkItem().ReleaseWaitHandle(); wir.GetWorkItem().ReleaseWaitHandle();
} }
} }
#endregion #endregion
#region Private Members #region Private Members
private WorkItemState GetWorkItemState() private WorkItemState GetWorkItemState()
{ {
if (_canceledWorkItemsGroup.IsCanceled) lock (this)
{
if (WorkItemState.Completed == _workItemState)
{
return _workItemState;
}
long nowTicks = DateTime.UtcNow.Ticks;
if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime)
{
_workItemState = WorkItemState.Canceled;
}
if (WorkItemState.InProgress == _workItemState)
{
return _workItemState;
}
if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled)
{ {
return WorkItemState.Canceled; return WorkItemState.Canceled;
} }
return _workItemState;
return _workItemState;
} }
}
/// <summary> /// <summary>
/// Sets the work item's state /// Sets the work item's state
/// </summary> /// </summary>
@ -582,10 +669,13 @@ namespace Amib.Threading.Internal
private void SetWorkItemState(WorkItemState workItemState) private void SetWorkItemState(WorkItemState workItemState)
{ {
lock (this) lock (this)
{
if (IsValidStatesTransition(_workItemState, workItemState))
{ {
_workItemState = workItemState; _workItemState = workItemState;
} }
} }
}
/// <summary> /// <summary>
/// Signals that work item has been completed or canceled /// Signals that work item has been completed or canceled
@ -606,7 +696,7 @@ namespace Amib.Threading.Internal
internal void WorkItemIsQueued() internal void WorkItemIsQueued()
{ {
_queuedTime = DateTime.Now; _waitingOnQueueStopwatch.Start();
} }
#endregion #endregion
@ -617,29 +707,82 @@ namespace Amib.Threading.Internal
/// Cancel the work item if it didn't start running yet. /// Cancel the work item if it didn't start running yet.
/// </summary> /// </summary>
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns> /// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
private bool Cancel() private bool Cancel(bool abortExecution)
{ {
#if (_WINDOWS_CE)
if(abortExecution)
{
throw new ArgumentOutOfRangeException("abortExecution", "WindowsCE doesn't support this feature");
}
#endif
bool success = false;
bool signalComplete = false;
lock (this) lock (this)
{ {
switch (GetWorkItemState()) switch (GetWorkItemState())
{ {
case WorkItemState.Canceled: case WorkItemState.Canceled:
//Debug.WriteLine("Work item already canceled"); //Debug.WriteLine("Work item already canceled");
return true; if (abortExecution)
{
Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread);
if (null != executionThread)
{
executionThread.Abort(); // "Cancel"
// No need to signalComplete, because we already cancelled this work item
// so it already signaled its completion.
//signalComplete = true;
}
}
success = true;
break;
case WorkItemState.Completed: case WorkItemState.Completed:
case WorkItemState.InProgress:
//Debug.WriteLine("Work item cannot be canceled"); //Debug.WriteLine("Work item cannot be canceled");
return false; break;
case WorkItemState.InProgress:
if (abortExecution)
{
Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread);
if (null != executionThread)
{
executionThread.Abort(); // "Cancel"
success = true;
signalComplete = true;
}
}
else
{
// **************************
// Stock SmartThreadPool 2.2.3 sets these to true and relies on the thread to check the
// WorkItem cancellation status. However, OpenSimulator uses a different mechanism to notify
// scripts of co-operative termination and the abort code also relies on this method
// returning false in order to implement a small wait.
//
// Therefore, as was the case previously with STP, we will not signal successful cancellation
// here. It's possible that OpenSimulator code could be changed in the future to remove
// the need for this change.
// **************************
success = false;
signalComplete = false;
}
break;
case WorkItemState.InQueue: case WorkItemState.InQueue:
// Signal to the wait for completion that the work // Signal to the wait for completion that the work
// item has been completed (canceled). There is no // item has been completed (canceled). There is no
// reason to wait for it to get out of the queue // reason to wait for it to get out of the queue
SignalComplete(true); signalComplete = true;
//Debug.WriteLine("Work item canceled"); //Debug.WriteLine("Work item canceled");
return true; success = true;
break;
}
if (signalComplete)
{
SignalComplete(true);
} }
} }
return false; return success;
} }
/// <summary> /// <summary>
@ -653,7 +796,7 @@ namespace Amib.Threading.Internal
bool exitContext, bool exitContext,
WaitHandle cancelWaitHandle) WaitHandle cancelWaitHandle)
{ {
Exception e = null; Exception e;
object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
if (null != e) if (null != e)
{ {
@ -694,7 +837,7 @@ namespace Amib.Threading.Internal
{ {
WaitHandle wh = GetWaitHandle(); WaitHandle wh = GetWaitHandle();
bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext); bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext);
ReleaseWaitHandle(); ReleaseWaitHandle();
@ -706,7 +849,7 @@ namespace Amib.Threading.Internal
else else
{ {
WaitHandle wh = GetWaitHandle(); WaitHandle wh = GetWaitHandle();
int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
ReleaseWaitHandle(); ReleaseWaitHandle();
switch (result) switch (result)
@ -717,7 +860,7 @@ namespace Amib.Threading.Internal
// work item (not the get result) // work item (not the get result)
break; break;
case 1: case 1:
case WaitHandle.WaitTimeout: case STPEventWaitHandle.WaitTimeout:
throw new WorkItemTimeoutException("Work item timeout"); throw new WorkItemTimeoutException("Work item timeout");
default: default:
Debug.Assert(false); Debug.Assert(false);
@ -749,7 +892,7 @@ namespace Amib.Threading.Internal
{ {
if (null == _workItemCompleted) if (null == _workItemCompleted)
{ {
_workItemCompleted = new ManualResetEvent(IsCompleted); _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted);
} }
++_workItemCompletedRefCount; ++_workItemCompletedRefCount;
} }
@ -843,172 +986,6 @@ namespace Amib.Threading.Internal
} }
} }
#region WorkItemResult class
private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult
{
/// <summary>
/// A back reference to the work item
/// </summary>
private WorkItem _workItem;
public WorkItemResult(WorkItem workItem)
{
_workItem = workItem;
}
internal WorkItem GetWorkItem()
{
return _workItem;
}
#region IWorkItemResult Members
public bool IsCompleted
{
get
{
return _workItem.IsCompleted;
}
}
public void Abort()
{
_workItem.Abort();
}
public bool IsCanceled
{
get
{
return _workItem.IsCanceled;
}
}
public object GetResult()
{
return _workItem.GetResult(Timeout.Infinite, true, null);
}
public object GetResult(int millisecondsTimeout, bool exitContext)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, null);
}
public object GetResult(TimeSpan timeout, bool exitContext)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null);
}
public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
}
public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
}
public object GetResult(out Exception e)
{
return _workItem.GetResult(Timeout.Infinite, true, null, out e);
}
public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e);
}
public object GetResult(TimeSpan timeout, bool exitContext, out Exception e)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e);
}
public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
{
return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
}
public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
{
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e);
}
public bool Cancel()
{
return _workItem.Cancel();
}
public object State
{
get
{
return _workItem._state;
}
}
public WorkItemPriority WorkItemPriority
{
get
{
return _workItem._workItemInfo.WorkItemPriority;
}
}
/// <summary>
/// Return the result, same as GetResult()
/// </summary>
public object Result
{
get { return GetResult(); }
}
/// <summary>
/// Returns the exception if occured otherwise returns null.
/// This value is valid only after the work item completed,
/// before that it is always null.
/// </summary>
public object Exception
{
get { return _workItem._exception; }
}
#endregion
#region IInternalWorkItemResult Members
public event WorkItemStateCallback OnWorkItemStarted
{
add
{
_workItem.OnWorkItemStarted += value;
}
remove
{
_workItem.OnWorkItemStarted -= value;
}
}
public event WorkItemStateCallback OnWorkItemCompleted
{
add
{
_workItem.OnWorkItemCompleted += value;
}
remove
{
_workItem.OnWorkItemCompleted -= value;
}
}
#endregion
}
#endregion
public void DisposeOfState() public void DisposeOfState()
{ {
if (_workItemInfo.DisposeOfStateObjects) if (_workItemInfo.DisposeOfStateObjects)
@ -1021,15 +998,5 @@ namespace Amib.Threading.Internal
} }
} }
} }
public void Abort()
{
lock (this)
{
if(currentThread != null)
currentThread.Abort();
} }
} }
}
#endregion
}

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
@ -12,6 +9,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item</returns> /// <returns>Returns a work item</returns>
@ -26,6 +24,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="workItemPriority">The priority of the work item</param> /// <param name="workItemPriority">The priority of the work item</param>
@ -42,6 +41,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="workItemInfo">Work item info</param> /// <param name="workItemInfo">Work item info</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
@ -63,6 +63,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -83,6 +84,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem( WorkItem workItem = new WorkItem(
workItemsGroup, workItemsGroup,
@ -95,6 +97,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -131,6 +134,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="workItemInfo">Work item information</param> /// <param name="workItemInfo">Work item information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
@ -160,6 +164,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -185,6 +190,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem( WorkItem workItem = new WorkItem(
workItemsGroup, workItemsGroup,
@ -198,6 +204,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -239,6 +246,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -266,6 +274,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = callToPostExecute; workItemInfo.CallToPostExecute = callToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem( WorkItem workItem = new WorkItem(
workItemsGroup, workItemsGroup,
@ -279,6 +288,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Create a new work item /// Create a new work item
/// </summary> /// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param> /// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param> /// <param name="callback">A callback to execute</param>
/// <param name="state"> /// <param name="state">
@ -322,7 +332,7 @@ namespace Amib.Threading.Internal
private static void ValidateCallback(Delegate callback) private static void ValidateCallback(Delegate callback)
{ {
if(callback.GetInvocationList().Length > 1) if (callback != null && callback.GetInvocationList().Length > 1)
{ {
throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
} }

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
namespace Amib.Threading namespace Amib.Threading
{ {
#region WorkItemInfo class #region WorkItemInfo class
@ -10,92 +7,62 @@ namespace Amib.Threading
/// </summary> /// </summary>
public class WorkItemInfo public class WorkItemInfo
{ {
/// <summary>
/// Use the caller's security context
/// </summary>
private bool _useCallerCallContext;
/// <summary>
/// Use the caller's security context
/// </summary>
private bool _useCallerHttpContext;
/// <summary>
/// Dispose of the state object of a work item
/// </summary>
private bool _disposeOfStateObjects;
/// <summary>
/// The option to run the post execute
/// </summary>
private CallToPostExecute _callToPostExecute;
/// <summary>
/// A post execute callback to call when none is provided in
/// the QueueWorkItem method.
/// </summary>
private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
/// <summary>
/// The priority of the work item
/// </summary>
private WorkItemPriority _workItemPriority;
public WorkItemInfo() public WorkItemInfo()
{ {
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_workItemPriority = SmartThreadPool.DefaultWorkItemPriority; WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
} }
public WorkItemInfo(WorkItemInfo workItemInfo) public WorkItemInfo(WorkItemInfo workItemInfo)
{ {
_useCallerCallContext = workItemInfo._useCallerCallContext; UseCallerCallContext = workItemInfo.UseCallerCallContext;
_useCallerHttpContext = workItemInfo._useCallerHttpContext; UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
_disposeOfStateObjects = workItemInfo._disposeOfStateObjects; DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
_callToPostExecute = workItemInfo._callToPostExecute; CallToPostExecute = workItemInfo.CallToPostExecute;
_postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback; PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
_workItemPriority = workItemInfo._workItemPriority; WorkItemPriority = workItemInfo.WorkItemPriority;
Timeout = workItemInfo.Timeout;
} }
public bool UseCallerCallContext /// <summary>
{ /// Get/Set if to use the caller's security context
get { return _useCallerCallContext; } /// </summary>
set { _useCallerCallContext = value; } public bool UseCallerCallContext { get; set; }
}
public bool UseCallerHttpContext /// <summary>
{ /// Get/Set if to use the caller's HTTP context
get { return _useCallerHttpContext; } /// </summary>
set { _useCallerHttpContext = value; } public bool UseCallerHttpContext { get; set; }
}
public bool DisposeOfStateObjects /// <summary>
{ /// Get/Set if to dispose of the state object of a work item
get { return _disposeOfStateObjects; } /// </summary>
set { _disposeOfStateObjects = value; } public bool DisposeOfStateObjects { get; set; }
}
public CallToPostExecute CallToPostExecute /// <summary>
{ /// Get/Set the run the post execute options
get { return _callToPostExecute; } /// </summary>
set { _callToPostExecute = value; } public CallToPostExecute CallToPostExecute { get; set; }
}
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback /// <summary>
{ /// Get/Set the post execute callback
get { return _postExecuteWorkItemCallback; } /// </summary>
set { _postExecuteWorkItemCallback = value; } public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }
}
public WorkItemPriority WorkItemPriority /// <summary>
{ /// Get/Set the work item's priority
get { return _workItemPriority; } /// </summary>
set { _workItemPriority = value; } public WorkItemPriority WorkItemPriority { get; set; }
}
/// <summary>
/// Get/Set the work item's timout in milliseconds.
/// This is a passive timout. When the timout expires the work item won't be actively aborted!
/// </summary>
public long Timeout { get; set; }
} }
#endregion #endregion

View File

@ -0,0 +1,128 @@
using System;
using System.Threading;
namespace Amib.Threading.Internal
{
#region WorkItemResultTWrapper class
internal class WorkItemResultTWrapper<TResult> : IWorkItemResult<TResult>, IInternalWaitableResult
{
private readonly IWorkItemResult _workItemResult;
public WorkItemResultTWrapper(IWorkItemResult workItemResult)
{
_workItemResult = workItemResult;
}
#region IWorkItemResult<TResult> Members
public TResult GetResult()
{
return (TResult)_workItemResult.GetResult();
}
public TResult GetResult(int millisecondsTimeout, bool exitContext)
{
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext);
}
public TResult GetResult(TimeSpan timeout, bool exitContext)
{
return (TResult)_workItemResult.GetResult(timeout, exitContext);
}
public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
{
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
}
public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
{
return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle);
}
public TResult GetResult(out Exception e)
{
return (TResult)_workItemResult.GetResult(out e);
}
public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
{
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e);
}
public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e)
{
return (TResult)_workItemResult.GetResult(timeout, exitContext, out e);
}
public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
{
return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
}
public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
{
return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e);
}
public bool IsCompleted
{
get { return _workItemResult.IsCompleted; }
}
public bool IsCanceled
{
get { return _workItemResult.IsCanceled; }
}
public object State
{
get { return _workItemResult.State; }
}
public bool Cancel()
{
return _workItemResult.Cancel();
}
public bool Cancel(bool abortExecution)
{
return _workItemResult.Cancel(abortExecution);
}
public WorkItemPriority WorkItemPriority
{
get { return _workItemResult.WorkItemPriority; }
}
public TResult Result
{
get { return (TResult)_workItemResult.Result; }
}
public object Exception
{
get { return (TResult)_workItemResult.Exception; }
}
#region IInternalWorkItemResult Members
public IWorkItemResult GetWorkItemResult()
{
return _workItemResult.GetWorkItemResult();
}
public IWorkItemResult<TRes> GetWorkItemResultT<TRes>()
{
return (IWorkItemResult<TRes>)this;
}
#endregion
#endregion
}
#endregion
}

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Threading; using System.Threading;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -8,33 +5,34 @@ using System.Diagnostics;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
{ {
#region WorkItemsGroup class #region WorkItemsGroup class
/// <summary> /// <summary>
/// Summary description for WorkItemsGroup. /// Summary description for WorkItemsGroup.
/// </summary> /// </summary>
public class WorkItemsGroup : IWorkItemsGroup public class WorkItemsGroup : WorkItemsGroupBase
{ {
#region Private members #region Private members
private object _lock = new object(); private readonly object _lock = new object();
/// <summary>
/// Contains the name of this instance of SmartThreadPool.
/// Can be changed by the user.
/// </summary>
private string _name = "WorkItemsGroup";
/// <summary> /// <summary>
/// A reference to the SmartThreadPool instance that created this /// A reference to the SmartThreadPool instance that created this
/// WorkItemsGroup. /// WorkItemsGroup.
/// </summary> /// </summary>
private SmartThreadPool _stp; private readonly SmartThreadPool _stp;
/// <summary> /// <summary>
/// The OnIdle event /// The OnIdle event
/// </summary> /// </summary>
private event WorkItemsGroupIdleHandler _onIdle; private event WorkItemsGroupIdleHandler _onIdle;
/// <summary>
/// A flag to indicate if the Work Items Group is now suspended.
/// </summary>
private bool _isSuspended;
/// <summary> /// <summary>
/// Defines how many work items of this WorkItemsGroup can run at once. /// Defines how many work items of this WorkItemsGroup can run at once.
/// </summary> /// </summary>
@ -44,7 +42,7 @@ namespace Amib.Threading.Internal
/// Priority queue to hold work items before they are passed /// Priority queue to hold work items before they are passed
/// to the SmartThreadPool. /// to the SmartThreadPool.
/// </summary> /// </summary>
private PriorityQueue _workItemsQueue; private readonly PriorityQueue _workItemsQueue;
/// <summary> /// <summary>
/// Indicate how many work items are waiting in the SmartThreadPool /// Indicate how many work items are waiting in the SmartThreadPool
@ -63,12 +61,13 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// WorkItemsGroup start information /// WorkItemsGroup start information
/// </summary> /// </summary>
private WIGStartInfo _workItemsGroupStartInfo; private readonly WIGStartInfo _workItemsGroupStartInfo;
/// <summary> /// <summary>
/// Signaled when all of the WorkItemsGroup's work item completed. /// Signaled when all of the WorkItemsGroup's work item completed.
/// </summary> /// </summary>
private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
/// <summary> /// <summary>
/// A common object for all the work items that this work items group /// A common object for all the work items that this work items group
@ -87,261 +86,90 @@ namespace Amib.Threading.Internal
{ {
if (concurrency <= 0) if (concurrency <= 0)
{ {
throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero"); throw new ArgumentOutOfRangeException(
"concurrency",
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
concurrency,
#endif
"concurrency must be greater than zero");
} }
_stp = stp; _stp = stp;
_concurrency = concurrency; _concurrency = concurrency;
_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo); _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly();
_workItemsQueue = new PriorityQueue(); _workItemsQueue = new PriorityQueue();
Name = "WorkItemsGroup";
// The _workItemsInStpQueue gets the number of currently executing work items, // The _workItemsInStpQueue gets the number of currently executing work items,
// because once a work item is executing, it cannot be cancelled. // because once a work item is executing, it cannot be cancelled.
_workItemsInStpQueue = _workItemsExecutingInStp; _workItemsInStpQueue = _workItemsExecutingInStp;
_isSuspended = _workItemsGroupStartInfo.StartSuspended;
} }
#endregion #endregion
#region IWorkItemsGroup implementation #region WorkItemsGroupBase Overrides
/// <summary> public override int Concurrency
/// Get/Set the name of the SmartThreadPool instance
/// </summary>
public string Name
{ {
get get { return _concurrency; }
{
return _name;
}
set set
{ {
_name = value; Debug.Assert(value > 0);
int diff = value - _concurrency;
_concurrency = value;
if (diff > 0)
{
EnqueueToSTPNextNWorkItem(diff);
}
}
}
public override int WaitingCallbacks
{
get { return _workItemsQueue.Count; }
}
public override object[] GetStates()
{
lock (_lock)
{
object[] states = new object[_workItemsQueue.Count];
int i = 0;
foreach (WorkItem workItem in _workItemsQueue)
{
states[i] = workItem.GetWorkItemResult().State;
++i;
}
return states;
} }
} }
/// <summary> /// <summary>
/// Queue a work item /// WorkItemsGroup start information
/// </summary> /// </summary>
/// <param name="callback">A callback to execute</param> public override WIGStartInfo WIGStartInfo
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
{ {
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback); get { return _workItemsGroupStartInfo; }
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
} }
/// <summary> /// <summary>
/// Queue a work item /// Start the Work Items Group if it was started suspended
/// </summary> /// </summary>
/// <param name="callback">A callback to execute</param> public override void Start()
/// <param name="workItemPriority">The priority of the work item</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
{ {
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority); // If the Work Items Group already started then quit
EnqueueToSTPNextWorkItem(workItem); if (!_isSuspended)
return workItem.GetWorkItemResult(); {
return;
}
_isSuspended = false;
EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
} }
/// <summary> public override void Cancel(bool abortExecution)
/// Queue a work item
/// </summary>
/// <param name="workItemInfo">Work item info</param>
/// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="workItemInfo">Work item information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
WorkItemPriority workItemPriority)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
CallToPostExecute callToPostExecute)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
CallToPostExecute callToPostExecute,
WorkItemPriority workItemPriority)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Wait for the thread pool to be idle
/// </summary>
public void WaitForIdle()
{
WaitForIdle(Timeout.Infinite);
}
/// <summary>
/// Wait for the thread pool to be idle
/// </summary>
public bool WaitForIdle(TimeSpan timeout)
{
return WaitForIdle((int)timeout.TotalMilliseconds);
}
/// <summary>
/// Wait for the thread pool to be idle
/// </summary>
public bool WaitForIdle(int millisecondsTimeout)
{
_stp.ValidateWorkItemsGroupWaitForIdle(this);
return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
}
public int WaitingCallbacks
{
get
{
return _workItemsQueue.Count;
}
}
public event WorkItemsGroupIdleHandler OnIdle
{
add
{
_onIdle += value;
}
remove
{
_onIdle -= value;
}
}
public void Cancel()
{ {
lock (_lock) lock (_lock)
{ {
@ -350,23 +178,26 @@ namespace Amib.Threading.Internal
_workItemsInStpQueue = 0; _workItemsInStpQueue = 0;
_canceledWorkItemsGroup = new CanceledWorkItemsGroup(); _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
} }
if (abortExecution)
{
_stp.CancelAbortWorkItemsGroup(this);
}
} }
public void Start() /// <summary>
/// Wait for the thread pool to be idle
/// </summary>
public override bool WaitForIdle(int millisecondsTimeout)
{ {
lock (this) SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
{ return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
if (!_workItemsGroupStartInfo.StartSuspended)
{
return;
}
_workItemsGroupStartInfo.StartSuspended = false;
} }
for(int i = 0; i < _concurrency; ++i) public override event WorkItemsGroupIdleHandler OnIdle
{ {
EnqueueToSTPNextWorkItem(null, false); add { _onIdle += value; }
} remove { _onIdle -= value; }
} }
#endregion #endregion
@ -375,22 +206,24 @@ namespace Amib.Threading.Internal
private void RegisterToWorkItemCompletion(IWorkItemResult wir) private void RegisterToWorkItemCompletion(IWorkItemResult wir)
{ {
IInternalWorkItemResult iwir = wir as IInternalWorkItemResult; IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
iwir.OnWorkItemStarted += new WorkItemStateCallback(OnWorkItemStartedCallback); iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
iwir.OnWorkItemCompleted += new WorkItemStateCallback(OnWorkItemCompletedCallback); iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
} }
public void OnSTPIsStarting() public void OnSTPIsStarting()
{ {
lock (this) if (_isSuspended)
{
if (_workItemsGroupStartInfo.StartSuspended)
{ {
return; return;
} }
EnqueueToSTPNextNWorkItem(_concurrency);
} }
for(int i = 0; i < _concurrency; ++i) public void EnqueueToSTPNextNWorkItem(int count)
{
for (int i = 0; i < count; ++i)
{ {
EnqueueToSTPNextWorkItem(null, false); EnqueueToSTPNextWorkItem(null, false);
} }
@ -417,8 +250,7 @@ namespace Amib.Threading.Internal
{ {
eh(this); eh(this);
} }
// Ignore exceptions catch { } // Suppress exceptions
catch{}
} }
} }
@ -435,6 +267,11 @@ namespace Amib.Threading.Internal
EnqueueToSTPNextWorkItem(null, true); EnqueueToSTPNextWorkItem(null, true);
} }
internal override void Enqueue(WorkItem workItem)
{
EnqueueToSTPNextWorkItem(workItem);
}
private void EnqueueToSTPNextWorkItem(WorkItem workItem) private void EnqueueToSTPNextWorkItem(WorkItem workItem)
{ {
EnqueueToSTPNextWorkItem(workItem, false); EnqueueToSTPNextWorkItem(workItem, false);
@ -475,7 +312,7 @@ namespace Amib.Threading.Internal
(0 == _workItemsInStpQueue)) (0 == _workItemsInStpQueue))
{ {
_stp.RegisterWorkItemsGroup(this); _stp.RegisterWorkItemsGroup(this);
Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle"); IsIdle = false;
_isIdleWaitHandle.Reset(); _isIdleWaitHandle.Reset();
} }
} }
@ -486,19 +323,31 @@ namespace Amib.Threading.Internal
if (0 == _workItemsInStpQueue) if (0 == _workItemsInStpQueue)
{ {
_stp.UnregisterWorkItemsGroup(this); _stp.UnregisterWorkItemsGroup(this);
Trace.WriteLine("WorkItemsGroup " + Name + " is idle"); IsIdle = true;
_isIdleWaitHandle.Set(); _isIdleWaitHandle.Set();
_stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle)); if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
{
_stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
}
} }
return; return;
} }
if (!_workItemsGroupStartInfo.StartSuspended) if (!_isSuspended)
{ {
if (_workItemsInStpQueue < _concurrency) if (_workItemsInStpQueue < _concurrency)
{ {
WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
_stp.Enqueue(nextWorkItem, true); try
{
_stp.Enqueue(nextWorkItem);
}
catch (ObjectDisposedException e)
{
e.GetHashCode();
// The STP has been shutdown
}
++_workItemsInStpQueue; ++_workItemsInStpQueue;
} }
} }

View File

@ -0,0 +1,471 @@
using System;
using System.Threading;
namespace Amib.Threading.Internal
{
public abstract class WorkItemsGroupBase : IWorkItemsGroup
{
#region Private Fields
/// <summary>
/// Contains the name of this instance of SmartThreadPool.
/// Can be changed by the user.
/// </summary>
private string _name = "WorkItemsGroupBase";
public WorkItemsGroupBase()
{
IsIdle = true;
}
#endregion
#region IWorkItemsGroup Members
#region Public Methods
/// <summary>
/// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance
/// </summary>
public string Name
{
get { return _name; }
set { _name = value; }
}
#endregion
#region Abstract Methods
public abstract int Concurrency { get; set; }
public abstract int WaitingCallbacks { get; }
public abstract object[] GetStates();
public abstract WIGStartInfo WIGStartInfo { get; }
public abstract void Start();
public abstract void Cancel(bool abortExecution);
public abstract bool WaitForIdle(int millisecondsTimeout);
public abstract event WorkItemsGroupIdleHandler OnIdle;
internal abstract void Enqueue(WorkItem workItem);
internal virtual void PreQueueWorkItem() { }
#endregion
#region Common Base Methods
/// <summary>
/// Cancel all the work items.
/// Same as Cancel(false)
/// </summary>
public virtual void Cancel()
{
Cancel(false);
}
/// <summary>
/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
/// </summary>
public void WaitForIdle()
{
WaitForIdle(Timeout.Infinite);
}
/// <summary>
/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
/// </summary>
public bool WaitForIdle(TimeSpan timeout)
{
return WaitForIdle((int)timeout.TotalMilliseconds);
}
/// <summary>
/// IsIdle is true when there are no work items running or queued.
/// </summary>
public bool IsIdle { get; protected set; }
#endregion
#region QueueWorkItem
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="workItemPriority">The priority of the work item</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="workItemInfo">Work item info</param>
/// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="workItemInfo">Work item information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
WorkItemPriority workItemPriority)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
CallToPostExecute callToPostExecute)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
/// <summary>
/// Queue a work item
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
/// The context object of the work item. Used for passing arguments to the work item.
/// </param>
/// <param name="postExecuteWorkItemCallback">
/// A delegate to call after the callback completion
/// </param>
/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
/// <param name="workItemPriority">The work item priority</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(
WorkItemCallback callback,
object state,
PostExecuteWorkItemCallback postExecuteWorkItemCallback,
CallToPostExecute callToPostExecute,
WorkItemPriority workItemPriority)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
Enqueue(workItem);
return workItem.GetWorkItemResult();
}
#endregion
#region QueueWorkItem(Action<...>)
public IWorkItemResult QueueWorkItem(Action action)
{
return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority);
}
public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority)
{
PreQueueWorkItem ();
WorkItem workItem = WorkItemFactory.CreateWorkItem (
this,
WIGStartInfo,
delegate
{
action.Invoke ();
return null;
}, priority);
Enqueue (workItem);
return workItem.GetWorkItemResult ();
}
public IWorkItemResult QueueWorkItem<T>(Action<T> action, T arg)
{
return QueueWorkItem<T> (action, arg, SmartThreadPool.DefaultWorkItemPriority);
}
public IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority)
{
PreQueueWorkItem ();
WorkItem workItem = WorkItemFactory.CreateWorkItem (
this,
WIGStartInfo,
state =>
{
action.Invoke (arg);
return null;
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority);
Enqueue (workItem);
return workItem.GetWorkItemResult ();
}
public IWorkItemResult QueueWorkItem<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
{
return QueueWorkItem<T1, T2> (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority);
}
public IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority)
{
PreQueueWorkItem ();
WorkItem workItem = WorkItemFactory.CreateWorkItem (
this,
WIGStartInfo,
state =>
{
action.Invoke (arg1, arg2);
return null;
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority);
Enqueue (workItem);
return workItem.GetWorkItemResult ();
}
public IWorkItemResult QueueWorkItem<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
{
return QueueWorkItem<T1, T2, T3> (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority);
;
}
public IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority)
{
PreQueueWorkItem ();
WorkItem workItem = WorkItemFactory.CreateWorkItem (
this,
WIGStartInfo,
state =>
{
action.Invoke (arg1, arg2, arg3);
return null;
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority);
Enqueue (workItem);
return workItem.GetWorkItemResult ();
}
public IWorkItemResult QueueWorkItem<T1, T2, T3, T4>(
Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
return QueueWorkItem<T1, T2, T3, T4> (action, arg1, arg2, arg3, arg4,
SmartThreadPool.DefaultWorkItemPriority);
}
public IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (
Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority)
{
PreQueueWorkItem ();
WorkItem workItem = WorkItemFactory.CreateWorkItem (
this,
WIGStartInfo,
state =>
{
action.Invoke (arg1, arg2, arg3, arg4);
return null;
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority);
Enqueue (workItem);
return workItem.GetWorkItemResult ();
}
#endregion
#region QueueWorkItem(Func<...>)
public IWorkItemResult<TResult> QueueWorkItem<TResult>(Func<TResult> func)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(
this,
WIGStartInfo,
state =>
{
return func.Invoke();
});
Enqueue(workItem);
return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult());
}
public IWorkItemResult<TResult> QueueWorkItem<T, TResult>(Func<T, TResult> func, T arg)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(
this,
WIGStartInfo,
state =>
{
return func.Invoke(arg);
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null);
Enqueue(workItem);
return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult());
}
public IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 arg1, T2 arg2)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(
this,
WIGStartInfo,
state =>
{
return func.Invoke(arg1, arg2);
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null);
Enqueue(workItem);
return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult());
}
public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult>(
Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(
this,
WIGStartInfo,
state =>
{
return func.Invoke(arg1, arg2, arg3);
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null);
Enqueue(workItem);
return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult());
}
public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult>(
Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
{
PreQueueWorkItem();
WorkItem workItem = WorkItemFactory.CreateWorkItem(
this,
WIGStartInfo,
state =>
{
return func.Invoke(arg1, arg2, arg3, arg4);
},
WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null);
Enqueue(workItem);
return new WorkItemResultTWrapper<TResult>(workItem.GetWorkItemResult());
}
#endregion
#endregion
}
}

View File

@ -1,7 +1,5 @@
// Ami Bar
// amibar@gmail.com
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
namespace Amib.Threading.Internal namespace Amib.Threading.Internal
@ -18,7 +16,7 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Waiters queue (implemented as stack). /// Waiters queue (implemented as stack).
/// </summary> /// </summary>
private WaiterEntry _headWaiterEntry = new WaiterEntry(); private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();
/// <summary> /// <summary>
/// Waiters count /// Waiters count
@ -28,18 +26,70 @@ namespace Amib.Threading.Internal
/// <summary> /// <summary>
/// Work items queue /// Work items queue
/// </summary> /// </summary>
private PriorityQueue _workItems = new PriorityQueue(); private readonly PriorityQueue _workItems = new PriorityQueue();
/// <summary> /// <summary>
/// Indicate that work items are allowed to be queued /// Indicate that work items are allowed to be queued
/// </summary> /// </summary>
private bool _isWorkItemsQueueActive = true; private bool _isWorkItemsQueueActive = true;
#if (WINDOWS_PHONE)
private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>();
#elif (_WINDOWS_CE)
private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot();
#else
[ThreadStatic]
private static WaiterEntry _waiterEntry;
#endif
/// <summary> /// <summary>
/// Each thread in the thread pool keeps its own waiter entry. /// Each thread in the thread pool keeps its own waiter entry.
/// </summary> /// </summary>
[ThreadStatic] private static WaiterEntry CurrentWaiterEntry
private static WaiterEntry _waiterEntry; {
#if (WINDOWS_PHONE)
get
{
lock (_waiterEntries)
{
WaiterEntry waiterEntry;
if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry))
{
return waiterEntry;
}
}
return null;
}
set
{
lock (_waiterEntries)
{
_waiterEntries[Thread.CurrentThread.ManagedThreadId] = value;
}
}
#elif (_WINDOWS_CE)
get
{
return Thread.GetData(_waiterEntrySlot) as WaiterEntry;
}
set
{
Thread.SetData(_waiterEntrySlot, value);
}
#else
get
{
return _waiterEntry;
}
set
{
_waiterEntry = value;
}
#endif
}
/// <summary> /// <summary>
/// A flag that indicates if the WorkItemsQueue has been disposed. /// A flag that indicates if the WorkItemsQueue has been disposed.
@ -57,13 +107,9 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
lock(this)
{
ValidateNotDisposed();
return _workItems.Count; return _workItems.Count;
} }
} }
}
/// <summary> /// <summary>
/// Returns the current number of waiters /// Returns the current number of waiters
@ -72,13 +118,9 @@ namespace Amib.Threading.Internal
{ {
get get
{ {
lock(this)
{
ValidateNotDisposed();
return _waitersCount; return _waitersCount;
} }
} }
}
#endregion #endregion
@ -144,21 +186,20 @@ namespace Amib.Threading.Internal
int millisecondsTimeout, int millisecondsTimeout,
WaitHandle cancelEvent) WaitHandle cancelEvent)
{ {
/// This method cause the caller to wait for a work item. // This method cause the caller to wait for a work item.
/// If there is at least one waiting work item then the // If there is at least one waiting work item then the
/// method returns immidiately with true. // method returns immidiately with it.
/// //
/// If there are no waiting work items then the caller // If there are no waiting work items then the caller
/// is queued between other waiters for a work item to arrive. // is queued between other waiters for a work item to arrive.
/// //
/// If a work item didn't come within millisecondsTimeout or // If a work item didn't come within millisecondsTimeout or
/// the user canceled the wait by signaling the cancelEvent // the user canceled the wait by signaling the cancelEvent
/// then the method returns false to indicate that the caller // then the method returns null to indicate that the caller
/// didn't get a work item. // didn't get a work item.
WaiterEntry waiterEntry = null; WaiterEntry waiterEntry;
WorkItem workItem = null; WorkItem workItem = null;
lock (this) lock (this)
{ {
ValidateNotDisposed(); ValidateNotDisposed();
@ -169,16 +210,15 @@ namespace Amib.Threading.Internal
workItem = _workItems.Dequeue() as WorkItem; workItem = _workItems.Dequeue() as WorkItem;
return workItem; return workItem;
} }
// No waiting work items ... // No waiting work items ...
else
{ // Get the waiter entry for the waiters queue
// Get the wait entry for the waiters queue
waiterEntry = GetThreadWaiterEntry(); waiterEntry = GetThreadWaiterEntry();
// Put the waiter with the other waiters // Put the waiter with the other waiters
PushWaiter(waiterEntry); PushWaiter(waiterEntry);
} }
}
// Prepare array of wait handle for the WaitHandle.WaitAny() // Prepare array of wait handle for the WaitHandle.WaitAny()
WaitHandle [] waitHandles = new WaitHandle[] { WaitHandle [] waitHandles = new WaitHandle[] {
@ -189,10 +229,10 @@ namespace Amib.Threading.Internal
// During the wait we are supposes to exit the synchronization // During the wait we are supposes to exit the synchronization
// domain. (Placing true as the third argument of the WaitAny()) // domain. (Placing true as the third argument of the WaitAny())
// It just doesn't work, I don't know why, so I have lock(this) // It just doesn't work, I don't know why, so I have two lock(this)
// statments insted of one. // statments instead of one.
int index = WaitHandle.WaitAny( int index = STPEventWaitHandle.WaitAny(
waitHandles, waitHandles,
millisecondsTimeout, millisecondsTimeout,
true); true);
@ -242,7 +282,7 @@ namespace Amib.Threading.Internal
/// Cleanup the work items queue, hence no more work /// Cleanup the work items queue, hence no more work
/// items are allowed to be queue /// items are allowed to be queue
/// </summary> /// </summary>
protected virtual void Cleanup() private void Cleanup()
{ {
lock(this) lock(this)
{ {
@ -279,6 +319,21 @@ namespace Amib.Threading.Internal
} }
} }
public object[] GetStates()
{
lock (this)
{
object[] states = new object[_workItems.Count];
int i = 0;
foreach (WorkItem workItem in _workItems)
{
states[i] = workItem.GetWorkItemResult().State;
++i;
}
return states;
}
}
#endregion #endregion
#region Private methods #region Private methods
@ -289,14 +344,14 @@ namespace Amib.Threading.Internal
/// <returns></returns> /// <returns></returns>
/// In order to avoid creation and destuction of WaiterEntry /// In order to avoid creation and destuction of WaiterEntry
/// objects each thread has its own WaiterEntry object. /// objects each thread has its own WaiterEntry object.
private WaiterEntry GetThreadWaiterEntry() private static WaiterEntry GetThreadWaiterEntry()
{ {
if (null == _waiterEntry) if (null == CurrentWaiterEntry)
{ {
_waiterEntry = new WaiterEntry(); CurrentWaiterEntry = new WaiterEntry();
} }
_waiterEntry.Reset(); CurrentWaiterEntry.Reset();
return _waiterEntry; return CurrentWaiterEntry;
} }
#region Waiters stack methods #region Waiters stack methods
@ -418,14 +473,15 @@ namespace Amib.Threading.Internal
#region WaiterEntry class #region WaiterEntry class
// A waiter entry in the _waiters queue. // A waiter entry in the _waiters queue.
public class WaiterEntry : IDisposable public sealed class WaiterEntry : IDisposable
{ {
#region Member variables #region Member variables
/// <summary> /// <summary>
/// Event to signal the waiter that it got the work item. /// Event to signal the waiter that it got the work item.
/// </summary> /// </summary>
private AutoResetEvent _waitHandle = new AutoResetEvent(false); //private AutoResetEvent _waitHandle = new AutoResetEvent(false);
private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();
/// <summary> /// <summary>
/// Flag to know if this waiter already quited from the queue /// Flag to know if this waiter already quited from the queue
@ -471,13 +527,10 @@ namespace Amib.Threading.Internal
public WorkItem WorkItem public WorkItem WorkItem
{ {
get get
{
lock(this)
{ {
return _workItem; return _workItem;
} }
} }
}
/// <summary> /// <summary>
/// Signal the waiter that it got a work item. /// Signal the waiter that it got a work item.
@ -550,18 +603,16 @@ namespace Amib.Threading.Internal
public void Dispose() public void Dispose()
{ {
lock (this)
{
if (!_isDisposed) if (!_isDisposed)
{ {
Close(); Close();
}
_isDisposed = true; _isDisposed = true;
} }
} }
~WaiterEntry()
{
Dispose();
}
#endregion #endregion
} }
@ -574,14 +625,8 @@ namespace Amib.Threading.Internal
if (!_isDisposed) if (!_isDisposed)
{ {
Cleanup(); Cleanup();
}
_isDisposed = true; _isDisposed = true;
GC.SuppressFinalize(this);
}
}
~WorkItemsQueue()
{
Cleanup();
} }
private void ValidateNotDisposed() private void ValidateNotDisposed()

View File

@ -21,6 +21,9 @@
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>] ; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
; * ; *
[Startup] [Startup]
; Place to create a PID file
; If no path if specified then a PID file is not created.
; PIDFile = "/tmp/my.pid"
; Plugin Registry Location ; Plugin Registry Location
; Set path to directory for plugin registry. Information ; Set path to directory for plugin registry. Information

View File

@ -13,6 +13,9 @@
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>] ; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
; * ; *
[Startup] [Startup]
; Place to create a PID file
; If no path if specified then a PID file is not created.
; PIDFile = "/tmp/my.pid"
; Plugin Registry Location ; Plugin Registry Location
; Set path to directory for plugin registry. Information ; Set path to directory for plugin registry. Information
@ -21,7 +24,6 @@
; The Robust.exe process must have R/W access to the location ; The Robust.exe process must have R/W access to the location
RegistryLocation = "." RegistryLocation = "."
; Modular configurations ; Modular configurations
; Set path to directory for modular ini files... ; Set path to directory for modular ini files...
; The Robust.exe process must have R/W access to the location ; The Robust.exe process must have R/W access to the location