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.
/// </summary>
/// <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();
string protocol = "http://";
@ -175,6 +175,9 @@ namespace OpenSim.Framework.Capabilities
if (excludeSeed && "SEED" == capsName)
continue;
if (requestedCaps != null && !requestedCaps.Contains(capsName))
continue;
caps[capsName] = baseUrl + m_capsHandlers[capsName].Path;
}
}

View File

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

View File

@ -355,11 +355,22 @@ namespace OpenSim.Region.ClientStack.Linden
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
foreach (KeyValuePair<string, string> kvp in m_HostCapsObj.ExternalCapsHandlers)
{
if (!validCaps.Contains(kvp.Key))
continue;
caps[kvp.Key] = kvp.Value;
}
string result = LLSDHelpers.SerialiseLLSDReply(caps);

View File

@ -252,7 +252,7 @@ namespace OpenSim.Region.CoreModules.Framework
{
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());
caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery);

View File

@ -676,11 +676,12 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
}
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
// Objects folder.
folder = m_Scene.InventoryService.GetFolderForType(userID, AssetType.Object);
so.FromFolderID = UUID.Zero;
}
else
{
@ -696,7 +697,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
//
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);
if (f != null)

View File

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

View File

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

View File

@ -551,7 +551,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
/// <param name="instance"></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>(
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))
return;
@ -1599,7 +1599,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
startInfo.MaxWorkerThreads = maxThreads;
startInfo.MinWorkerThreads = minThreads;
startInfo.ThreadPriority = threadPriority;;
startInfo.StackSize = stackSize;
startInfo.MaxStackSize = stackSize;
startInfo.StartSuspended = true;
m_ThreadPool = new SmartThreadPool(startInfo);

View File

@ -52,16 +52,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine
return wr.Cancel();
}
public void Abort()
public bool Abort()
{
wr.Abort();
return wr.Cancel(true);
}
public bool Wait(int t)
{
// 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
// 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).
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.Diagnostics;
using System.Threading;
@ -6,7 +9,7 @@ using System.Web;
using System.Runtime.Remoting.Messaging;
namespace Amib.Threading
namespace Amib.Threading.Internal
{
#region CallerThreadContext class
@ -19,10 +22,10 @@ namespace Amib.Threading
#region Prepare reflection information
// Cached type information.
private static MethodInfo getLogicalCallContextMethodInfo =
private static readonly MethodInfo getLogicalCallContextMethodInfo =
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);
private static string HttpContextSlotName = GetHttpContextSlotName();
@ -32,8 +35,10 @@ namespace Amib.Threading
FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
if (fi != null)
{
return (string) fi.GetValue(null);
else // Use the default "HttpContext" slot name
}
return "HttpContext";
}
@ -41,8 +46,8 @@ namespace Amib.Threading
#region Private fields
private HttpContext _httpContext = null;
private LogicalCallContext _callContext = null;
private HttpContext _httpContext;
private LogicalCallContext _callContext;
#endregion
@ -122,102 +127,12 @@ namespace Amib.Threading
// Restore HttpContext
if (callerThreadContext._httpContext != null)
{
CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
HttpContext.Current = callerThreadContext._httpContext;
//CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
}
}
}
#endregion
}
/*
// 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
}
*/
#endif

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;
#if !(_WINDOWS_CE)
using System.Runtime.Serialization;
#endif
namespace Amib.Threading
{
#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>
/// Represents an exception in case IWorkItemResult.GetResult has been canceled
/// </summary>
[Serializable]
public sealed class WorkItemCancelException : ApplicationException
public sealed partial class WorkItemCancelException
{
public WorkItemCancelException() : base()
{
}
public WorkItemCancelException(string message) : base(message)
{
}
public WorkItemCancelException(string message, Exception e) : base(message, e)
{
}
public WorkItemCancelException(SerializationInfo si, StreamingContext sc) : base(si, sc)
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
/// </summary>
[Serializable]
public sealed class WorkItemTimeoutException : ApplicationException
public sealed partial class WorkItemTimeoutException
{
public WorkItemTimeoutException() : base()
{
}
public WorkItemTimeoutException(string message) : base(message)
{
}
public WorkItemTimeoutException(string message, Exception e) : base(message, e)
{
}
public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc)
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
/// </summary>
[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)
{
}
public WorkItemResultException(string message, Exception e) : base(message, e)
{
}
public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc)
{
}
}
#endif
#endregion
}

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
using System;
using System.Threading;
@ -20,16 +17,39 @@ namespace Amib.Threading
/// <param name="wir">The work item result object</param>
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>
/// A delegate to call when a WorkItemsGroup becomes idle
/// </summary>
/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
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
#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
{
Lowest,
@ -41,19 +61,11 @@ namespace Amib.Threading
#endregion
#region IHasWorkItemPriority interface
public interface IHasWorkItemPriority
{
WorkItemPriority WorkItemPriority { get; }
}
#endregion
#region IWorkItemsGroup interface
/// <summary>
/// IWorkItemsGroup interface
/// Created by SmartThreadPool.CreateWorkItemsGroup()
/// </summary>
public interface IWorkItemsGroup
{
@ -62,27 +74,293 @@ namespace Amib.Threading
/// </summary>
string Name { get; set; }
IWorkItemResult QueueWorkItem(WorkItemCallback callback);
IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
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);
/// <summary>
/// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
/// </summary>
int Concurrency { get; set; }
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
/// <summary>
/// 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();
/// <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);
/// <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);
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;
void Cancel();
void Start();
#region QueueWorkItem
/// <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
@ -92,9 +370,24 @@ namespace Amib.Threading
[Flags]
public enum CallToPostExecute
{
/// <summary>
/// Never call to the PostExecute call back
/// </summary>
Never = 0x00,
/// <summary>
/// Call to the PostExecute only when the work item is cancelled
/// </summary>
WhenWorkItemCanceled = 0x01,
/// <summary>
/// Call to the PostExecute only when the work item is not cancelled
/// </summary>
WhenWorkItemNotCanceled = 0x02,
/// <summary>
/// Always call to the PostExecute
/// </summary>
Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
}
@ -103,16 +396,43 @@ namespace Amib.Threading
#region IWorkItemResult interface
/// <summary>
/// IWorkItemResult interface
/// The common interface of IWorkItemResult and IWorkItemResult&lt;T&gt;
/// </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>
/// Get the result of the work item.
/// If the work item didn't run yet then the caller waits.
/// </summary>
/// <returns>The result of the work item</returns>
object GetResult();
TResult GetResult();
/// <summary>
/// Get the result of the work item.
@ -120,7 +440,7 @@ namespace Amib.Threading
/// </summary>
/// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException
object GetResult(
TResult GetResult(
int millisecondsTimeout,
bool exitContext);
@ -130,12 +450,10 @@ namespace Amib.Threading
/// </summary>
/// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException
object GetResult(
TResult GetResult(
TimeSpan timeout,
bool exitContext);
void Abort();
/// <summary>
/// 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.
@ -148,7 +466,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
object GetResult(
TResult GetResult(
int millisecondsTimeout,
bool exitContext,
WaitHandle cancelWaitHandle);
@ -160,7 +478,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
object GetResult(
TResult GetResult(
TimeSpan timeout,
bool exitContext,
WaitHandle cancelWaitHandle);
@ -171,16 +489,18 @@ namespace Amib.Threading
/// </summary>
/// <param name="e">Filled with the exception if one was thrown</param>
/// <returns>The result of the work item</returns>
object GetResult(out Exception e);
TResult GetResult(out Exception e);
/// <summary>
/// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout.
/// </summary>
/// <param name="millisecondsTimeout"></param>
/// <param name="exitContext"></param>
/// <param name="e">Filled with the exception if one was thrown</param>
/// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException
object GetResult(
TResult GetResult(
int millisecondsTimeout,
bool exitContext,
out Exception e);
@ -189,10 +509,12 @@ namespace Amib.Threading
/// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout.
/// </summary>
/// <param name="exitContext"></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>
/// On timeout throws WorkItemTimeoutException
object GetResult(
TResult GetResult(
TimeSpan timeout,
bool exitContext,
out Exception e);
@ -210,7 +532,7 @@ namespace Amib.Threading
/// <returns>The result of the work item</returns>
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
object GetResult(
TResult GetResult(
int millisecondsTimeout,
bool exitContext,
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.
/// </summary>
/// <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="timeout"></param>
/// <param name="exitContext"></param>
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
object GetResult(
TResult GetResult(
TimeSpan timeout,
bool exitContext,
WaitHandle cancelWaitHandle,
@ -241,16 +566,30 @@ namespace Amib.Threading
bool IsCanceled { get; }
/// <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>
object State { get; }
/// <summary>
/// Cancel the work item if it didn't start running yet.
/// Same as Cancel(false).
/// </summary>
/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
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>
/// Get the work item's priority
/// </summary>
@ -259,7 +598,7 @@ namespace Amib.Threading
/// <summary>
/// Return the result, same as GetResult()
/// </summary>
object Result { get; }
TResult Result { get; }
/// <summary>
/// Returns the exception if occured otherwise returns null.
@ -267,5 +606,23 @@ namespace Amib.Threading
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
}

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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace Amib.Threading.Internal
@ -25,17 +23,17 @@ namespace Amib.Threading.Internal
/// <summary>
/// Work items queues. There is one for each type of priority
/// </summary>
private Queue [] _queues = new Queue[_queuesCount];
private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];
/// <summary>
/// The total number of work items within the queues
/// </summary>
private int _workItemsCount = 0;
private int _workItemsCount;
/// <summary>
/// Use with IEnumerable interface
/// </summary>
private int _version = 0;
private int _version;
#endregion
@ -45,7 +43,7 @@ namespace Amib.Threading.Internal
{
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 < _queuesCount);
_queues[queueIndex].Enqueue(workItem);
_queues[queueIndex].AddLast(workItem);
++_workItemsCount;
++_version;
}
@ -82,7 +80,8 @@ namespace Amib.Threading.Internal
{
int queueIndex = GetNextNonEmptyQueue(-1);
Debug.Assert(queueIndex >= 0);
workItem = _queues[queueIndex].Dequeue() as IHasWorkItemPriority;
workItem = _queues[queueIndex].First.Value;
_queues[queueIndex].RemoveFirst();
Debug.Assert(null != workItem);
--_workItemsCount;
++_version;
@ -128,7 +127,7 @@ namespace Amib.Threading.Internal
{
if (_workItemsCount > 0)
{
foreach(Queue queue in _queues)
foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
{
queue.Clear();
}
@ -159,7 +158,7 @@ namespace Amib.Threading.Internal
/// </summary>
private class PriorityQueueEnumerator : IEnumerator
{
private PriorityQueue _priorityQueue;
private readonly PriorityQueue _priorityQueue;
private int _version;
private int _queueIndex;
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.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
{
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
{
// Fields
@ -37,7 +59,7 @@ namespace Amib.Threading.Internal
internal class STPPerformanceCounter
{
// Fields
private PerformanceCounterType _pcType;
private readonly PerformanceCounterType _pcType;
protected string _counterHelp;
protected string _counterName;
@ -47,9 +69,9 @@ namespace Amib.Threading.Internal
string counterHelp,
PerformanceCounterType pcType)
{
this._counterName = counterName;
this._counterHelp = counterHelp;
this._pcType = pcType;
_counterName = counterName;
_counterHelp = counterHelp;
_pcType = pcType;
}
public void AddCounterToCollection(CounterCreationDataCollection counterData)
@ -76,7 +98,7 @@ namespace Amib.Threading.Internal
{
// Fields
internal STPPerformanceCounter[] _stpPerformanceCounters;
private static STPPerformanceCounters _instance;
private static readonly STPPerformanceCounters _instance;
internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
internal const string _stpCategoryName = "SmartThreadPool";
@ -127,19 +149,12 @@ namespace Amib.Threading.Internal
_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(
_stpCategoryName,
_stpCategoryHelp,
//PerformanceCounterCategoryType.MultiInstance,
PerformanceCounterCategoryType.MultiInstance,
counters);
#pragma warning restore 0618
}
}
@ -156,16 +171,18 @@ namespace Amib.Threading.Internal
internal class STPInstancePerformanceCounter : IDisposable
{
// Fields
private bool _isDisposed;
private PerformanceCounter _pcs;
// Methods
protected STPInstancePerformanceCounter()
{
_isDisposed = false;
}
public STPInstancePerformanceCounter(
string instance,
STPPerformanceCounterType spcType)
STPPerformanceCounterType spcType) : this()
{
STPPerformanceCounters counters = STPPerformanceCounters.Instance;
_pcs = new PerformanceCounter(
@ -176,10 +193,6 @@ namespace Amib.Threading.Internal
_pcs.RawValue = _pcs.RawValue;
}
~STPInstancePerformanceCounter()
{
Close();
}
public void Close()
{
@ -192,9 +205,20 @@ namespace Amib.Threading.Internal
}
public void Dispose()
{
Dispose(true);
}
public virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
Close();
GC.SuppressFinalize(this);
}
}
_isDisposed = true;
}
public virtual void Increment()
@ -216,27 +240,19 @@ namespace Amib.Threading.Internal
internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
{
// Methods
public STPInstanceNullPerformanceCounter() {}
public override void Increment() {}
public override void IncrementBy(long value) {}
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
private STPInstancePerformanceCounter[] _pcs;
private static STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
// Methods
static STPInstancePerformanceCounters()
@ -246,8 +262,13 @@ namespace Amib.Threading.Internal
public STPInstancePerformanceCounters(string instance)
{
_isDisposed = false;
_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++)
{
if (instance != null)
@ -272,22 +293,28 @@ namespace Amib.Threading.Internal
{
if (null != _pcs[i])
{
_pcs[i].Close();
_pcs[i].Dispose();
}
}
_pcs = null;
}
}
~STPInstancePerformanceCounters()
public void Dispose()
{
Dispose(true);
}
public virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
Close();
}
public void Dispose()
{
Close();
GC.SuppressFinalize(this);
}
_isDisposed = true;
}
private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
@ -327,21 +354,17 @@ namespace Amib.Threading.Internal
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
}
}
#endif
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
{
static NullSTPInstancePerformanceCounters()
{
}
private static NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(null);
private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();
public static NullSTPInstancePerformanceCounters Instance
{
get { return _instance; }
}
public NullSTPInstancePerformanceCounters(string instance) {}
public void Close() {}
public void Dispose() {}
@ -349,6 +372,77 @@ namespace Amib.Threading.Internal
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
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
// amibar@gmail.com
using System;
using System.Threading;
namespace Amib.Threading
@ -10,104 +8,205 @@ namespace Amib.Threading
/// </summary>
public class STPStartInfo : WIGStartInfo
{
/// <summary>
/// Idle timeout in milliseconds.
/// If a thread is idle for _idleTimeout milliseconds then
/// it may quit.
/// </summary>
private int _idleTimeout;
private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
#if !(WINDOWS_PHONE)
private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
#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>
/// 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()
public STPStartInfo()
{
_performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
#if !(WINDOWS_PHONE)
_threadPriority = SmartThreadPool.DefaultThreadPriority;
#endif
_maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
_idleTimeout = SmartThreadPool.DefaultIdleTimeout;
_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;
_minWorkerThreads = stpStartInfo._minWorkerThreads;
_maxWorkerThreads = stpStartInfo._maxWorkerThreads;
_threadPriority = stpStartInfo._threadPriority;
_idleTimeout = stpStartInfo.IdleTimeout;
_minWorkerThreads = stpStartInfo.MinWorkerThreads;
_maxWorkerThreads = stpStartInfo.MaxWorkerThreads;
#if !(WINDOWS_PHONE)
_threadPriority = stpStartInfo.ThreadPriority;
#endif
_performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName;
_enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters;
_threadPoolName = stpStartInfo._threadPoolName;
_pcInstanceName = stpStartInfo._pcInstanceName;
_stackSize = stpStartInfo._stackSize;
_areThreadsBackground = stpStartInfo.AreThreadsBackground;
#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; }
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; }
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; }
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; }
set { _threadPriority = value; }
}
public virtual string ThreadPoolName
set
{
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; }
set { _threadPoolName = value; }
}
public string PerformanceCounterInstanceName
set
{
get { return _pcInstanceName; }
set { _pcInstanceName = value; }
ThrowIfReadOnly ();
_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; }
set { _stackSize = value; }
get { return _performanceCounterInstanceName; }
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
// amibar@gmail.com
using System;
namespace Amib.Threading
{
@ -8,92 +7,165 @@ namespace Amib.Threading
/// </summary>
public class WIGStartInfo
{
/// <summary>
/// Use the caller's security context
/// </summary>
private bool _useCallerCallContext;
/// <summary>
/// Use the caller's HTTP 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>
/// Indicate the WorkItemsGroup to suspend the handling of the work items
/// until the Start() method is called.
/// </summary>
private bool _startSuspended;
private WorkItemPriority _workItemPriority;
private bool _fillStateWithArgs;
protected bool _readOnly;
public WIGStartInfo()
{
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
_workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
_startSuspended = SmartThreadPool.DefaultStartSuspended;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
}
public WIGStartInfo(WIGStartInfo wigStartInfo)
{
_useCallerCallContext = wigStartInfo._useCallerCallContext;
_useCallerHttpContext = wigStartInfo._useCallerHttpContext;
_disposeOfStateObjects = wigStartInfo._disposeOfStateObjects;
_callToPostExecute = wigStartInfo._callToPostExecute;
_postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback;
_startSuspended = wigStartInfo._startSuspended;
_useCallerCallContext = wigStartInfo.UseCallerCallContext;
_useCallerHttpContext = wigStartInfo.UseCallerHttpContext;
_disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
_callToPostExecute = wigStartInfo.CallToPostExecute;
_postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
_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; }
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; }
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; }
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; }
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; }
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; }
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.Threading;
using System.Diagnostics;
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>
/// Holds a callback delegate and the state for that delegate.
/// </summary>
public class WorkItem : IHasWorkItemPriority, IWorkItem
public partial class WorkItem : IHasWorkItemPriority
{
#region WorkItemState enum
@ -61,33 +16,57 @@ namespace Amib.Threading.Internal
/// </summary>
private enum WorkItemState
{
InQueue,
InProgress,
Completed,
Canceled,
InQueue = 0, // Nexts: InProgress, Canceled
InProgress = 1, // Nexts: Completed, Canceled
Completed = 2, // Stays Completed
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
#region Member Variables
public Thread currentThread;
#region Fields
/// <summary>
/// Callback delegate for the callback.
/// </summary>
private WorkItemCallback _callback;
private readonly WorkItemCallback _callback;
/// <summary>
/// State with which to call the callback delegate.
/// </summary>
private object _state;
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
/// <summary>
/// Stores the caller's context
/// </summary>
private CallerThreadContext _callerContext;
private readonly CallerThreadContext _callerContext;
#endif
/// <summary>
/// Holds the result of the mehtod
/// </summary>
@ -117,12 +96,12 @@ namespace Amib.Threading.Internal
/// <summary>
/// Represents the result state of the work item
/// </summary>
private WorkItemResult _workItemResult;
private readonly WorkItemResult _workItemResult;
/// <summary>
/// Work item info
/// </summary>
private WorkItemInfo _workItemInfo;
private readonly WorkItemInfo _workItemInfo;
/// <summary>
/// Called when the WorkItem starts
@ -141,30 +120,41 @@ namespace Amib.Threading.Internal
private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
/// <summary>
/// The work item group this work item belong to.
///
/// A reference to an object that indicates whatever the
/// SmartThreadPool has been canceled
/// </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
/// <summary>
/// The time when the work items is queued.
/// Used with the performance counter.
/// </summary>
private DateTime _queuedTime;
/// <summary>
/// The time when the work items starts its execution.
/// Used with the performance counter.
/// Stores how long the work item waited on the stp queue
/// </summary>
private DateTime _beginProcessTime;
private Stopwatch _waitingOnQueueStopwatch;
/// <summary>
/// The time when the work items ends its execution.
/// Used with the performance counter.
/// Stores how much time it took the work item to execute after it went out of the queue
/// </summary>
private DateTime _endProcessTime;
private Stopwatch _processingStopwatch;
#endregion
@ -176,7 +166,7 @@ namespace Amib.Threading.Internal
{
get
{
return (_beginProcessTime - _queuedTime);
return _waitingOnQueueStopwatch.Elapsed;
}
}
@ -184,7 +174,15 @@ namespace Amib.Threading.Internal
{
get
{
return (_endProcessTime - _beginProcessTime);
return _processingStopwatch.Elapsed;
}
}
internal WorkItemInfo WorkItemInfo
{
get
{
return _workItemInfo;
}
}
@ -195,6 +193,8 @@ namespace Amib.Threading.Internal
/// <summary>
/// Initialize the callback holding object.
/// </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="state">State with which to call the callback delegate.</param>
///
@ -209,10 +209,12 @@ namespace Amib.Threading.Internal
_workItemsGroup = workItemsGroup;
_workItemInfo = workItemInfo;
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
{
_callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
}
#endif
_callback = callback;
_state = state;
@ -222,9 +224,18 @@ namespace Amib.Threading.Internal
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;
_workItemCompleted = null;
_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)
@ -237,17 +248,16 @@ namespace Amib.Threading.Internal
#region Methods
public CanceledWorkItemsGroup CanceledWorkItemsGroup
internal CanceledWorkItemsGroup CanceledWorkItemsGroup
{
get
{
return _canceledWorkItemsGroup;
get { return _canceledWorkItemsGroup; }
set { _canceledWorkItemsGroup = value; }
}
set
internal CanceledWorkItemsGroup CanceledSmartThreadPool
{
_canceledWorkItemsGroup = value;
}
get { return _canceledSmartThreadPool; }
set { _canceledSmartThreadPool = value; }
}
/// <summary>
@ -259,7 +269,8 @@ namespace Amib.Threading.Internal
/// </returns>
public bool StartingWorkItem()
{
_beginProcessTime = DateTime.Now;
_waitingOnQueueStopwatch.Stop();
_processingStopwatch.Start();
lock (this)
{
@ -277,6 +288,9 @@ namespace Amib.Threading.Internal
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);
}
@ -311,7 +325,7 @@ namespace Amib.Threading.Internal
PostExecute();
}
_endProcessTime = DateTime.Now;
_processingStopwatch.Stop();
}
internal void FireWorkItemCompleted()
@ -323,7 +337,20 @@ namespace Amib.Threading.Internal
_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>
private void ExecuteWorkItem()
{
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
CallerThreadContext ctc = null;
if (null != _callerContext)
{
ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
CallerThreadContext.Apply(_callerContext);
}
#endif
Exception exception = null;
object result = null;
try
{
try
{
result = _callback(_state);
@ -352,13 +384,46 @@ namespace Amib.Threading.Internal
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)
{
CallerThreadContext.Apply(ctc);
}
#endif
if (!SmartThreadPool.IsWorkItemCanceled)
{
SetResult(result, exception);
}
}
/// <summary>
/// Runs the post execute callback
@ -369,7 +434,7 @@ namespace Amib.Threading.Internal
{
try
{
_workItemInfo.PostExecuteWorkItemCallback(this._workItemResult);
_workItemInfo.PostExecuteWorkItemCallback(_workItemResult);
}
catch (Exception e)
{
@ -382,6 +447,8 @@ namespace Amib.Threading.Internal
/// Set the result of the work item to return
/// </summary>
/// <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)
{
_result = result;
@ -401,39 +468,39 @@ namespace Amib.Threading.Internal
/// <summary>
/// Wait for all work items to complete
/// </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="exitContext">
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
/// </param>
/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
/// <returns>
/// true when every work item in workItemResults has completed; otherwise false.
/// true when every work item in waitableResults has completed; otherwise false.
/// </returns>
internal static bool WaitAll(
IWorkItemResult [] workItemResults,
IWaitableResult[] waitableResults,
int millisecondsTimeout,
bool exitContext,
WaitHandle cancelWaitHandle)
{
if (0 == workItemResults.Length)
if (0 == waitableResults.Length)
{
return true;
}
bool success;
WaitHandle [] waitHandles = new WaitHandle[workItemResults.Length];;
GetWaitHandles(workItemResults, waitHandles);
WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length];
GetWaitHandles(waitableResults, waitHandles);
if ((null == cancelWaitHandle) && (waitHandles.Length <= 64))
{
success = WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
}
else
{
success = true;
int millisecondsLeft = millisecondsTimeout;
DateTime start = DateTime.Now;
Stopwatch stopwatch = Stopwatch.StartNew();
WaitHandle[] whs;
if (null != cancelWaitHandle)
@ -450,7 +517,7 @@ namespace Amib.Threading.Internal
// We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle
// won't affect it.
// 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
if (!waitInfinitely && (millisecondsLeft < 0))
@ -460,8 +527,8 @@ namespace Amib.Threading.Internal
}
whs[0] = waitHandles[i];
int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
if((result > 0) || (WaitHandle.WaitTimeout == result))
int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result))
{
success = false;
break;
@ -470,13 +537,12 @@ namespace Amib.Threading.Internal
if (!waitInfinitely)
{
// Update the time left to wait
TimeSpan ts = DateTime.Now - start;
millisecondsLeft = millisecondsTimeout - (int)ts.TotalMilliseconds;
millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
}
}
}
// Release the wait handles
ReleaseWaitHandles(workItemResults);
ReleaseWaitHandles(waitableResults);
return success;
}
@ -484,7 +550,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
/// </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="exitContext">
/// 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.
/// </returns>
internal static int WaitAny(
IWorkItemResult [] workItemResults,
IWaitableResult[] waitableResults,
int millisecondsTimeout,
bool exitContext,
WaitHandle cancelWaitHandle)
{
WaitHandle [] waitHandles = null;
WaitHandle[] waitHandles;
if (null != cancelWaitHandle)
{
waitHandles = new WaitHandle[workItemResults.Length+1];
GetWaitHandles(workItemResults, waitHandles);
waitHandles[workItemResults.Length] = cancelWaitHandle;
waitHandles = new WaitHandle[waitableResults.Length + 1];
GetWaitHandles(waitableResults, waitHandles);
waitHandles[waitableResults.Length] = cancelWaitHandle;
}
else
{
waitHandles = new WaitHandle[workItemResults.Length];
GetWaitHandles(workItemResults, waitHandles);
waitHandles = new WaitHandle[waitableResults.Length];
GetWaitHandles(waitableResults, waitHandles);
}
int result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
// Treat cancel as timeout
if (null != cancelWaitHandle)
{
if (result == workItemResults.Length)
if (result == waitableResults.Length)
{
result = WaitHandle.WaitTimeout;
result = STPEventWaitHandle.WaitTimeout;
}
}
ReleaseWaitHandles(workItemResults);
ReleaseWaitHandles(waitableResults);
return result;
}
@ -532,16 +598,16 @@ namespace Amib.Threading.Internal
/// <summary>
/// Fill an array of wait handles with the work items wait handles.
/// </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>
private static void GetWaitHandles(
IWorkItemResult [] workItemResults,
IWaitableResult[] waitableResults,
WaitHandle[] waitHandles)
{
for(int i = 0; i < workItemResults.Length; ++i)
for (int i = 0; i < waitableResults.Length; ++i)
{
WorkItemResult wir = workItemResults[i] as WorkItemResult;
Debug.Assert(null != wir, "All workItemResults must be WorkItemResult objects");
WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult;
Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects");
waitHandles[i] = wir.GetWorkItem().GetWaitHandle();
}
@ -550,31 +616,52 @@ namespace Amib.Threading.Internal
/// <summary>
/// Release the work items' wait handles
/// </summary>
/// <param name="workItemResults">An array of work item results</param>
private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults)
/// <param name="waitableResults">An array of work item results</param>
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();
}
}
#endregion
#region Private Members
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;
return _workItemState;
}
}
/// <summary>
/// Sets the work item's state
/// </summary>
@ -582,10 +669,13 @@ namespace Amib.Threading.Internal
private void SetWorkItemState(WorkItemState workItemState)
{
lock (this)
{
if (IsValidStatesTransition(_workItemState, workItemState))
{
_workItemState = workItemState;
}
}
}
/// <summary>
/// Signals that work item has been completed or canceled
@ -606,7 +696,7 @@ namespace Amib.Threading.Internal
internal void WorkItemIsQueued()
{
_queuedTime = DateTime.Now;
_waitingOnQueueStopwatch.Start();
}
#endregion
@ -617,29 +707,82 @@ namespace Amib.Threading.Internal
/// Cancel the work item if it didn't start running yet.
/// </summary>
/// <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)
{
switch (GetWorkItemState())
{
case WorkItemState.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.InProgress:
//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:
// Signal to the wait for completion that the work
// item has been completed (canceled). There is no
// reason to wait for it to get out of the queue
SignalComplete(true);
signalComplete = true;
//Debug.WriteLine("Work item canceled");
return true;
success = true;
break;
}
if (signalComplete)
{
SignalComplete(true);
}
}
return false;
return success;
}
/// <summary>
@ -653,7 +796,7 @@ namespace Amib.Threading.Internal
bool exitContext,
WaitHandle cancelWaitHandle)
{
Exception e = null;
Exception e;
object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
if (null != e)
{
@ -694,7 +837,7 @@ namespace Amib.Threading.Internal
{
WaitHandle wh = GetWaitHandle();
bool timeout = !wh.WaitOne(millisecondsTimeout, exitContext);
bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext);
ReleaseWaitHandle();
@ -706,7 +849,7 @@ namespace Amib.Threading.Internal
else
{
WaitHandle wh = GetWaitHandle();
int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
ReleaseWaitHandle();
switch (result)
@ -717,7 +860,7 @@ namespace Amib.Threading.Internal
// work item (not the get result)
break;
case 1:
case WaitHandle.WaitTimeout:
case STPEventWaitHandle.WaitTimeout:
throw new WorkItemTimeoutException("Work item timeout");
default:
Debug.Assert(false);
@ -749,7 +892,7 @@ namespace Amib.Threading.Internal
{
if (null == _workItemCompleted)
{
_workItemCompleted = new ManualResetEvent(IsCompleted);
_workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted);
}
++_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()
{
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;
namespace Amib.Threading.Internal
@ -12,6 +9,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item</returns>
@ -26,6 +24,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="workItemPriority">The priority of the work item</param>
@ -42,6 +41,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="workItemInfo">Work item info</param>
/// <param name="callback">A callback to execute</param>
@ -63,6 +63,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
@ -83,6 +84,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem(
workItemsGroup,
@ -95,6 +97,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
@ -131,6 +134,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="workItemInfo">Work item information</param>
/// <param name="callback">A callback to execute</param>
@ -160,6 +164,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
@ -185,6 +190,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem(
workItemsGroup,
@ -198,6 +204,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
@ -239,6 +246,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
@ -266,6 +274,7 @@ namespace Amib.Threading.Internal
workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
workItemInfo.CallToPostExecute = callToPostExecute;
workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
WorkItem workItem = new WorkItem(
workItemsGroup,
@ -279,6 +288,7 @@ namespace Amib.Threading.Internal
/// <summary>
/// Create a new work item
/// </summary>
/// <param name="workItemsGroup">The work items group</param>
/// <param name="wigStartInfo">Work item group start information</param>
/// <param name="callback">A callback to execute</param>
/// <param name="state">
@ -322,7 +332,7 @@ namespace Amib.Threading.Internal
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");
}

View File

@ -1,6 +1,3 @@
// Ami Bar
// amibar@gmail.com
namespace Amib.Threading
{
#region WorkItemInfo class
@ -10,92 +7,62 @@ namespace Amib.Threading
/// </summary>
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()
{
_useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
_useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
_disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
_callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
_postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
_workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
}
public WorkItemInfo(WorkItemInfo workItemInfo)
{
_useCallerCallContext = workItemInfo._useCallerCallContext;
_useCallerHttpContext = workItemInfo._useCallerHttpContext;
_disposeOfStateObjects = workItemInfo._disposeOfStateObjects;
_callToPostExecute = workItemInfo._callToPostExecute;
_postExecuteWorkItemCallback = workItemInfo._postExecuteWorkItemCallback;
_workItemPriority = workItemInfo._workItemPriority;
UseCallerCallContext = workItemInfo.UseCallerCallContext;
UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
CallToPostExecute = workItemInfo.CallToPostExecute;
PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
WorkItemPriority = workItemInfo.WorkItemPriority;
Timeout = workItemInfo.Timeout;
}
public bool UseCallerCallContext
{
get { return _useCallerCallContext; }
set { _useCallerCallContext = value; }
}
/// <summary>
/// Get/Set if to use the caller's security context
/// </summary>
public bool UseCallerCallContext { get; set; }
public bool UseCallerHttpContext
{
get { return _useCallerHttpContext; }
set { _useCallerHttpContext = value; }
}
/// <summary>
/// Get/Set if to use the caller's HTTP context
/// </summary>
public bool UseCallerHttpContext { get; set; }
public bool DisposeOfStateObjects
{
get { return _disposeOfStateObjects; }
set { _disposeOfStateObjects = value; }
}
/// <summary>
/// Get/Set if to dispose of the state object of a work item
/// </summary>
public bool DisposeOfStateObjects { get; set; }
public CallToPostExecute CallToPostExecute
{
get { return _callToPostExecute; }
set { _callToPostExecute = value; }
}
/// <summary>
/// Get/Set the run the post execute options
/// </summary>
public CallToPostExecute CallToPostExecute { get; set; }
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
{
get { return _postExecuteWorkItemCallback; }
set { _postExecuteWorkItemCallback = value; }
}
/// <summary>
/// Get/Set the post execute callback
/// </summary>
public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }
public WorkItemPriority WorkItemPriority
{
get { return _workItemPriority; }
set { _workItemPriority = value; }
}
/// <summary>
/// Get/Set the work item's priority
/// </summary>
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

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.Threading;
using System.Runtime.CompilerServices;
@ -8,33 +5,34 @@ using System.Diagnostics;
namespace Amib.Threading.Internal
{
#region WorkItemsGroup class
/// <summary>
/// Summary description for WorkItemsGroup.
/// </summary>
public class WorkItemsGroup : IWorkItemsGroup
public class WorkItemsGroup : WorkItemsGroupBase
{
#region Private members
private object _lock = new object();
/// <summary>
/// Contains the name of this instance of SmartThreadPool.
/// Can be changed by the user.
/// </summary>
private string _name = "WorkItemsGroup";
private readonly object _lock = new object();
/// <summary>
/// A reference to the SmartThreadPool instance that created this
/// WorkItemsGroup.
/// </summary>
private SmartThreadPool _stp;
private readonly SmartThreadPool _stp;
/// <summary>
/// The OnIdle event
/// </summary>
private event WorkItemsGroupIdleHandler _onIdle;
/// <summary>
/// A flag to indicate if the Work Items Group is now suspended.
/// </summary>
private bool _isSuspended;
/// <summary>
/// Defines how many work items of this WorkItemsGroup can run at once.
/// </summary>
@ -44,7 +42,7 @@ namespace Amib.Threading.Internal
/// Priority queue to hold work items before they are passed
/// to the SmartThreadPool.
/// </summary>
private PriorityQueue _workItemsQueue;
private readonly PriorityQueue _workItemsQueue;
/// <summary>
/// Indicate how many work items are waiting in the SmartThreadPool
@ -63,12 +61,13 @@ namespace Amib.Threading.Internal
/// <summary>
/// WorkItemsGroup start information
/// </summary>
private WIGStartInfo _workItemsGroupStartInfo;
private readonly WIGStartInfo _workItemsGroupStartInfo;
/// <summary>
/// Signaled when all of the WorkItemsGroup's work item completed.
/// </summary>
private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
//private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
/// <summary>
/// A common object for all the work items that this work items group
@ -87,261 +86,90 @@ namespace Amib.Threading.Internal
{
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;
_concurrency = concurrency;
_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo);
_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly();
_workItemsQueue = new PriorityQueue();
Name = "WorkItemsGroup";
// The _workItemsInStpQueue gets the number of currently executing work items,
// because once a work item is executing, it cannot be cancelled.
_workItemsInStpQueue = _workItemsExecutingInStp;
_isSuspended = _workItemsGroupStartInfo.StartSuspended;
}
#endregion
#region IWorkItemsGroup implementation
#region WorkItemsGroupBase Overrides
/// <summary>
/// Get/Set the name of the SmartThreadPool instance
/// </summary>
public string Name
public override int Concurrency
{
get
{
return _name;
}
get { return _concurrency; }
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>
/// Queue a work item
/// WorkItemsGroup start information
/// </summary>
/// <param name="callback">A callback to execute</param>
/// <returns>Returns a work item result</returns>
public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
public override WIGStartInfo WIGStartInfo
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
get { return _workItemsGroupStartInfo; }
}
/// <summary>
/// Queue a work item
/// Start the Work Items Group if it was started suspended
/// </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)
public override void Start()
{
WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority);
EnqueueToSTPNextWorkItem(workItem);
return workItem.GetWorkItemResult();
// If the Work Items Group already started then quit
if (!_isSuspended)
{
return;
}
_isSuspended = false;
EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
}
/// <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)
{
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()
public override void Cancel(bool abortExecution)
{
lock (_lock)
{
@ -350,23 +178,26 @@ namespace Amib.Threading.Internal
_workItemsInStpQueue = 0;
_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)
{
if (!_workItemsGroupStartInfo.StartSuspended)
{
return;
}
_workItemsGroupStartInfo.StartSuspended = false;
SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
}
for(int i = 0; i < _concurrency; ++i)
public override event WorkItemsGroupIdleHandler OnIdle
{
EnqueueToSTPNextWorkItem(null, false);
}
add { _onIdle += value; }
remove { _onIdle -= value; }
}
#endregion
@ -375,22 +206,24 @@ namespace Amib.Threading.Internal
private void RegisterToWorkItemCompletion(IWorkItemResult wir)
{
IInternalWorkItemResult iwir = wir as IInternalWorkItemResult;
iwir.OnWorkItemStarted += new WorkItemStateCallback(OnWorkItemStartedCallback);
iwir.OnWorkItemCompleted += new WorkItemStateCallback(OnWorkItemCompletedCallback);
IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
}
public void OnSTPIsStarting()
{
lock (this)
{
if (_workItemsGroupStartInfo.StartSuspended)
if (_isSuspended)
{
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);
}
@ -417,8 +250,7 @@ namespace Amib.Threading.Internal
{
eh(this);
}
// Ignore exceptions
catch{}
catch { } // Suppress exceptions
}
}
@ -435,6 +267,11 @@ namespace Amib.Threading.Internal
EnqueueToSTPNextWorkItem(null, true);
}
internal override void Enqueue(WorkItem workItem)
{
EnqueueToSTPNextWorkItem(workItem);
}
private void EnqueueToSTPNextWorkItem(WorkItem workItem)
{
EnqueueToSTPNextWorkItem(workItem, false);
@ -475,7 +312,7 @@ namespace Amib.Threading.Internal
(0 == _workItemsInStpQueue))
{
_stp.RegisterWorkItemsGroup(this);
Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle");
IsIdle = false;
_isIdleWaitHandle.Reset();
}
}
@ -486,19 +323,31 @@ namespace Amib.Threading.Internal
if (0 == _workItemsInStpQueue)
{
_stp.UnregisterWorkItemsGroup(this);
Trace.WriteLine("WorkItemsGroup " + Name + " is idle");
IsIdle = true;
_isIdleWaitHandle.Set();
_stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle));
if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
{
_stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
}
}
return;
}
if (!_workItemsGroupStartInfo.StartSuspended)
if (!_isSuspended)
{
if (_workItemsInStpQueue < _concurrency)
{
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;
}
}

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

View File

@ -21,6 +21,9 @@
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
; *
[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
; Set path to directory for plugin registry. Information

View File

@ -13,6 +13,9 @@
; * [[<ConfigName>@]<port>/]<dll name>[:<class name>]
; *
[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
; Set path to directory for plugin registry. Information
@ -21,7 +24,6 @@
; The Robust.exe process must have R/W access to the location
RegistryLocation = "."
; Modular configurations
; Set path to directory for modular ini files...
; The Robust.exe process must have R/W access to the location