diff --git a/Docs/SmartThreadPool.JPG b/Docs/SmartThreadPool.JPG
new file mode 100644
index 0000000..721e5ec
Binary files /dev/null and b/Docs/SmartThreadPool.JPG differ
diff --git a/Docs/SmartThreadPool.html b/Docs/SmartThreadPool.html
new file mode 100644
index 0000000..4a21efd
--- /dev/null
+++ b/Docs/SmartThreadPool.html
@@ -0,0 +1,1201 @@
+
This is a Thread Pool, if you got here you probably you know what you
+ need. If you want to understand the features and know how it works keep reading
+ the sections below. If you just want to use it, here is a quick usage snippet.
+ See examples for advanced usage.
+Smart Thread Pool is a thread pool written in C#. The implementation was first based on Stephan Toub's thread pool with some extra features, but now it is far beyond the original. Here is a list of the thread pool features:
+When I wrote my application, I discovered that I needed a thread pool with the following features:
+After I published the smart thread pool here, I found out that more features were required and some features had to change. So, the following is an updated list of the implemented features:
+Because of features 3 and 5, the thread pool no longer complies to the .NET ThreadPool, and so I could add more features.
+See the additional features section below for the new features added in this version.
+The Windows system provides one .NET ThreadPool for each process. The .NET ThreadPool can contain up to 25 (by default) threads per processor. It is also stated that the operations in .NET ThreadPool should be quick to avoid suspension of the work of others who use the .NET ThreadPool. Note that several AppDomains in the same process share the same .NET ThreadPool. If you want a thread to work for a long period of time, then the .NET ThreadPool is not a good choice for you (unless you know what you are doing). Note that each asynchronous method call from the .NET Framework that begins with "Begin…" (e.g., BeginInvoke, BeginSend, BeginReceive, etc.) uses the .NET ThreadPool to run its callback. Also note that the .NET ThreadPool
+ doesn't support calls to COM with single threaded apartment (STA), since the ThreadPool
+ threads are MTA by design.
+This thread pool doesn't comply with the requirements 1, 5, 6, 8, 9, 10, 12-22.
+Note that the requirements 3 and 4 are implemented in .NET ThreadPool with delegates.
+Toub's thread pool is a better choice than the .NET ThreadPool, since a thread from his pool can be used for a longer period of time, without affecting the asynchronous method calls. Toub's thread pool uses static methods; hence you cannot instantiate more than one thread pool. However, this limitation applies per AppDomain rather than the whole process. The main disadvantage of Toub's thread pool over the .NET TheradPool is that Toub creates all the threads in the pool at the initialization point, while the .NET ThreadPool creates threads on the fly.
+This thread pool doesn't comply with the requirements 1, 2, 3, 4, 5, 6, 8-22.
+As I mentioned before, the Smart Thread Pool is based on Toub's thread pool implementation. However, since I have expanded its features, the code is no longer similar to the original one.
+
+The thread pool is instantiated .
+The reason I need an instantiable thread pool is because I have different needs. I have work items that take a long time to execute and I have work items that take very short time to execute. Executing the same type of work items on the same thread pool may cause some serious performance or response problems.
+To implement this feature, I just copied Toub's implementation and removed the static keyword from the methods. That's the easy part of it.
+The number of threads dynamically changes .
+The number of threads dynamically changes according to the workload on the threads in the pool, with lower and upper constraints for the number of threads in the pool. This feature is needed so we won't have redundant threads in the application.
+This feature is a real issue and is the core of the Smart Thread Pool. How do you know when to add a new thread and when to remove it?
+I decided to add a new thread every time a new work item is queued and all the threads in the pool are busy. The formula for adding a new thread can be summarized to:
(InUseThreads + WaitingCallbacks) > WorkerThreads
+where WorkerThreads is the current number of threads in the pool, InUseThreads is the number of threads in the pool that are currently working on a work item, and WaitingCallbacks is the number of waiting work items. (Thanks to jrshute for the comment.)
+The SmartThreadPool.Enqueue() method looks like this:
private void Enqueue(WorkItem workItem)
+{
+ // Make sure the workItem is not null
+ Debug.Assert(null != workItem);
+
+ // Enqueue the work item
+ _workItemsQueue.EnqueueWorkItem(workItem);
+
+ // If all the threads are busy then try
+ // to create a new one
+ if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count)
+ {
+ StartThreads(1);
+ }
+}
+When the number of threads reaches the upper limit, no more threads are created.
+I decided to remove a thread from the pool when it is idle (i.e. the thread doesn't work on any work item) for a specific period of time. Each time a thread waits for a work item on the work item's queue, it also waits for a timeout. If the waiting exceeds the timeout, the thread should leave the pool, meaning the thread should quit if it is idle. It sounds like a simple solution, but what about the following scenario: assume that the lower limit of threads is 0 and the upper limit is 100. The idle timeout is 60 seconds. Currently, the thread pool contains 60 threads, and each second a new work item arrives, and it takes a thread one second to handle a work item. This means that, each minute, 60 work items arrive and are handled by 60 threads in the pool. As a result, no thread exits, since no thread is idle for a full 60 seconds, although 1 or 2 threads are enough to do all the work.
+In order to solve the problem of this scenario, you have to calculate how much time each thread worked, and once in a while exit the threads that don't work for enough time in the timeout interval. This means, the thread pool has to use a timer (uses the .NET ThreadPool) or a manager thread to handle the thread pool. To me, it seems an overhead to use a special thread to handle a thread pool.
+This led me to the idea that the thread pool mechanism should starve the threads in order to let them quit. So, how do you starve the threads?
+All the threads in the pool are waiting on the same work items queue. The work items queue manages two queues, one for the work items and one for the waiters (the threads in the pool). Since the trivial work items queue works, the first arrived waiter for a work item gets it first (queue), and so you cannot starve the threads.
+Have a look at the following scenario:
+The thread pool contains four threads. Let's name them A, B, C, and D. Every second a new work item arrives, and it takes less than one second and a half to handle each work item:
+
+
+
+
+Work item arrival time (sec)
+
+Work item work duration (sec)
+
+Threads queue state
+
+The thread that will execute the arrived work item
+
+
+00:00:00
+
+1.5
+
+A, B, C, D
+
+A
+
+
+00:00:01
+
+1.5
+
+B, C, D
+
+B
+
+
+00:00:02
+
+1.5
+
+C, D, A
+
+C
+
+
+00:00:03
+
+1.5
+
+D, A, B
+
+D
+In this scenario, all the four threads are used, although two threads could handle all the work items.
+The solution is to implement the waiters queue as a stack. In this implementation, the last arrived waiter for a work item gets it first (stack). This way, a thread that just finished its work on a work item waits as the first waiter in the queue of waiters.
+The previous scenario will look like this with the new implementation:
+
+
+
+
+Work item arrival time (sec)
+
+Work item work duration (sec)
+
+Threads queue state
+
+The thread that will execute the arrived work item
+
+
+00:00:00
+
+1.5
+
+A, B, C, D
+
+A
+
+
+00:00:01
+
+1.5
+
+B, C, D
+
+B
+
+
+00:00:02
+
+1.5
+
+A, C, D
+
+A
+
+
+00:00:03
+
+1.5
+
+B, C, D
+
+B
+Threads A and B handle all the work items, since they get back to the front of the waiters queue after they have finished. Threads C and D are starved, and if the same work items are going to arrive for a long time, then the threads C and D will have to quit.
+The thread pool doesn't implement a load balancing mechanism, since all the threads run on the same machine and take the same CPUs. Note that if you have many threads in the pool, then you will prefer minimum number of threads to do the job, since each context switch of the threads may result in paging of the threads' stacks. Less working threads means less paging of the threads' stacks.
+The work items queue implementation causes threads to starve, and the starved threads quit. This solves the scenario I mentioned earlier without using any extra thread.
+The second feature also states that there should be a lower limit to the number of threads in the pool. To implement this feature, every thread that gets a timeout, because it doesn't get any work items, checks whatever it can to quit. The Smart Thread Pool allows the thread to quit only if the current number of threads is above the lower limit. If the number of threads in the pool is below or equal to the lower limit, then the thread stays alive.
+Work items return a value . (This feature is enhanced)This feature is very useful in cases you want to know the result of a work item.
+The .NET ThreadPool supports this feature via delegates. Each time you create a delegate you get for free BeginInvoke() and EndInvoke() methods. The BeginInvoke() queues the method and its parameters on the .NET ThreadPool and the EndInvoke() returns the result of the method. The delegate class is sealed so I couldn't override the BeginInvoke() and EndInvoke() methods. I took a different approach to implement this.
+First, the work item callback delegate can return a value:
+public delegate object WorkItemCallback(object state);
+
+Or in its enhanced form the callback can be any of the following forms:
+
+public delegate void Action();
+public delegate void Action<T>(T arg);
+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();
+public delegate TResult Func<T>(T arg1);
+public delegate TResult Func<T1, T2>(T1 arg1, T2 arg2);
+public delegate TResult Func<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
+public delegate TResult Func<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+(Note that the above delegates are defined in .NET 3.5. In .NET 2.0 & 3.0 only public delegate void Action<T>(T arg) is defined)
+Second, the SmartThreadPool.QueueWorkItem() method returns a reference to an object that implements the
+ IWorkItemResult<TResult> interface. The caller can use this object to get the result of the work item. The interface is similar to the IAsyncResult interface:
+public interface IWorkItemResult<TResult>
+{
+ /// Get the result of the work item.
+ /// If the work item didn't run yet then the caller waits
+ /// until timeout or until the cancelWaitHandle is signaled.
+ /// If the work item threw then GetResult() will rethrow it.
+ /// Returns the result of the work item.
+ /// On timeout throws WorkItemTimeoutException.
+ /// On cancel throws WorkItemCancelException.
+ TResult GetResult(
+ int millisecondsTimeout,
+ bool exitContext,
+ WaitHandle cancelWaitHandle);
+
+ /// Some of the GetResult() overloads
+ /// get Exception as an output parameter.
+ /// In case the work item threw
+ /// an exception this parameter is filled with
+ /// it and the GetResult() returns null.
+ /// These overloads are provided
+ /// for performance reasons. It is faster to
+ /// return the exceptions as an output
+ /// parameter than rethrowing it.
+ TResult GetResult(..., out Exception e);
+
+ /// Other GetResult() overloads.
+ ...
+
+ /// Gets an indication whether the asynchronous operation has completed.
+ bool IsCompleted { get; }
+
+ /// Returns the user-defined object that was provided in the QueueWorkItem.
+ /// If the work item callback is Action<...> or Func<...> the State value
+ /// depends on the WIGStartInfo.FillStateWithArgs.
+ object State { get; }
+
+ /// Cancel the work item execution.
+ /// If the work item is in the queue, it won't execute
+ /// If the work item is completed, it will remain completed
+ /// If the work item is already cancelled it will remain cancelled
+ /// If the work item is in progress, the result of the work item is cancelled.
+ /// (See the work item canceling section for more information)
+ /// Param: abortExecution - When true send an AbortException to the executing thread.</param>
+ /// Returns true if the work item was not completed, otherwise false.
+ bool Cancel(bool abortExecution);
+
+ /// Get the work item's priority
+ WorkItemPriority WorkItemPriority { get; }
+
+ /// Returns the result, same as GetResult().
+ /// Note that this property blocks the caller like GetResult().
+ TResult Result { get; }
+
+ /// Returns the exception, if occured, otherwise returns null.
+ /// This function is not blocking like the Result property.
+ object Exception { get; }
+}
+
+
+
+ If the work item callback is object WorkItemCallback(object state)
+ then IWorkItemResult is returned and GetResult() returns object. Same as in previous versions
+
+ If the work item callback is one of the Func<...> methods I mentioned above, the result of the QueueWorkItem is IWorkItemResult<TResult>. So the result of the work item is strongly typed.
+
+
+
+ If the work item callback is one of the Action<...> methods I mentioned above, the result of the QueueWorkItem is IWorkItemResult and GetResult() always returns null.
+
+
+ If the work item callback is Action<...> or Func<...> and
+ WIGStartInfo.FillStateWithArgs is set to true then the State of the
+ IWorkItemResult is initialized with object [] that contains the work item
+ arguments. Otherwise the State is null.
+
+The code examples in the section below shows some snippets of how to use it.
+To get the result of the work item, use the Result property or the GetResult() method. This method has several overloads. In the interface above, I have written only some of them. The other overloads use less parameters by giving default values. The GetResult() returns the result of the work item callback. If the work item hasn't completed then the caller waits until one of the following occurs:
+
+
+
+GetResult() return reason
+GetResult() return value
+
+The work item has been executed and completed.
+The result of the work item.
+
+The work item has been canceled.
+Throws WorkItemCancelException.
+
+The timeout expired.
+Throws WorkItemTimeoutException.
+
+The cancelWaitHandle is signaled.
+Throws WorkItemTimeoutException.
+
+The work item threw an exception.
+Throws WorkItemResultException with the work item's exception as the inner exception.
+There are two ways to wait for a single work item to complete:
+
+The following function uses the GetResult() method which blocks the caller until the result is available: private void WaitForResult1(IWorkItemResult wir)
+{
+ wir.GetResult();
+}
+ The following function is not recommended, because it uses a busy wait loop. You can use it if you know what you are doing: private void WaitForResult2(IWorkItemResult wir)
+{
+ while(!wir.IsCompleted)
+ {
+ Thread.Sleep(100);
+ }
+}
+The caller can wait for multiple work items to complete .
+This feature is very useful if you want to run several work items at once and then wait for all of them to complete. The SmartThreadPool class has two static methods for this: WaitAny() and WaitAll() (they have several overloads). Their signature is similar to the WaitHandle equivalent methods except that in the SmartThreadPool case, it gets an array of
+IWaitableResult (the IWorkItemResult interface inherits from IWaitableResult) objects instead of WaitHandle objects.
+The following snippets show how to wait for several work item results at once. Assume wir1 and wir2 are of type IWorkItemResult. You can wait for both work items to complete:
// Wait for both work items complete
+SmartThreadPool.WaitAll(new IWaitableResult[] { wir1, wir2});
+Or, for any of the work items to complete:
// Wait for at least one of the work items complete
+SmartThreadPool.WaitAny(new IWaitableResult[] { wir1, wir2});
+The WaitAll() and WaitAny() methods are overloaded, so you can specify timeout, exit context, and cancelWaitHandle (just like in the GetResult() method mentioned earlier).
+Note that in order to use WaitAny() and WaitAll(), you need to work in MTA, because internally I use WaitHandle.WaitAny() and WaitHandle.WaitAll() which requires it. If you don't do that, the methods will throw an exception to remind you.
+Also note that Windows supports WaitAny() of up to 64 handles. The WaitAll() is more flexible and I re-implemented it so it is not limited to 64 handles.
+See in the examples section below the code snippets for WaitAll and WaitAny .
+A work item can be cancelled. (This feature is enhanced)
+
+ This feature enables to cancel work items.
+
+
+ There are several options to cancel work items. To cancel a single work item call
+ to IWorkItemResult.Cancel(). To cancel more than one call to IWorkItemsGroup.Cancel()
+ or SmartThreadPool.Cancel(). All cancels works in O(1).
+
+
+ There is no guarantee that a work item will be cancelled, it depends on the state
+ of the work item when the cancel is called and the cooperation of the work item.
+ (Note the work item's state I mention here has nothing to do with the state object
+ argument provided in the QueueWorkItem).
+
+ These are the possible states of a work item: (defined in the WorkItemState enum)
+
+ Queued – The work item is waiting in a queue to be executed.
+ In Progress – A thread from the pool is executing the work item.
+ Completed – The work item execution has been completed.
+ Cancelled – The work item has been cancelled.
+
+
+ The cancel behavior depends on the state of the work item.
+
+
+
+
+ Initial State
+
+
+ Next State
+
+
+ Notes
+
+
+
+ Queued
+
+ Cancelled
+
+ A queued work item becomes cancelled and is not executed at all.
+
+
+
+ In Progress
+
+ Cancelled
+
+ An executing work item becomes cancelled even if it completed its execution!
+
+
+
+ Completed
+
+ Completed
+
+ A completed work item stays completed
+
+
+
+ Cancelled
+
+ Cancelled
+
+ A cancelled work item stays cancelled
+
+
+
+
+ Cancelled work item throws WorkItemCancelException when their GetResult() methods
+ is called.
+
+
+ The behavior of the Cancel() when the work item is in Completed or Cancelled states
+ is straight forward so I won't get into details. A queued work item is marked as
+ cancelled and is discarded once a thread from the pool dequeues it.
+
+
+ If the work item is in the In Progress state then the behavior depends on the value
+ of abortExecution in the Cancel call. When the abortExecution is true, a Thread.Abort()
+ will be called upon the executing thread. When the abortExecution is false, the
+ work item method is responsible to sample the SmartThreadPool.IsWorkItemCanceled
+ static method and quit. Note that in both cases the work item is cancelled and throws
+ the WorkItemCancelException on GetResult().
+
+Here is an example of a cooperative work item:
+
+private void DoWork()
+{
+ // Do something here.
+
+ // Sample SmartThreadPool.IsWorkItemCanceled
+ if (SmartThreadPool.IsWorkItemCanceled)
+ {
+ return;
+ }
+
+ // Sample the SmartThreadPool.IsWorkItemCanceled in a loop
+ while (!SmartThreadPool.IsWorkItemCanceled)
+ {
+ // Do some work here
+ }
+}
+
+The caller thread's context is used when the work item is executed (limited) .
+This feature should be elementary, but it is not so simple to implement. In order to pass the thread's context, the caller thread's CompressedStack should be passed. This is impossible since Microsoft blocks this option with security. Other parts of the thread's context can be passed. These include:
+
+CurrentCulture - The culture of the thread.
+CurrentUICulture - The culture used by the resource manager to look up culture-specific resources at run time.
+CurrentPrincipal - The current principal (for role-based security).
+CurrentContext - The current context in which the thread is executing. (Used in remoting.)
+The first three belong to the System.Threading.Thread class (static or instance) and are get/set properties. However, the last one is a read only property. In order to set it, I used reflection, which slows down the application. If you need this context, just remove the comments from the code.
+To simplify the operation of capturing the context and then applying it later, I wrote a special class that is used internally and does all that stuff. The class is called CallerThreadContext and it is used internally. When Microsoft unblocks the protection on the CompressedStack, I will add it there.
+The caller thread's context is stored when the work item is created, within the EnqueueWorkItem() method. Each time a thread from the pool executes a work item, the thread's context changes in the following order:
+
+The current thread context is captured.
+ The caller thread context is applied.
+ The work item is executed.
+ The current old thread context is restored.
+
+Usage of minimum number of Win32 event handles, so the handle count of the application won't explode .
+The seventh feature is a result of Kevin's comment on the earlier version of Smart Thread Pool. It seemed that the test application consumed a lot of handles (Handle Count in the Task Manager) without freeing them. After a few tests, I got to the conclusion that the Close() method of ManualResetEvent class doesn't always release the Win32 event handle immediately, and waits for the garbage collector to do that. Hence, running the GC explicitly releases the handles.
+To make this problem less acute, I used a new approach. First, I wanted to create less number of handles, second, I wanted to reuse the handles I had already created. Therefore, I need not expose any WaitHandle but use them internally and then close them.
+In order to create fewer handles, I created the ManualResetEvent objects only when the user asks for them (lazy creation). For example, if you don't use the GetResult() of the IWorkItemResult interface then a handle is not created. Using SmartThreadPool.WaitAll() and SmartThreadPool.WaitAny() creates a handle.
+The work item queue created a lot of handles since each new wait for a work item created a new ManualResetEvent. Hence, a handle for each work item. The waiters of the queue are always the same threads and a thread cannot wait more than once. So now, every thread in the thread pool has its own ManualResetEvent and reuses it. To avoid coupling of the work items queue and the thread pool implementation, the work items queue stores a context inside the TLS (Thread Local Storage) of the thread.
+Work item can have a PostExecute callback. This is a method that will be called right after the work item execution has been completed .
+A PostExecute is a callback method that is called right after the work item execution has been completed. It runs in the same context of the thread that executed the work item. The user can choose the cases in which the PostExecute is called. The options are represented in the CallToPostExecute flagged enumerator:
[Flags]
+public enum CallToPostExecute
+{
+ Never = 0x00,
+ WhenWorkItemCanceled = 0x01,
+ WhenWorkItemNotCanceled = 0x02,
+ Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
+}
+Explanation:
+
+Never – Don't run the PostExecute.
+WhenWorkItemCanceled - Run the PostExecute only when the work item has been canceled.
+WhenWorkItemNotCanceled - Run the PostExecute only when the work item has not been canceled.
+Always – Always run the PostExecute.
+The SmartThreadPool has a default CallToPostExecute value of CallToPostExecute.Always. This can be changed during the construction of the SmartThreadPool in the STPStartInfo class argument. Another way to give the CallToPostExecute value is in one of the SmartThreadPool.QueueWorkItem overloads. Note that as opposed to the WorkItem execution, if an exception has been thrown during the PostExecute, then it is ignored. The PostExecute is a delegate with the following signature:
public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
+As you can see, the PostExecute receives as an argument of type IWorkItemResult. It can be used to get the result of the work item, or any other information made available by the IWorkItemResult interface.
+The user can choose to automatically dispose off the state object that accompanies the work item .
+When the user calls the QueueWorkItem, he/she can provide a state object. The state object usually stores specific information, such as arguments, that should be used within the WorkItemCallback delegate.
+The state object life time depends on its contents and the user's application. Sometimes, it is useful to dispose off the state object just after the work item has been completed. Especially if it contains unmanaged resources.
+For this reason, I added a boolean to the SmartThreadPool that indicates to call Dispose on the state object when the work item has been completed. The boolean is initialized when the thread pool is constructed with the STPStartInfo. The Dispose is called only if the state object implements the IDisposable interface. The Dispose is called after the WorkItem has been completed and its PostExecute has run (if a PostExecute exists). The state object is disposed even if the work item has been canceled or the thread pool has been shutdown.
+
+ Note that this feature only applies to the state argument that
+ comes with WorkItemCallback, it doesn't apply to the
+ arguments supplied in Action<...> and Func<...> arguments!!!
+The user can wait for the Smart Thread Pool / Work Items Group
+ to become idle.
+This feature enables the user to wait for a Smart Thread Pool or a Work Items Group
+ to become idle. They become idle when the work items queue is empty and all the
+ threads have completed executing all their work items.
+This is useful in case you want to run a batch of work items and then wait for all of them to complete. It saves you from handling the IWorkItemResult objects in case you just want to wait for all of the work items to complete.
+
+ The SmartThreadPool and WorkItemsGroup classes both implement the IWorkItemsGroup
+ interface which define the WaitForIdle methods.
+
+ To take advantage of this feature, use the IWorkItemsGroup.WaitForIdle() method (Both SmartThreadPool and WorkItemsGroup implement the IWorkItemsGroup interface
+ which define the WaitForIdle methods). It has several overloads which provide a timeout argument. The WaitForIdle() method is not static and should be used on a SmartThreadPool instance.
+The SmartThreadPool always keeps track of how many work items it has. When a new work item is queued, the number is incremented. When a thread completes a work item, the number is decremented. The total number of work items includes the work items in the queue and the work items that the threads are currently working on.
+The WaitForIdle() mechanism works with a private ManualResetEvent. When a work item is queued, the ManualResetEvent is reset (changed to non signaled state). When the work items count becomes zero (initial state of the Smart Thread Pool), the ManualResetEvent is set (changed to signaled state). The WaitForIdle() method just waits for the ManualResetEvent to implement its functionality.
+See the example below.
+The exception handling is changed, so if a work item throws an exception, it is rethrown at GetResult(), rather than firing an UnhandledException event. Note that PostExecute exceptions are always ignored .
+After I did some reading about delegates and their implementation, I decided to change the way the SmartThreadPool treats exceptions. In the previous versions, I used an event driven mechanism. Entities were registered to the SmartThreadPool.UnhandledException event, and when a work item threw an exception, this event was fired. This is the behavior of Toub’s thread pool.
+.NET delegates behave differently. Instead of using an event driven mechanism, it re-throws the exception of the delegated method at the EndInvoke(). Similarly, the SmartThreadPool exception mechanism is changed so that exceptions are no longer fired by the UnhandledException event, but rather re-thrown again when IWorkItemResult.GetResult() is called.
+Note that exceptions slow down .NET and degrade the performance. .NET works faster when no exceptions are thrown at all. For this reason, I added an output parameter to some of the GetResult() overloads, so the exception can be retrieved rather than re-thrown. The work item throws the exception anyway, so re-throwing it will be a waste of time. As a rule of thumb, it is better to use the output parameter than catch the re-thrown exception.
+The GetResult() can be called unlimited number of times and it re-throws the same exception each time.
+Note that PostExecute is called, as and when needed, even if the work item has thrown an exception. Of course, PostExecute implementation should handle exceptions if it calls GetResult().
+Also note that if the PostExecute throws an exception then its exception is ignored.
+See the example below.
+Work items have priority .
+Work items priority enables the user to order work items at run time. Work items are ordered by their priority. High priority is treated first. There are five priorities:
public enum WorkItemPriority
+{
+ Lowest,
+ BelowNormal,
+ Normal,
+ AboveNormal,
+ Highest,
+}
+The default priority is Normal.
+The implementation of priorities is quite simple. Instead of using one queue that keeps the work items sorted inside, I used one queue for each priority. Each queue is a FIFO. When the user enqueues a work item, the work item is added to the queue with a matching priority. When a thread dequeues a work item, it looks for the highest priority queue that is not empty.
+This is the easiest solution to sort the work items.
+The caller thread's HTTP context can be used when the work item is executed .
+This feature improves 6, and was implemented by Steven T. I just replaced my code with that implementation.
+With this feature the Smart Thread Pool can be used with ASP.NET to pass the context of HTTP between the caller thread and the thread in the pool that will execute the work item.
+Work items group .
+This feature enables the user to execute a group of work items specifying the maximum level of concurrency.
+For example, assume that your application uses several resources, the resources
+ are not thread safe so only one thread can use a resource at a time. There are a
+ few solutions to this, from creating one thread that uses all resources to creating
+ a thread per resource. The first solution doesn’t harness the power of parallelism,
+ the later solution is too expensive (many threads) if the resources are idle most
+ of the time.
+
+ The Smart Thread Pool solution is to create a WorkItemsGroup per resource, with
+ concurrency of one. Each time a resource needs to do some work a work item is queued
+ into its WorkItemsGroup. The concurrency of the WorkItemsGroup is one so only one
+ work item can run at a time per resource. The number of threads dynamically changes
+ according to the load of work items.
+
+ Here is a code snippet to show how it works:
+...
+
+// Create a SmartThreadPool
+SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+// Create a work items group that processes
+// one work item at a time for resource 1
+IWorkItemsGroup wigPrinter1 = smartThreadPool.CreateWorkItemsGroup(1);
+
+// Create a work items group that processes
+// one work item at a time for resource 2
+IWorkItemsGroup wigPrinter2 = smartThreadPool.CreateWorkItemsGroup(1);
+
+// Queue work items to resources
+wigPrinter1.QueueWorkItem(Print, printer1, lessons);
+wigPrinter1.QueueWorkItem(Print, printer1, homework);
+wigPrinter2.QueueWorkItem(Print, printer2, blueprints);
+
+...
+
+// Print prototype
+void Print(Printer printer, Document document) {...}
+
+...
+As you can see from the snippet a Work Items Group is attached to an instance of a Smart Thread Pool. The Work Items Group doesn't have threads of its own, but rather uses the threads of the Smart Thread Pool. It also has an interface similar to the Smart Thread Pool, so it can be used in the same way and replaced when needed.
+The WorkItemsGroup has a priority queue (the same as the SmartThreadPool). The queue stores the work items of the WorkItemsGroup. The WorkItemsGroup dequeues the work item with the highest priority at the head of the queue and queues it into the SmartThreadPool with the same priority.
+The WorkItemsGroup is responsible for managing the maximum level of concurrency of its work items. Once a work item is queued into the WorkItemsGroup, it checks how many work items it has in the SmartThreadPool. If this number is less than the maximum level of concurrency, it queues the work item into the SmartThreadPool. If this number is equal (it cannot be greater) then the WorkItemsGroup stores the work item in its own priority queue.
+In case the WorkItemsGroup is created in suspend mode, it will store the work items in its queue until it is started. When it is started it will queue the work items into the SmartThreadPool up to the maximum level of concurrency.
+Note that the WorkItemsGroup only has a maximum level of concurrency and not a minimum or exact value. It is possible to have a concurrency level of 3, and have non work items executing, since they are waiting in the SmartThreadPool queue.
+To accomplish the concurrency level, the WorkItemsGroup registers to the completion event of its work items. The event is used internally and is not exposed to the user. Once registered, the WorkItemsGroup will get an event when its work item is completed. The event will trigger the WorkItemsGroup to queue more work items into the SmartThreadPool. The event is the only way to accomplish the concurrency level. When I tried to do it with PostExecute I got fluctuating WaitForIdle.
+Another advantage of the WorkItemsGroup is that it can cancel all its work items that haven't been executed yet in one method with a complexity of O(1). The WorkItemsGroup does so by attaching an object to each one of its work items that indicates if the WorkItemsGroup has been cancelled. When a work item is about to be executed, it is asked for its current state (InQueue, InProgress, Completed, or Canceled). The final state considers this object's value to know if the work item was cancelled.
+
+ The Work Items Group can also be use as a conjuction point. Say you want to accoplish
+ a task by splitting to a subtasks. Once the subtasks are completed a new task is
+ issued and splitted to subtasks too. This can be achieved by using the OnIdle event
+ of WorkItemsGroup. See conjuction point example .
+
+ See the examples below.
+
+ See WorkItemsGroupDemo demo in the source code solution.
+
+The caller can create thread pools and work items groups in suspended state .
+When a Smart Thread Pool is created, by default, it starts its threads immediately. However, sometimes you need to queue a few work items and only then start executing them.
+In these cases, you can create the Smart Thread Pool and the Work Items Group in a suspended state. When you need to execute the work items, just call the Start() method. The same method exists in the Work Items Group for the same purpose.
+Note that if you create a suspended Work Items Group in a suspended Smart Thread Pool, starting the Work Items Group won't execute the work items until the Smart Thread Pool is started.
+Threads have priority .
+The STPStartInfo contains a property that defines the priority in which the threads are started in the SmartThreadPool. Use it if you know what you are doing. Playing with threads priority may end up with dead locks, live lock, and days locked :-(.
+
+ The MaxThreads/MinThreads/Concurrency can
+ be changed at run time .
+
+ This addition allows the user to control the concurrency of work items execution.
+ It is useful to make the STP adaptable.
+
+ To execute more work items in parallel, increment
+ the concurrency. To limit the number if executed work items and/or lower the CPU usage,
+ decrement the concurrency.
+
+
+ This option is available in the SmartThreadPool and in the WorkItemsGroup with the IWokItemsGroup interface:
+
+public interface IWorkItemsGroup
+{
+ ...
+ int Concurrency { get; set; }
+ ...
+}
+
+The value of Concurrency must be positive.
+
+Although the Concurrency has the same meaning and the same behavior in the SmartThreadPool and in the WorkItemsGroup it’s implemented differently.
+
+The SmartThreadPool’s Concurrency is equivalent to the MaxThreads property. When the Concurrency is incremented, the SmartThreadPool can create more threads to handle its work items up to the Concurrency limit. The creation of thread, in this case, is immediate. The threads are still created as explained in section 2 .
+
+When the Concurrency is decrement, the SmartThreadPool doesn’t create new threads and let existing threads to be terminated in order decrement the number of threads in the thread pool. Note that the lowering the Concurrency may take a while effect, since we need to wait for work items to complete. The SmartThreadPool doesn’t abort a thread actively, but wait passively until it quits.
+
+The WorkItemsGroup’s Concurrency is responsible to how many work items may be handled in parallel in the SmartThreadPool as explained in section 14 . When the Concurrency is incremented more work items are queued to the SmartThreadPool. When the Concurrency is decremented, the WorkItemsGroup stops to queue work items until the number of work items in the SmartThreadPool of this WorkItemsGroup is lower than the WorkItemsGroup’s Concurrency.
+
+In addition the SmartThreadPool also let the MinThreads property to be changed after its creation. When the MinThreads is created the number of threads in the pool is raised so it will be at least MinThreads.
+
+The number of MaxThreads must be greater or equal to MinThreads. If MaxThreads is set to a number lower than MinThreads than MinThreads is also set to the new value of MaxThreads. And vice versa for MinThreads.
+
+ Improved the Cancel behavior .
+
+ (See section 5)
+ Added events for initialization and termination of threads .
+This functionality enables the user to execute code when a thread is created or
+terminated in the thread pool. The code is executed within the thread’s context.
+
+This feature is exposed as new events in the SmartThreadPool class:
+
+// A delegate to call after a thread is created, but before it's first use.
+public delegate void ThreadInitializationHandler();
+
+// A delegate to call when a thread is about to exit, after it is no longer
+// belong to the pool.
+public delegate void ThreadTerminationHandler();
+
+public event ThreadInitializationHandler OnThreadInitialization
+{...}
+
+public event ThreadTerminationHandler OnThreadTermination;
+{...}
+
+
+The OnThreadInitialization event is fired when a thread is created and
+added to the threads pool. The event is called from the created thread.
+ In this event the user should add a code to initialize resources that
+ are used by the thread, and should be initialized once per thread instead
+ of once per work item.
+
+
+
+The OnThreadTermination event is fired when a thread leaves the threads
+pool. The event is called from the terminated thread. In this event the
+user has the opportunity to clean up the resources that were initialized
+earlier in the OnThreadInitialization event.
+
+Added support for Windows CE (limited)
+
+The SmartThreadPool project has a similar project named SmartThreadPoolCE. This
+ version of the SmartThreadPool can be run on Windows CE.
+ It has the same features as the PC version, but it doesn't fully work yet. I
+ still have an issue with the threads scheduling, since the thread idle stuff
+ explained in section 2 doesn't work on Windows CE.
+Added IsIdle flag to the SmartThreadPool and to the IWorkItemsGroup
+This flag enables the user to poll the Smart Thread Pool / Work Items Group for
+ idle.
+Added support for Action<T>
+ and Func<T> (strongly typed work items)
+
+ (See section 3)
+
+
+
+The Smart Thread Pool is good when your work items don't do too much, but wait for events, IOs, sockets, etc. This means that the work items don't use CPU, but run for a long time. It is also good when you don't need to keep alive too many threads in the air all the time. If your work items do a short time work, then use the .NET ThreadPool. If you have a constant heavy load of work, then use Toub's thread pool and define the maximum number of threads accordingly.
+
+
+
+
+Description
+Value name
+Default value
+Smart Thread Pool
+Work Items Group
+
+Idle timeout
+IdleTimeout
+60 seconds
+Used
+Not used
+
+Maximum number of threads
+MaxWorkerThreads
+25
+Used
+Not used
+
+Minimum number of threads
+MinWorkerThreads
+0
+Used
+Not used
+
+Use caller thread call context
+UseCallerCallContext
+false
+Used
+Used
+
+Use caller thread HTTP context
+UseCallerHttpContext
+false
+Used
+Used
+
+Dispose of the state objects
+DisposeOfStateObjects
+false
+Used
+Used
+
+Call to PostExecute
+CallToPostExecute
+CallToPostExecute.Always
+Used
+Used
+
+PostExecute method
+PostExecuteWorkItemCallback
+null (Do nothing)
+Used
+Used
+
+Start suspended
+StartSuspended
+false
+Used
+Used
+
+Fill state with args
+
+ (Action<T> & Func<T>)
+FillStateWithArgs
+false
+Used
+Used
+
+Thread priority in thread pool
+ThreadPriority
+ThreadPriority.Normal
+Used
+Not used
+
+Work item default priority
+WorkItemPriority
+.Normal
+Used
+Used
+Once defined in the construction, they cannot be changed. So, choose their values according to your needs. The minimum number of threads should be proportional to the number of work items that you want to handle at normal times. The maximum number of threads should be proportional to the number of work items that you want to handle at peak times. The idle timeout should be proportional to the peak length time.
+Code examples
+Creating a Smart Thread Pool instance:
SmartThreadPool smartThreadPool =
+ new SmartThreadPool(
+ 10*1000, // Idle timeout in milliseconds
+ 25, // Threads upper limit
+ 5, // Threads lower limit
+ true); // Use caller thread context
+Another way to create an instance:
// Create a STPStartInfo object
+STPStartInfo stpStartInfo = new STPStartInfo();
+
+// Change the defaults of the STPStartInfo object
+stpStartInfo.DisposeOfStateObjects = true;
+
+// Create the SmartThreadPool instance
+SmartThreadPool smartThreadPool =
+ new SmartThreadPool(stpStartInfo);
+Using the Smart Thread Pool:
+The following snippet is a simple example. The user queues a work item and then gets the result. Note that the Result property blocks until a result is available or the work item is cancelled:
public class SimpleExample
+{
+ public void DoWork(int [] numbers)
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ // Queue the work item
+ IWorkItemResult<double> wir = smartThreadPool.QueueWorkItem(new Func<int[], double>(CalcAverage), numbers);
+
+ // Do some other work here
+
+ // Get the result of the operation
+ double average = wir.Result;
+
+ smartThreadPool.Shutdown();
+ }
+
+ // Do the real work
+ private double CalcAverage(int [] numbers)
+ {
+ double average = 0.0;
+
+ // Do the real work here and put
+ // the result in 'result'
+
+ return average;
+ }
+}
+This example shows how you can wait for specific work items to complete. The user queues two work items, waits for both of them to complete, and then gets the results:
public class WaitForAllExample
+{
+ public void DoWork()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ IWorkItemResult wir1 =
+ smartThreadPool.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork1), null);
+
+ IWorkItemResult wir2 =
+ smartThreadPool.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork2), null);
+
+ bool success = SmartThreadPool.WaitAll(
+ new IWorkItemResult [] { wir1, wir2 });
+
+ if (success)
+ {
+ int result1 = (int)wir1.Result;
+ int result2 = (int)wir2.Result;
+ }
+
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomeWork1(object state)
+ {
+ return 1;
+ }
+
+ private object DoSomeWork2(object state)
+ {
+ return 2;
+ }
+}
+This example shows how you can wait for one of the specific work items to complete. The user queues two work items, waits for one of them to complete, and then gets its result:
public class WaitForAnyExample
+{
+ public void DoWork()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ IWorkItemResult wir1 =
+ smartThreadPool.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork1), null);
+
+ IWorkItemResult wir2 =
+ smartThreadPool.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork2), null);
+
+ IWorkItemResult [] wirs =
+ new IWorkItemResult [] { wir1, wir2 };
+
+ int index = SmartThreadPool.WaitAny(wirs);
+
+ if (index != WaitHandle.WaitTimeout)
+ {
+ int result = (int)wirs[index].Result;
+ }
+
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomeWork1(object state)
+ {
+ return 1;
+ }
+
+ private object DoSomeWork2(object state)
+ {
+ return 1;
+ }
+}
+The following example shows the use of WaitForIdle(). We just queue all the work items and then wait for all of them to complete. Note that we ignore the results of the work items:
public class WaitForIdleExample
+{
+ public void DoWork(object [] states)
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ foreach(object state in states)
+ {
+ smartThreadPool.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork), state);
+ }
+
+ // Wait for the completion of all work items
+ smartThreadPool.WaitForIdle();
+
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomeWork(object state)
+ {
+ // Do the work
+ return null;
+ }
+}
+The following example shows how to handle exceptions. Pay attention to the Result property that throws WorkItemResultException and not the real exception:
public class CatchExceptionExample
+{
+ public void DoWork()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ IWorkItemResult<double> wir = smartThreadPool.QueueWorkItem(new Func<double, double, double>(DoDiv), 10.0, 0.0);
+
+ try
+ {
+ double result = wir.Result;
+ }
+ // Catch the exception that Result threw
+ catch (WorkItemResultException e)
+ {
+ // Dump the inner exception which DoDiv threw
+ Debug.WriteLine(e.InnerException);
+ }
+
+ smartThreadPool.Shutdown();
+ }
+
+ private double DoDiv(double x, double y)
+ {
+ return x / y;
+ }
+}
+This is another example that shows how to handle exceptions. It is better than the previous one because it is faster. .NET works fast when everything is OK. When .NET needs to deal with exceptions, it becomes slower:
public class GetExceptionExample
+{
+ public void DoWork()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ IWorkItemResult<double> wir = smartThreadPool.QueueWorkItem(new Func<double, double, double>(DoDiv), 10.0, 0.0);
+
+ Exception e = null;
+ double result = wir.GetResult(out e);
+ // e contains the expetion that DoDiv threw
+ if(null != e)
+ {
+ // Do something with the exception
+ }
+
+ smartThreadPool.Shutdown();
+ }
+
+ private double DoDiv(double x, double y)
+ {
+ return x / y;
+ }
+}
+The next example shows how to create a Work Items Group and use it:
public class WorkItemsGroupExample
+{
+ public void DoWork(object [] states)
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ // Create a work items group that processes
+ // one work item at a time
+ IWorkItemsGroup wig =
+ smartThreadPool.CreateWorkItemsGroup(1);
+
+ // Queue some work items
+ foreach(object state in states)
+ {
+ wig.QueueWorkItem(
+ new WorkItemCallback(this.DoSomeWork), state);
+ }
+
+ // Wait for the completion of all work
+ // items in the work items group
+ wig.WaitForIdle();
+
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomeWork(object state)
+ {
+ // Do the work
+ return null;
+ }
+}
+The next example shows how to create a suspended Smart Thread Pool:
public class SuspendedSTPStartExample
+{
+ public void DoWork(object [] states)
+ {
+ STPStartInfo stpStartInfo = new STPStartInfo();
+ stpStartInfo.StartSuspended = true;
+
+ SmartThreadPool smartThreadPool =
+ new SmartThreadPool(stpStartInfo);
+
+ foreach(object state in states)
+ {
+ smartThreadPool.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork), state);
+ }
+
+ // Start working on the work items in the queue
+ smartThreadPool.Start();
+
+ // Wait for the completion of all work items
+ smartThreadPool.WaitForIdle();
+
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomeWork(object state)
+ {
+ // Do the work
+ return null;
+ }
+}
+The next example shows how to create a suspended Work Items Group:
public class SuspendedWIGStartExample
+{
+ public void DoWork(object [] states)
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ WIGStartInfo wigStartInfo = new WIGStartInfo();
+ wigStartInfo.StartSuspended = true;
+
+ IWorkItemsGroup wig =
+ smartThreadPool.CreateWorkItemsGroup(1, wigStartInfo);
+
+ foreach(object state in states)
+ {
+ wig.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork), state);
+ }
+
+ // Start working on the work items
+ // in the work items group queue
+ wig.Start();
+
+ // Wait for the completion of all work items
+ wig.WaitForIdle();
+
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomeWork(object state)
+ {
+ // Do the work
+ return null;
+ }
+}
+
+ This example shows how to get the Work Items Group's OnIdle event:
+public class OnWIGIdleEventExample
+{
+ public void DoWork(object [] states)
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ IWorkItemsGroup wig =
+ smartThreadPool.CreateWorkItemsGroup(1);
+
+ wig.OnIdle += new WorkItemsGroupIdleHandler(wig_OnIdle);
+
+ foreach(object state in states)
+ {
+ wig.QueueWorkItem(new
+ WorkItemCallback(this.DoSomeWork), state);
+ }
+
+ smartThreadPool.WaitForIdle();
+ smartThreadPool.Shutdown();
+ }
+
+ private object DoSomeWork(object state)
+ {
+ // Do the work
+ return null;
+ }
+
+ private void wig_OnIdle(IWorkItemsGroup workItemsGroup)
+ {
+ Debug.WriteLine("WIG is idle");
+ }
+}
+
+Disclaimer
+THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+7th Aug, 2004: Initial version.
+ 14th Sep, 2004: Bug fixes:
+
+Changed the start thread formula to 'if ((InUseThreads + WaitingCallbacks) > _workerThreads.Count)'.
+ Fixed handle leaks.
+ 16th Oct, 2004: Added a few features:
+
+Work items return result.
+ Supports waiting synchronization for multiple work items.
+ Work items can be cancelled.
+ Passage of the caller thread's context to the thread in the pool.
+ Minimal usage of Win32 handles.
+ Minor bug fixes.
+ 26th Dec, 2004: Changes:
+
+Added PostExecute with options on which cases to call it.
+ Added a WaitForIdle() method that waits until the work items queue is empty.
+ Added option to dispose off the state objects.
+ Updated the FireUnhandledException so it will be more robust.
+ Removed static constructors.
+ Added finalizers.
+ Changed exceptions so that they are serializable.
+ Fixed the bug in one of the SmartThreadPool constructors.
+ Changed SmartThreadPool.WaitAll() so that it will support any number of waiters. The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
+ Changed exception handling so if a work item throws an exception, it is re-thrown at GetResult(), rather than firing an UnhandledException event. Note that PostExecute exceptions are always ignored.
+ 25th Mar, 2005: Changes:
+
+Fixed bug where the work items got lost. It happens especially when the idle timeout is small.
+ 3rd Jul, 2005: Changes:
+
+Fixed bug where Enqueue() throws an exception when PopWaiter() returns null, hardly reconstructed.
+ 16th Aug 2005: Changes:
+
+Fixed bug where the InUseThreads becomes negative while canceling work items.
+ 31st Jan, 2006: Changes:
+
+Added work items priority.
+ Removed support of chained delegates in callbacks and post executes (nobody really uses this).
+ Added work items groups.
+ Added work items group's idle event.
+ Changed SmartThreadPool.WaitAll() behavior so that when it gets an empty array it returns true rather than throwing an exception.
+ Added option to start the Smart Thread Pool and the Work Items Groups as suspended.
+ Exception behavior changed (again). The real exception is returned by an inner exception.
+ Added option to keep the HTTP context of the caller thread. (Thanks to Steven T.).
+ Added performance counters.
+ Added priority to the threads in the pool.
+ 13th Feb, 2006: Changes:
+
+Fixed the demo, so that it won't crash with exception on start.
+ Added a call to dispose off the performance counters so that there is no performance counter leak.
+ Added exception catch in case the performance counters cannot be created.
+
+
+
+ 16th May 2008: Changes
+
+Changed the dispose behavior and removed the Finalizers.
+Enabled the change of the MaxThreads and MinThreads at run time.
+Enabled the change of the Concurrency of a IWorkItemsGroup at run time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency refers to the MaxThreads.
+Improved the cancel behavior.
+Added events for thread creation and termination.
+Fixed the HttpContext context capture.
+Changed internal collections so they use generic collections
+Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup
+Added support for WinCE
+Added support for Action<T> and Func<T>
+
+
+
+
+
+
+
+
+
diff --git a/Docs/WorkItemsGroup.JPG b/Docs/WorkItemsGroup.JPG
new file mode 100644
index 0000000..f6d0fe1
Binary files /dev/null and b/Docs/WorkItemsGroup.JPG differ
diff --git a/STPCEDemo/Form1.Designer.cs b/STPCEDemo/Form1.Designer.cs
new file mode 100644
index 0000000..5fbfa78
--- /dev/null
+++ b/STPCEDemo/Form1.Designer.cs
@@ -0,0 +1,316 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace STPCEDemo
+{
+ partial class Form1
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+ private System.Windows.Forms.MainMenu mainMenu1;
+ private UsageControl.UsageHistoryControl usageHistoryControl1;
+ private Button btnStartStop;
+ private Label label1;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.mainMenu1 = new System.Windows.Forms.MainMenu();
+ this.btnStartStop = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.lblThreadsInUse = new System.Windows.Forms.Label();
+ this.label7 = new System.Windows.Forms.Label();
+ this.lblThreadsInPool = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.spnMinThreads = new System.Windows.Forms.NumericUpDown();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.spnMaxThreads = new System.Windows.Forms.NumericUpDown();
+ this.spnIdleTimeout = new System.Windows.Forms.NumericUpDown();
+ this.spnWorkItemsPerSecond = new System.Windows.Forms.NumericUpDown();
+ this.label6 = new System.Windows.Forms.Label();
+ this.spnWorkItemDuration = new System.Windows.Forms.NumericUpDown();
+ this.label8 = new System.Windows.Forms.Label();
+ this.usageControl1 = new UsageControl.UsageControl();
+ this.usageHistoryControl1 = new UsageControl.UsageHistoryControl();
+ this.SuspendLayout();
+ //
+ // btnStartStop
+ //
+ this.btnStartStop.Location = new System.Drawing.Point(148, 10);
+ this.btnStartStop.Name = "btnStartStop";
+ this.btnStartStop.Size = new System.Drawing.Size(72, 20);
+ this.btnStartStop.TabIndex = 0;
+ this.btnStartStop.Text = "Start";
+ this.btnStartStop.Click += new System.EventHandler(this.btnStartStop_Click);
+ //
+ // label1
+ //
+ this.label1.Font = new System.Drawing.Font("Tahoma", 10F, System.Drawing.FontStyle.Bold);
+ this.label1.Location = new System.Drawing.Point(10, 10);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(143, 20);
+ this.label1.Text = "Smart Thread Pool";
+ //
+ // lblThreadsInUse
+ //
+ this.lblThreadsInUse.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.lblThreadsInUse.Location = new System.Drawing.Point(148, 106);
+ this.lblThreadsInUse.Name = "lblThreadsInUse";
+ this.lblThreadsInUse.Size = new System.Drawing.Size(72, 24);
+ this.lblThreadsInUse.Text = "0";
+ this.lblThreadsInUse.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+ //
+ // label7
+ //
+ this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.label7.Location = new System.Drawing.Point(10, 106);
+ this.label7.Name = "label7";
+ this.label7.Size = new System.Drawing.Size(124, 24);
+ this.label7.Text = "Used threads (Green)";
+ //
+ // lblThreadsInPool
+ //
+ this.lblThreadsInPool.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.lblThreadsInPool.Location = new System.Drawing.Point(148, 88);
+ this.lblThreadsInPool.Name = "lblThreadsInPool";
+ this.lblThreadsInPool.Size = new System.Drawing.Size(72, 24);
+ this.lblThreadsInPool.Text = "0";
+ this.lblThreadsInPool.TextAlign = System.Drawing.ContentAlignment.TopCenter;
+ //
+ // label2
+ //
+ this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.label2.Location = new System.Drawing.Point(10, 88);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(124, 24);
+ this.label2.Text = "Threads in pool (Red)";
+ //
+ // spnMinThreads
+ //
+ this.spnMinThreads.Location = new System.Drawing.Point(148, 125);
+ this.spnMinThreads.Maximum = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ this.spnMinThreads.Name = "spnMinThreads";
+ this.spnMinThreads.Size = new System.Drawing.Size(72, 24);
+ this.spnMinThreads.TabIndex = 3;
+ this.spnMinThreads.ValueChanged += new System.EventHandler(this.spnMinThreads_ValueChanged);
+ //
+ // label4
+ //
+ this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.label4.Location = new System.Drawing.Point(10, 173);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(120, 24);
+ this.label4.Text = "Idle timeout (Seconds)";
+ //
+ // label3
+ //
+ this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.label3.Location = new System.Drawing.Point(10, 149);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(104, 24);
+ this.label3.Text = "Maximum Threads";
+ //
+ // label5
+ //
+ this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.label5.Location = new System.Drawing.Point(10, 125);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(104, 24);
+ this.label5.Text = "Minimum Threads";
+ //
+ // spnMaxThreads
+ //
+ this.spnMaxThreads.Location = new System.Drawing.Point(148, 149);
+ this.spnMaxThreads.Maximum = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ this.spnMaxThreads.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spnMaxThreads.Name = "spnMaxThreads";
+ this.spnMaxThreads.Size = new System.Drawing.Size(72, 24);
+ this.spnMaxThreads.TabIndex = 4;
+ this.spnMaxThreads.Value = new decimal(new int[] {
+ 5,
+ 0,
+ 0,
+ 0});
+ this.spnMaxThreads.ValueChanged += new System.EventHandler(this.spnMaxThreads_ValueChanged);
+ //
+ // spnIdleTimeout
+ //
+ this.spnIdleTimeout.Location = new System.Drawing.Point(148, 173);
+ this.spnIdleTimeout.Maximum = new decimal(new int[] {
+ 60,
+ 0,
+ 0,
+ 0});
+ this.spnIdleTimeout.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spnIdleTimeout.Name = "spnIdleTimeout";
+ this.spnIdleTimeout.Size = new System.Drawing.Size(72, 24);
+ this.spnIdleTimeout.TabIndex = 5;
+ this.spnIdleTimeout.Value = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ //
+ // spnWorkItemsPerSecond
+ //
+ this.spnWorkItemsPerSecond.Location = new System.Drawing.Point(148, 203);
+ this.spnWorkItemsPerSecond.Name = "spnWorkItemsPerSecond";
+ this.spnWorkItemsPerSecond.Size = new System.Drawing.Size(72, 24);
+ this.spnWorkItemsPerSecond.TabIndex = 6;
+ this.spnWorkItemsPerSecond.Value = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ //
+ // label6
+ //
+ this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.label6.Location = new System.Drawing.Point(10, 203);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(120, 24);
+ this.label6.Text = "Work items/sec";
+ //
+ // spnWorkItemDuration
+ //
+ this.spnWorkItemDuration.Increment = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ this.spnWorkItemDuration.Location = new System.Drawing.Point(148, 227);
+ this.spnWorkItemDuration.Maximum = new decimal(new int[] {
+ 6000,
+ 0,
+ 0,
+ 0});
+ this.spnWorkItemDuration.Name = "spnWorkItemDuration";
+ this.spnWorkItemDuration.Size = new System.Drawing.Size(72, 24);
+ this.spnWorkItemDuration.TabIndex = 7;
+ this.spnWorkItemDuration.Value = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ //
+ // label8
+ //
+ this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular);
+ this.label8.Location = new System.Drawing.Point(10, 227);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(132, 24);
+ this.label8.Text = "Work item duration (ms)";
+ //
+ // usageControl1
+ //
+ this.usageControl1.BackColor = System.Drawing.Color.Black;
+ this.usageControl1.Location = new System.Drawing.Point(10, 33);
+ this.usageControl1.Maximum = 10;
+ this.usageControl1.Name = "usageControl1";
+ this.usageControl1.Size = new System.Drawing.Size(41, 52);
+ this.usageControl1.TabIndex = 1;
+ this.usageControl1.Value1 = 0;
+ this.usageControl1.Value2 = 0;
+ //
+ // usageHistoryControl1
+ //
+ this.usageHistoryControl1.BackColor = System.Drawing.Color.Black;
+ this.usageHistoryControl1.Location = new System.Drawing.Point(66, 33);
+ this.usageHistoryControl1.Maximum = 10;
+ this.usageHistoryControl1.Name = "usageHistoryControl1";
+ this.usageHistoryControl1.Size = new System.Drawing.Size(154, 52);
+ this.usageHistoryControl1.TabIndex = 2;
+ //
+ // Form1
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.AutoScroll = true;
+ this.ClientSize = new System.Drawing.Size(240, 258);
+ this.Controls.Add(this.btnStartStop);
+ this.Controls.Add(this.spnWorkItemDuration);
+ this.Controls.Add(this.label8);
+ this.Controls.Add(this.spnWorkItemsPerSecond);
+ this.Controls.Add(this.label6);
+ this.Controls.Add(this.spnIdleTimeout);
+ this.Controls.Add(this.spnMaxThreads);
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.label5);
+ this.Controls.Add(this.spnMinThreads);
+ this.Controls.Add(this.lblThreadsInUse);
+ this.Controls.Add(this.label7);
+ this.Controls.Add(this.lblThreadsInPool);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.usageControl1);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.usageHistoryControl1);
+ this.Menu = this.mainMenu1;
+ this.Name = "Form1";
+ this.Text = "Smart Thread Pool";
+ this.Load += new System.EventHandler(this.Form1_Load);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private UsageControl.UsageControl usageControl1;
+ private Label lblThreadsInUse;
+ private Label label7;
+ private Label lblThreadsInPool;
+ private Label label2;
+ private NumericUpDown spnMinThreads;
+ private Label label4;
+ private Label label3;
+ private Label label5;
+ private NumericUpDown spnMaxThreads;
+ private NumericUpDown spnIdleTimeout;
+ private NumericUpDown spnWorkItemsPerSecond;
+ private Label label6;
+ private NumericUpDown spnWorkItemDuration;
+ private Label label8;
+
+
+ }
+}
+
diff --git a/STPCEDemo/Form1.cs b/STPCEDemo/Form1.cs
new file mode 100644
index 0000000..04e7e5c
--- /dev/null
+++ b/STPCEDemo/Form1.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows.Forms;
+using Amib.Threading;
+using System.Threading;
+
+namespace STPCEDemo
+{
+ public partial class Form1 : Form
+ {
+ private bool running;
+ private readonly System.Windows.Forms.Timer uiTimer;
+ private SmartThreadPool _stp;
+ private readonly System.Windows.Forms.Timer wiTimer;
+
+ public Form1()
+ {
+ running = false;
+ InitializeComponent();
+
+ uiTimer = new System.Windows.Forms.Timer();
+ uiTimer.Interval = 1000;
+ uiTimer.Tick += new EventHandler(uiTimer_Tick);
+
+ wiTimer = new System.Windows.Forms.Timer();
+ wiTimer.Interval = 1000;
+ wiTimer.Tick += new EventHandler(wiTimer_Tick);
+ //Debug.WriteLine("Form Thread Priority is " + (int)GetCurrentThreadPriority());
+ }
+
+ private void Form1_Load(object sender, EventArgs e)
+ {
+ SetRunningState(false);
+ }
+
+ void uiTimer_Tick(object sender, EventArgs e)
+ {
+ if (null == _stp)
+ {
+ return;
+ }
+ try
+ {
+ int inUse = _stp.InUseThreads;
+ int inPool = _stp.ActiveThreads;
+
+ this.usageHistoryControl1.AddValues(inUse, inPool);
+ this.usageControl1.Value1 = inUse;
+ this.usageControl1.Value2 = inPool;
+ this.lblThreadsInUse.Text = inUse.ToString();
+ this.lblThreadsInPool.Text = inPool.ToString();
+ }
+ catch(Exception ex)
+ {
+ Debug.WriteLine(ex);
+ return;
+ }
+ }
+
+ void wiTimer_Tick(object sender, EventArgs e)
+ {
+ int count = Convert.ToInt32(spnWorkItemsPerSecond.Value);
+ int sleepDuration = Convert.ToInt32(spnWorkItemDuration.Value);
+ Debug.WriteLine(string.Format("{0}: C = {1}, S = {2}", DateTime.Now.ToString("HH:mm:ss"), count, sleepDuration));
+ for (int i = 0; i < count; i++)
+ {
+ _stp.QueueWorkItem(DoWork, sleepDuration);
+ }
+ }
+
+ private void DoWork(int sleepDuration)
+ {
+ //Debug.WriteLine("DoWork Thread Priority is " + (int)GetCurrentThreadPriority());
+
+ DateTime start = DateTime.Now;
+ //int sleepDuration = Convert.ToInt32(state);
+ Thread.Sleep(sleepDuration);
+ TimeSpan duration = DateTime.Now - start;
+
+ Debug.WriteLine(string.Format("{0}: Duration = {1}", DateTime.Now.ToString("HH:mm:ss"), duration.TotalMilliseconds));
+
+ //return null;
+ }
+
+ void btnStartStop_Click(object sender, EventArgs e)
+ {
+ SetRunningState(!running);
+ }
+
+ private void SetRunningState(bool newRunningState)
+ {
+ btnStartStop.Text = newRunningState ? "Stop" : "Start";
+ if (newRunningState)
+ {
+ _stp = new SmartThreadPool(
+ Convert.ToInt32(spnIdleTimeout.Value) * 1000,
+ Convert.ToInt32(spnMaxThreads.Value),
+ Convert.ToInt32(spnMinThreads.Value));
+ }
+ else
+ {
+ if (null != _stp)
+ {
+ _stp.Shutdown();
+ }
+ _stp = null;
+ }
+ spnIdleTimeout.Enabled = !newRunningState;
+ uiTimer.Enabled = newRunningState;
+ wiTimer.Enabled = newRunningState;
+ running = newRunningState;
+ }
+
+ private void spnMinThreads_ValueChanged(object sender, EventArgs e)
+ {
+ if (null != _stp)
+ {
+ _stp.MinThreads = Convert.ToInt32(spnMinThreads.Value);
+ }
+ }
+
+ private void spnMaxThreads_ValueChanged(object sender, EventArgs e)
+ {
+ if (null != _stp)
+ {
+ _stp.MaxThreads = Convert.ToInt32(spnMaxThreads.Value);
+ }
+ }
+
+ private static int GetCurrentThreadPriority()
+ {
+ IntPtr hThread = GetCurrentThread();
+ int priority = CeGetThreadPriority(hThread);
+ return priority;
+ }
+
+ [DllImport("coredll.dll", SetLastError = true)]
+ public static extern int CeGetThreadPriority(IntPtr hThread);
+
+ [DllImport("coredll.dll")]
+ public static extern IntPtr GetCurrentThread();
+ }
+}
\ No newline at end of file
diff --git a/STPCEDemo/Form1.resx b/STPCEDemo/Form1.resx
new file mode 100644
index 0000000..d04e463
--- /dev/null
+++ b/STPCEDemo/Form1.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ WEBPAD
+
+
+ False
+
+
\ No newline at end of file
diff --git a/STPCEDemo/Pens.cs b/STPCEDemo/Pens.cs
new file mode 100644
index 0000000..28b31eb
--- /dev/null
+++ b/STPCEDemo/Pens.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Text;
+
+namespace STPCEDemo
+{
+ public static class Pens
+ {
+ private static Pen _red = new Pen(Color.Red);
+ private static Pen _lawnGreen = new Pen(Color.LawnGreen);
+
+ private static Pen _green = new Pen(Color.Green);
+ private static Pen _darkGray = new Pen(Color.DarkGray);
+ private static Pen _white = new Pen(Color.White);
+
+
+ public static Pen Red
+ {
+ get { return _red; }
+ }
+
+
+ public static Pen LawnGreen
+ {
+ get { return _lawnGreen; }
+ }
+
+ public static Pen Green
+ {
+ get { return _green; }
+ }
+
+ public static Pen DarkGray
+ {
+ get { return _darkGray; }
+ }
+
+ public static Pen White
+ {
+ get { return _white; }
+ }
+ }
+}
diff --git a/STPCEDemo/Program.cs b/STPCEDemo/Program.cs
new file mode 100644
index 0000000..e0a9177
--- /dev/null
+++ b/STPCEDemo/Program.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace STPCEDemo
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [MTAThread]
+ static void Main()
+ {
+ Application.Run(new Form1());
+ }
+ }
+}
\ No newline at end of file
diff --git a/STPCEDemo/Properties/AssemblyInfo.cs b/STPCEDemo/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..776e74b
--- /dev/null
+++ b/STPCEDemo/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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("STPCEDemo")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("STPCEDemo")]
+[assembly: AssemblyCopyright("Copyright © 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a9f7d57c-9b87-4b5e-81a5-9d4b2719b5b5")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+
diff --git a/STPCEDemo/Properties/Resources.Designer.cs b/STPCEDemo/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..fce6210
--- /dev/null
+++ b/STPCEDemo/Properties/Resources.Designer.cs
@@ -0,0 +1,60 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.1433
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace STPCEDemo.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("STPCEDemo.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/STPCEDemo/Properties/Resources.resx b/STPCEDemo/Properties/Resources.resx
new file mode 100644
index 0000000..27c69f6
--- /dev/null
+++ b/STPCEDemo/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/STPCEDemo/STPCEDemo.csproj b/STPCEDemo/STPCEDemo.csproj
new file mode 100644
index 0000000..77f8a8c
--- /dev/null
+++ b/STPCEDemo/STPCEDemo.csproj
@@ -0,0 +1,115 @@
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {356114BA-5F10-4DB6-9971-987CE11F765F}
+ WinExe
+ Properties
+ STPCEDemo
+ STPCEDemo
+ {4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ WindowsCE
+ E2BECB1F-8C8C-41ba-B736-9BE7D946A398
+ 5.0
+ STPCEDemo
+ v2.0
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE;$(PlatformFamilyName)
+ true
+ true
+ prompt
+ 512
+ 4
+ Off
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;$(PlatformFamilyName)
+ true
+ true
+ prompt
+ 512
+ 4
+ Off
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ Form1.cs
+
+
+
+
+
+ Designer
+ Form1.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ UsageControl.cs
+ Designer
+
+
+ UsageHistoryControl.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ UserControl
+
+
+ UserControl
+
+
+
+
+ {D81DD596-C71F-4AC2-816C-63C19589E7E0}
+ SmartThreadPoolCE
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/STPCEDemo/UsageControl.cs b/STPCEDemo/UsageControl.cs
new file mode 100644
index 0000000..d2f7b1b
--- /dev/null
+++ b/STPCEDemo/UsageControl.cs
@@ -0,0 +1,328 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Data;
+using System.Windows.Forms;
+using STPCEDemo;
+
+namespace UsageControl
+{
+ ///
+ /// Summary description for UsageControl.
+ ///
+ public class UsageControl : System.Windows.Forms.UserControl
+ {
+ private System.ComponentModel.IContainer components = null;
+
+ private const int ovalWidth = 18;
+ private const int columns = 2;
+
+ private int fixedWidth;
+ private int rows = 1;
+
+ private int min = 0; // Minimum value for progress range
+ private int max = 100; // Maximum value for progress range
+ private int val1 = 0; // Current progress
+ private int val2 = 0; // Current progress
+
+ public UsageControl()
+ {
+ // This call is required by the Windows.Forms Form Designer.
+ InitializeComponent();
+
+ EnableDoubleBuffering();
+ fixedWidth = 2 + (ovalWidth+1)*columns + 1;
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ private void EnableDoubleBuffering()
+ {
+ // Set the value of the double-buffering style bits to true.
+ //this.SetStyle(ControlStyles.DoubleBuffer |
+ // ControlStyles.UserPaint |
+ // ControlStyles.AllPaintingInWmPaint,
+ // true);
+ //this.UpdateStyles();
+ }
+
+ #region Component Designer generated code
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ //
+ // UsageControl
+ //
+ this.BackColor = System.Drawing.Color.Black;
+ this.Name = "UsageControl";
+ this.Size = new System.Drawing.Size(192, 160);
+
+ }
+ #endregion
+
+ protected override void OnResize(EventArgs e)
+ {
+ Width = fixedWidth;
+ rows = (ClientRectangle.Height / 4) - 1;
+ // Invalidate the control to get a repaint.
+ this.Invalidate();
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ Graphics g = e.Graphics;
+
+ int percent1 = ((val1 - min) * 100) / (max - min);
+ int percent2 = ((val2 - min) * 100) / (max - min);
+
+ int filledCount1 = (rows * percent1) / 100;
+ int filledCount2 = (rows * percent2) / 100;
+ int diff = Math.Abs(filledCount1 - filledCount2);
+
+ int emptyCount = rows - Math.Max(filledCount1, filledCount2);
+
+ int i = 0;
+
+ for(i = 0; i < filledCount1; ++i)
+ {
+ for(int j = 0; j < columns; ++j)
+ {
+ DrawOval(
+ g,
+ Pens.LawnGreen,
+ 2 + j*(1+ovalWidth),
+ ClientRectangle.Bottom - 5 - i*4);
+ }
+ }
+
+ for(; i < filledCount2; ++i)
+ {
+ for(int j = 0; j < columns; ++j)
+ {
+ DrawOval(
+ g,
+ Pens.Red,
+ 2 + j*(1+ovalWidth),
+ ClientRectangle.Bottom - 5 - i*4);
+ }
+ }
+
+ for(; i < rows; ++i)
+ {
+ for(int j = 0; j < columns; ++j)
+ {
+ DrawOval(
+ g,
+ Pens.Green,
+ 2 + j*(1+ovalWidth),
+ ClientRectangle.Bottom - 5 - i*4);
+ }
+ }
+
+ // Draw a three-dimensional border around the control.
+ Draw3DBorder(g);
+
+ // Clean up.
+ //g.Dispose();
+ base.OnPaint (e);
+ }
+
+ private void DrawOval(Graphics g, Pen pen, int x, int y)
+ {
+ g.DrawLine(pen, x+1 , y, x+ovalWidth-2, y);
+ g.DrawLine(pen, x, y+1, x+ovalWidth-1, y+1);
+ g.DrawLine(pen, x+1, y+2, x+ovalWidth-2, y+2);
+ }
+
+ public int Maximum
+ {
+ get
+ {
+ return max;
+ }
+
+ set
+ {
+ // Make sure that the maximum value is never set lower than the minimum value.
+ if (value < min)
+ {
+ min = value;
+ }
+
+ max = value;
+
+ // Make sure that value is still in range.
+ if (val1 > max)
+ {
+ val1 = max;
+ }
+
+ // Invalidate the control to get a repaint.
+ this.Invalidate();
+ }
+ }
+
+ public int Value1
+ {
+ get
+ {
+ return val1;
+ }
+
+ set
+ {
+ int oldValue = val1;
+
+ // Make sure that the value does not stray outside the valid range.
+ if (value < min)
+ {
+ val1 = min;
+ }
+ else if (value > max)
+ {
+ val1 = max;
+ }
+ else
+ {
+ val1 = value;
+ }
+ Invalidate();
+/*
+ // Invalidate only the changed area.
+ float percent;
+
+ Rectangle newValueRect = this.ClientRectangle;
+ Rectangle oldValueRect = this.ClientRectangle;
+
+ // Use a new value to calculate the rectangle for progress.
+ percent = (float)(val1 - min) / (float)(max - min);
+ newValueRect.Width = (int)((float)newValueRect.Width * percent);
+
+ // Use an old value to calculate the rectangle for progress.
+ percent = (float)(oldValue - min) / (float)(max - min);
+ oldValueRect.Width = (int)((float)oldValueRect.Width * percent);
+
+ Rectangle updateRect = new Rectangle();
+
+ // Find only the part of the screen that must be updated.
+ if (newValueRect.Width > oldValueRect.Width)
+ {
+ updateRect.X = oldValueRect.Size.Width;
+ updateRect.Width = newValueRect.Width - oldValueRect.Width;
+ }
+ else
+ {
+ updateRect.X = newValueRect.Size.Width;
+ updateRect.Width = oldValueRect.Width - newValueRect.Width;
+ }
+
+ updateRect.Height = this.Height;
+
+ // Invalidate the intersection region only.
+ this.Invalidate(updateRect);
+*/
+ }
+ }
+
+ public int Value2
+ {
+ get
+ {
+ return val2;
+ }
+
+ set
+ {
+ int oldValue = val2;
+
+ // Make sure that the value does not stray outside the valid range.
+ if (value < min)
+ {
+ val2 = min;
+ }
+ else if (value > max)
+ {
+ val2 = max;
+ }
+ else
+ {
+ val2 = value;
+ }
+ Invalidate();
+
+ /*
+ // Invalidate only the changed area.
+ float percent;
+
+ Rectangle newValueRect = this.ClientRectangle;
+ Rectangle oldValueRect = this.ClientRectangle;
+
+ // Use a new value to calculate the rectangle for progress.
+ percent = (float)(val1 - min) / (float)(max - min);
+ newValueRect.Width = (int)((float)newValueRect.Width * percent);
+
+ // Use an old value to calculate the rectangle for progress.
+ percent = (float)(oldValue - min) / (float)(max - min);
+ oldValueRect.Width = (int)((float)oldValueRect.Width * percent);
+
+ Rectangle updateRect = new Rectangle();
+
+ // Find only the part of the screen that must be updated.
+ if (newValueRect.Width > oldValueRect.Width)
+ {
+ updateRect.X = oldValueRect.Size.Width;
+ updateRect.Width = newValueRect.Width - oldValueRect.Width;
+ }
+ else
+ {
+ updateRect.X = newValueRect.Size.Width;
+ updateRect.Width = oldValueRect.Width - newValueRect.Width;
+ }
+
+ updateRect.Height = this.Height;
+
+ // Invalidate the intersection region only.
+ this.Invalidate(updateRect);
+ */
+ }
+ }
+
+ private void Draw3DBorder(Graphics g)
+ {
+ int PenWidth = (int)Pens.White.Width;
+
+ g.DrawLine(Pens.DarkGray,
+ this.ClientRectangle.Left, this.ClientRectangle.Top,
+ this.ClientRectangle.Width - PenWidth, this.ClientRectangle.Top);
+ g.DrawLine(Pens.DarkGray,
+ this.ClientRectangle.Left, this.ClientRectangle.Top,
+ this.ClientRectangle.Left, this.ClientRectangle.Height - PenWidth);
+ g.DrawLine(Pens.White,
+ this.ClientRectangle.Left, this.ClientRectangle.Height - PenWidth,
+ this.ClientRectangle.Width - PenWidth, this.ClientRectangle.Height - PenWidth);
+ g.DrawLine(Pens.White,
+ this.ClientRectangle.Width - PenWidth, this.ClientRectangle.Top,
+ this.ClientRectangle.Width - PenWidth, this.ClientRectangle.Height - PenWidth);
+ }
+
+
+ }
+}
diff --git a/STPCEDemo/UsageControl.resx b/STPCEDemo/UsageControl.resx
new file mode 100644
index 0000000..e35e03a
--- /dev/null
+++ b/STPCEDemo/UsageControl.resx
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ 80
+
+
+ (Default)
+
+
+ False
+
+
+ UsageControl
+
+
+ Private
+
+
+ 8, 8
+
+
\ No newline at end of file
diff --git a/STPCEDemo/UsageHistoryControl.cs b/STPCEDemo/UsageHistoryControl.cs
new file mode 100644
index 0000000..99d2a0c
--- /dev/null
+++ b/STPCEDemo/UsageHistoryControl.cs
@@ -0,0 +1,192 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+using STPCEDemo;
+
+namespace UsageControl
+{
+ ///
+ /// Summary description for UsageHistoryControl.
+ ///
+ public class UsageHistoryControl : System.Windows.Forms.UserControl
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.Container components = null;
+
+ private const int squareWidth = 12;
+ private const int maxLastValuesCount = 2000;
+ private const int shiftStep = 3;
+
+ private int shift = 0;
+
+ private int [] lastValues1 = new int[maxLastValuesCount];
+ private int [] lastValues2 = new int[maxLastValuesCount];
+ private int nextValueIndex;
+ private int lastValuesCount;
+
+ private int max = 100;
+ private int min = 0;
+
+ public UsageHistoryControl()
+ {
+ // This call is required by the Windows.Forms Form Designer.
+ InitializeComponent();
+
+ EnableDoubleBuffering();
+ Reset();
+ }
+
+ public void Reset()
+ {
+ lastValues1.Initialize();
+ lastValues2.Initialize();
+ nextValueIndex = 0;
+ lastValuesCount = 0;
+ Invalidate();
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Component Designer generated code
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ //
+ // UsageHistoryControl
+ //
+ this.BackColor = System.Drawing.Color.Black;
+ this.Name = "UsageHistoryControl";
+
+ }
+ #endregion
+
+ private void EnableDoubleBuffering()
+ {
+ // Set the value of the double-buffering style bits to true.
+ //this.SetStyle(ControlStyles.DoubleBuffer |
+ // ControlStyles.UserPaint |
+ // ControlStyles.AllPaintingInWmPaint,
+ // true);
+ //this.UpdateStyles();
+ }
+
+ protected override void OnResize(EventArgs e)
+ {
+ // Invalidate the control to get a repaint.
+ this.Invalidate();
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ Graphics g = e.Graphics;
+
+ for(int i = 0; i <= ClientRectangle.Width+squareWidth; i += squareWidth)
+ {
+ g.DrawLine(Pens.Green, i-shift, 0, i-shift, ClientRectangle.Height);
+ }
+
+ for(int i = 0; i < ClientRectangle.Height; i += squareWidth)
+ {
+ g.DrawLine(Pens.Green, 0, i, ClientRectangle.Width, i);
+ }
+
+ int startValueIndex = (nextValueIndex-1+maxLastValuesCount)%maxLastValuesCount;
+
+ int prevVal1 = GetRelativeValue(lastValues1[startValueIndex]);
+ int prevVal2 = GetRelativeValue(lastValues2[startValueIndex]);
+
+ for(int i = 1; i < lastValuesCount; ++i)
+ {
+ int index = nextValueIndex - 1 - i;
+ if (index < 0)
+ {
+ index += maxLastValuesCount;
+ }
+
+ int val1 = GetRelativeValue(lastValues1[index]);
+ int val2 = GetRelativeValue(lastValues2[index]);
+
+ g.DrawLine(
+ Pens.Red,
+ ClientRectangle.Width-(i-1)*shiftStep, ClientRectangle.Height-prevVal2,
+ ClientRectangle.Width-i*shiftStep, ClientRectangle.Height-val2);
+
+ g.DrawLine(
+ Pens.LawnGreen,
+ ClientRectangle.Width-(i-1)*shiftStep, ClientRectangle.Height-prevVal1,
+ ClientRectangle.Width-i*shiftStep, ClientRectangle.Height-val1);
+
+ prevVal1 = val1;
+ prevVal2 = val2;
+ }
+ }
+
+ private int GetRelativeValue(int val)
+ {
+ int result = val * (ClientRectangle.Height-2) / max + 1;
+ return result;
+ }
+
+ public void AddValues(int val1, int val2)
+ {
+ lastValues1[nextValueIndex] = val1;
+ lastValues2[nextValueIndex] = val2;
+
+ nextValueIndex++;
+ nextValueIndex %= maxLastValuesCount;
+ lastValuesCount++;
+ if (lastValuesCount > maxLastValuesCount)
+ {
+ lastValuesCount = maxLastValuesCount;
+ }
+
+ shift += shiftStep;
+ shift %= squareWidth;
+ Invalidate();
+ }
+
+ public int Maximum
+ {
+ get
+ {
+ return max;
+ }
+
+ set
+ {
+ // Make sure that the maximum value is never set lower than the minimum value.
+ if (value < min)
+ {
+ min = value;
+ }
+
+ max = value;
+
+ // Invalidate the control to get a repaint.
+ this.Invalidate();
+ }
+ }
+
+ }
+}
diff --git a/STPCEDemo/UsageHistoryControl.resx b/STPCEDemo/UsageHistoryControl.resx
new file mode 100644
index 0000000..487ccbc
--- /dev/null
+++ b/STPCEDemo/UsageHistoryControl.resx
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ UsageHistoryControl
+
+
+ 80
+
+
+ (Default)
+
+
+ False
+
+
+ Private
+
+
+ 8, 8
+
+
\ No newline at end of file
diff --git a/STPExamples/CatchExceptionExample.cs b/STPExamples/CatchExceptionExample.cs
index 19f5019..8445dd0 100644
--- a/STPExamples/CatchExceptionExample.cs
+++ b/STPExamples/CatchExceptionExample.cs
@@ -6,42 +6,29 @@ namespace Examples
{
public class CatchExceptionExample
{
- private class DivArgs
- {
- public int x;
- public int y;
- }
+ public void DoWork()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
- public void DoWork()
- {
- SmartThreadPool smartThreadPool = new SmartThreadPool();
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(new Func(DoDiv), 10.0, 0.0);
- DivArgs divArgs = new DivArgs();
- divArgs.x = 10;
- divArgs.y = 0;
+ try
+ {
+ double result = wir.Result;
+ }
+ // Catch the exception that Result threw
+ catch (WorkItemResultException e)
+ {
+ // Dump the inner exception which DoDiv threw
+ Debug.WriteLine(e.InnerException);
+ }
- IWorkItemResult wir =
- smartThreadPool.QueueWorkItem(new
- WorkItemCallback(this.DoDiv), divArgs);
+ smartThreadPool.Shutdown();
+ }
- try
- {
- int result = (int)wir.Result;
- }
- // Catch the exception that Result threw
- catch (WorkItemResultException e)
- {
- // Dump the inner exception which DoDiv threw
- Debug.WriteLine(e.InnerException);
- }
-
- smartThreadPool.Shutdown();
- }
-
- private object DoDiv(object state)
- {
- DivArgs divArgs = (DivArgs)state;
- return (divArgs.x / divArgs.y);
- }
- }
+ private double DoDiv(double x, double y)
+ {
+ return x / y;
+ }
+ }
}
diff --git a/STPExamples/CooperativeCancelExample.cs b/STPExamples/CooperativeCancelExample.cs
new file mode 100644
index 0000000..f4481fe
--- /dev/null
+++ b/STPExamples/CooperativeCancelExample.cs
@@ -0,0 +1,45 @@
+using System.Threading;
+using Amib.Threading;
+
+namespace Examples
+{
+ public class CooperativeCancelExample
+ {
+ public void DoWork(object state)
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ // Queue the work item
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(DoRealWork);
+
+ // Give the work item some time to complete.
+ Thread.Sleep(1000);
+
+ // If the work item hasn't completed yet then cancel it.
+ if (!wir.IsCompleted)
+ {
+ wir.Cancel();
+ }
+
+ smartThreadPool.Shutdown();
+ }
+
+ // Do some lengthy work
+private void DoRealWork()
+{
+ // Do something here.
+
+ // Sample SmartThreadPool.IsWorkItemCanceled
+ if (SmartThreadPool.IsWorkItemCanceled)
+ {
+ return;
+ }
+
+ // Sample the SmartThreadPool.IsWorkItemCanceled in a loop
+ while (!SmartThreadPool.IsWorkItemCanceled)
+ {
+ // Do some real work here
+ }
+}
+ }
+}
diff --git a/STPExamples/ParallelQuickSort.cs b/STPExamples/ParallelQuickSort.cs
new file mode 100644
index 0000000..5629776
--- /dev/null
+++ b/STPExamples/ParallelQuickSort.cs
@@ -0,0 +1,108 @@
+using Amib.Threading;
+
+namespace STPExamples
+{
+ ///
+ /// This is a Parallel QuickSort example.
+ /// It shows how to use the QueueWorkItem with Action methods.
+ ///
+ public class ParallelQuickSort
+ {
+ public static void Main()
+ {
+ // Generate an array
+ int[] array = GenerateArray(100);
+
+ // Create a Smart Thread Pool
+ SmartThreadPool stp = new SmartThreadPool();
+
+ // Use the Smart Thread Pool to parallel the sort
+ QuickSort(stp, array);
+ }
+
+ ///
+ /// QuickSort array using wig to parallel the sort
+ ///
+ /// A IWorkItemsGroup to use to parallel the sort
+ /// The array of items to sort
+ public static void QuickSort(IWorkItemsGroup wig, int[] array)
+ {
+ // Initiate the QuickSort
+ wig.QueueWorkItem(QuickSort, wig, array, 0, array.Length - 1);
+
+ // Wait for the sort to complete.
+ wig.WaitForIdle();
+ }
+
+ ///
+ /// Sort a subarray in array, starting with the left item and ending in the right item.
+ /// The method uses the IWorkItemsGroup wig to parallel the sort.
+ ///
+ /// A IWorkItemsGroup to use to parallel the sort
+ /// The array of items to sort
+ /// The left index in the subarray
+ /// The right index in the subarray
+ private static void QuickSort(IWorkItemsGroup wig, int[] array, int left, int right)
+ {
+ if (right > left)
+ {
+ int pivotIndex = left;
+ int pivotNewIndex = Partition(array, left, right, pivotIndex);
+
+ wig.QueueWorkItem(QuickSort, wig, array, left, pivotNewIndex - 1);
+ wig.QueueWorkItem(QuickSort, wig, array, pivotNewIndex + 1, right);
+ }
+ }
+
+ ///
+ /// Partition a subarray of array
+ /// (This is part of the QuickSort algorithm)
+ ///
+ ///
+ private static int Partition(int[] array, int left, int right, int pivotIndex)
+ {
+ int pivotValue = array[pivotIndex];
+ Swap(array, pivotIndex, right);
+ int storeIndex = left;
+
+ for (int i = left; i < right; i++)
+ {
+ if (array[i] <= pivotValue)
+ {
+ Swap(array, i, storeIndex);
+ ++storeIndex;
+ }
+ }
+ Swap(array, storeIndex, right);
+ return storeIndex;
+ }
+
+ ///
+ /// Swap between two item in the array
+ ///
+ /// Array of integers
+ /// First index
+ /// Second index
+ private static void Swap(int[] array, int index1, int index2)
+ {
+ int temp = array[index1];
+ array[index1] = array[index2];
+ array[index2] = temp;
+ }
+
+ ///
+ /// Generate an array with the numbers 0-count ordered in reverse
+ ///
+ /// The number of items in the array
+ /// Returns the generated array
+ private static int[] GenerateArray(int count)
+ {
+ int[] array = new int[count];
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i] = array.Length - i;
+ }
+ return array;
+ }
+ }
+}
diff --git a/STPExamples/STPExamples.csproj b/STPExamples/STPExamples.csproj
index 3a81549..b1e153a 100644
--- a/STPExamples/STPExamples.csproj
+++ b/STPExamples/STPExamples.csproj
@@ -16,11 +16,10 @@
Grid
IE50
false
- Library
+ Exe
STPExamples
OnBuildSuccess
-
-
+ STPExamples.ParallelQuickSort
@@ -72,6 +71,16 @@
none
prompt
+
+ bin\ReleaseCE\
+ TRACE
+ 285212672
+ true
+
+
+ AnyCPU
+ prompt
+
System
@@ -79,6 +88,10 @@
System.Data
+
+ False
+ ..\..\..\..\Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\CompactFramework\2.0\v2.0\WindowsCE\System.Drawing.dll
+
System.XML
@@ -96,12 +109,14 @@
Code
+
Code
Code
+
Code
@@ -114,6 +129,7 @@
Code
+
Code
diff --git a/STPExamples/SimpleExample.cs b/STPExamples/SimpleExample.cs
index da303ab..6da6272 100644
--- a/STPExamples/SimpleExample.cs
+++ b/STPExamples/SimpleExample.cs
@@ -1,35 +1,34 @@
+using System;
using Amib.Threading;
namespace Examples
{
public class SimpleExample
{
- public void DoWork(object state)
- {
- SmartThreadPool smartThreadPool = new SmartThreadPool();
+ public void DoWork(int[] numbers)
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
- // Queue the work item
- IWorkItemResult wir =
- smartThreadPool.QueueWorkItem(
- new WorkItemCallback(this.DoRealWork),
- state);
+ // Queue the work item
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(new Func(CalcAverage), numbers);
- // Do some other work here
+ // Do some other work here
- // Get the result of the operation
- object result = wir.Result;
+ // Get the result of the operation
+ double average = wir.Result;
- smartThreadPool.Shutdown();
- }
+ smartThreadPool.Shutdown();
+ }
- // Do the real work
- private object DoRealWork(object state)
- {
- object result = null;
+ // Do the real work
+ private double CalcAverage(int[] numbers)
+ {
+ double average = 0.0;
- // Do the real work here and put the result in 'result'
+ // Do the real work here and put
+ // the result in 'result'
- return result;
- }
- }
+ return average;
+ }
+ }
}
diff --git a/STPExamples/ThreadEventsExample.cs b/STPExamples/ThreadEventsExample.cs
new file mode 100644
index 0000000..1385bcd
--- /dev/null
+++ b/STPExamples/ThreadEventsExample.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Amib.Threading;
+
+namespace STPExamples
+{
+ public class MyResource : IDisposable
+ {
+ // ...
+
+ public void DoIt()
+ {
+ //...
+ }
+
+ public void Dispose()
+ {
+ //...
+ }
+ }
+
+ public class ThreadEventsExample
+ {
+ public static void Main()
+ {
+
+ SmartThreadPool stp = new SmartThreadPool();
+ stp.OnThreadInitialization += new ThreadInitializationHandler(OnInitialization);
+ stp.OnThreadTermination += new ThreadTerminationHandler(OnTermination);
+
+ stp.QueueWorkItem(DoSomeWork);
+ }
+
+ [ThreadStatic]
+ private static MyResource _resource;
+
+ public static void OnInitialization()
+ {
+ // Initialize the resource
+ _resource = new MyResource();
+ }
+
+ private static object DoSomeWork(object state)
+ {
+ // Use the resouce
+ _resource.DoIt();
+
+ return null;
+ }
+
+ public static void OnTermination()
+ {
+ // Do resource cleanup
+ _resource.Dispose();
+ }
+ }
+}
diff --git a/STPTests/QueueWorkItemHelper.cs b/STPTests/QueueWorkItemHelper.cs
new file mode 100644
index 0000000..93fcebe
--- /dev/null
+++ b/STPTests/QueueWorkItemHelper.cs
@@ -0,0 +1,693 @@
+using System.Threading;
+
+using NUnit.Framework;
+
+using Amib.Threading;
+using System.Reflection;
+
+#pragma warning disable 168
+
+namespace SmartThreadPoolTests
+{
+ public static class QueueWorkItemHelper
+ {
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback);
+ public static void TestQueueWorkItemCall(IWorkItemsGroup wig)
+ {
+ WorkItemInfo wii = new WorkItemInfo();
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii);
+
+ IWorkItemResult wir = wig.QueueWorkItem(wiic.CompareWorkItemInfo);
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
+ public static void TestQueueWorkItemCallPrio(IWorkItemsGroup wig)
+ {
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.WorkItemPriority = WorkItemPriority.AboveNormal;
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii);
+
+ IWorkItemResult wir = wig.QueueWorkItem((WorkItemCallback)wiic.CompareWorkItemInfo, WorkItemPriority.AboveNormal);
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
+ public static void TestQueueWorkItemCallStat(IWorkItemsGroup wig)
+ {
+ object state = new object();
+ WorkItemInfo wii = new WorkItemInfo();
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii, state);
+
+ IWorkItemResult wir = wig.QueueWorkItem((WorkItemCallback) wiic.CompareWorkItemInfo, state);
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
+ public static void TestQueueWorkItemCallStatPrio(IWorkItemsGroup wig)
+ {
+ object state = new object();
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.WorkItemPriority = WorkItemPriority.AboveNormal;
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii, state);
+
+ IWorkItemResult wir = wig.QueueWorkItem(wiic.CompareWorkItemInfo, state, WorkItemPriority.AboveNormal);
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
+ public static void TestQueueWorkItemCallStatPost(IWorkItemsGroup wig)
+ {
+ bool postExecuteCalled = false;
+ object state = new object();
+ PostExecuteWorkItemCallback postExecuteWorkItemCallback = delegate(IWorkItemResult w) { postExecuteCalled = true; };
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii, state);
+
+ IWorkItemResult wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsTrue(postExecuteCalled);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
+ public static void TestQueueWorkItemCallStatPostPrio(IWorkItemsGroup wig)
+ {
+ bool postExecuteCalled = false;
+ object state = new object();
+ PostExecuteWorkItemCallback postExecuteWorkItemCallback = delegate(IWorkItemResult w) { postExecuteCalled = true; };
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.WorkItemPriority = WorkItemPriority.BelowNormal;
+ wii.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii, state);
+
+ IWorkItemResult wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ WorkItemPriority.BelowNormal);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsTrue(postExecuteCalled);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
+ public static void TestQueueWorkItemCallStatPostPflg(IWorkItemsGroup wig)
+ {
+ bool postExecuteCalled;
+ CallToPostExecute callToPostExecute;
+ object state = new object();
+ PostExecuteWorkItemCallback postExecuteWorkItemCallback = delegate(IWorkItemResult w) { postExecuteCalled = true; };
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii, state);
+ IWorkItemResult wir;
+ bool success;
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.Always;
+
+ // Check without cancel
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsTrue(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsTrue(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.Never;
+
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsFalse(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsFalse(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.WhenWorkItemNotCanceled;
+
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsTrue(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsFalse(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.WhenWorkItemCanceled;
+
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsFalse(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsTrue(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
+ public static void TestQueueWorkItemCallStatPostPflgPrio(IWorkItemsGroup wig)
+ {
+ bool postExecuteCalled;
+ CallToPostExecute callToPostExecute;
+ object state = new object();
+ PostExecuteWorkItemCallback postExecuteWorkItemCallback = delegate(IWorkItemResult w) { postExecuteCalled = true; };
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii, state);
+ WorkItemPriority workItemPriority;
+ IWorkItemResult wir;
+ bool success;
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.Always;
+ workItemPriority = WorkItemPriority.Lowest;
+
+ // Check without cancel
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+ wii.WorkItemPriority = workItemPriority;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsTrue(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsTrue(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.Never;
+ workItemPriority = WorkItemPriority.Highest;
+
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+ wii.WorkItemPriority = workItemPriority;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsFalse(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsFalse(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.WhenWorkItemNotCanceled;
+ workItemPriority = WorkItemPriority.AboveNormal;
+
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+ wii.WorkItemPriority = workItemPriority;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsTrue(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsFalse(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ callToPostExecute = CallToPostExecute.WhenWorkItemCanceled;
+ workItemPriority = WorkItemPriority.BelowNormal;
+
+ postExecuteCalled = false;
+ wiic.SleepTime = 0;
+
+ wii.CallToPostExecute = callToPostExecute;
+ wii.WorkItemPriority = workItemPriority;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ Assert.IsFalse(postExecuteCalled);
+
+ // Check with cancel
+ success = false;
+ postExecuteCalled = false;
+ wiic.SleepTime = 100;
+
+ wir = wig.QueueWorkItem(
+ wiic.CompareWorkItemInfo,
+ state,
+ postExecuteWorkItemCallback,
+ callToPostExecute,
+ workItemPriority);
+
+ wir.Cancel();
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ Assert.IsTrue(postExecuteCalled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ success = true;
+ }
+
+ Assert.IsTrue(success);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
+ public static void TestQueueWorkItemInfoCall(IWorkItemsGroup wig)
+ {
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.CallToPostExecute = CallToPostExecute.Never;
+ wii.DisposeOfStateObjects = true;
+ wii.PostExecuteWorkItemCallback = delegate(IWorkItemResult w) { };
+ wii.UseCallerCallContext = true;
+ wii.UseCallerHttpContext = true;
+ wii.WorkItemPriority = WorkItemPriority.Highest;
+
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii);
+
+ IWorkItemResult wir = wig.QueueWorkItem(wii, wiic.CompareWorkItemInfo);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
+ public static void TestQueueWorkItemInfoCallStat(IWorkItemsGroup wig)
+ {
+ object state = new object();
+ WorkItemInfo wii = new WorkItemInfo();
+ wii.CallToPostExecute = CallToPostExecute.Never;
+ wii.DisposeOfStateObjects = true;
+ wii.PostExecuteWorkItemCallback = delegate(IWorkItemResult w) { };
+ wii.UseCallerCallContext = true;
+ wii.UseCallerHttpContext = true;
+ wii.WorkItemPriority = WorkItemPriority.Highest;
+
+ WorkItemInfoComparer wiic = new WorkItemInfoComparer(wii, state);
+
+ IWorkItemResult wir = wig.QueueWorkItem(wii, wiic.CompareWorkItemInfo, state);
+
+ // We must wait for idle to let the post execute run
+ wig.WaitForIdle();
+
+ bool success = (bool)wir.Result;
+
+ Assert.IsTrue(success);
+ }
+
+ private static WorkItemInfo GetCurrentWorkItemInfo()
+ {
+ object threadEntry = typeof(SmartThreadPool).GetField("_threadEntry", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
+ object workitem = threadEntry.GetType().GetField("_currentWorkItem", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(threadEntry);
+ WorkItemInfo wii = (WorkItemInfo)workitem.GetType().GetField("_workItemInfo", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(workitem);
+ return wii;
+ }
+
+ private class WorkItemInfoComparer
+ {
+ private WorkItemInfo _neededWorkItemInfo;
+ private object _state;
+ private int _sleepTime = 0;
+
+ public int SleepTime
+ {
+ get { return _sleepTime; }
+ set { _sleepTime = value; }
+ }
+
+ public WorkItemInfoComparer(WorkItemInfo workItemInfo)
+ {
+ _neededWorkItemInfo = workItemInfo;
+ _state = null;
+ }
+
+ public WorkItemInfoComparer(WorkItemInfo workItemInfo, object state)
+ {
+ _neededWorkItemInfo = workItemInfo;
+ _state = state;
+ }
+
+ public object CompareWorkItemInfo(object state)
+ {
+ bool equals = object.Equals(_state, state);
+ if (equals)
+ {
+ WorkItemInfo currentWorkItemInfo = GetCurrentWorkItemInfo();
+ equals = CompareWorkItemInfo(currentWorkItemInfo, _neededWorkItemInfo);
+ }
+ if (_sleepTime > 0)
+ {
+ Thread.Sleep(_sleepTime);
+ }
+
+ return equals;
+ }
+
+ private bool CompareWorkItemInfo(WorkItemInfo wii1, WorkItemInfo wii2)
+ {
+ bool equal = true;
+ equal = equal && (wii1.CallToPostExecute == wii2.CallToPostExecute);
+ equal = equal && (wii1.DisposeOfStateObjects == wii2.DisposeOfStateObjects);
+ equal = equal && (wii1.PostExecuteWorkItemCallback == wii2.PostExecuteWorkItemCallback);
+ equal = equal && (wii1.UseCallerCallContext == wii2.UseCallerCallContext);
+ equal = equal && (wii1.UseCallerHttpContext == wii2.UseCallerHttpContext);
+ equal = equal && (wii1.WorkItemPriority == wii2.WorkItemPriority);
+
+ return equal;
+ }
+ }
+ }
+}
diff --git a/STPTests/STPTests.csproj b/STPTests/STPTests.csproj
index 6942a30..c0671b4 100644
--- a/STPTests/STPTests.csproj
+++ b/STPTests/STPTests.csproj
@@ -60,7 +60,7 @@
TRACE
- false
+ true
4096
false
@@ -70,11 +70,21 @@
false
false
4
- none
+ full
+ prompt
+
+
+ true
+ bin\ReleaseCE\
+ TRACE
+ 285212672
+ true
+ full
+ AnyCPU
prompt
-
+
System
@@ -94,12 +104,26 @@
Code
+
+
+
+
+
+
+
+
+
+
+
+
Code
Code
+
+
Code
diff --git a/STPTests/TestActionT.cs b/STPTests/TestActionT.cs
new file mode 100644
index 0000000..dc76396
--- /dev/null
+++ b/STPTests/TestActionT.cs
@@ -0,0 +1,124 @@
+using Amib.Threading;
+using NUnit.Framework;
+
+namespace SmartThreadPoolTests
+{
+ ///
+ /// Summary description for TestCancel.
+ ///
+ [TestFixture]
+ [Category("TestActionT")]
+ public class TestActionT
+ {
+ private SmartThreadPool _stp;
+ private object _result;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void ActionT0()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _stp.QueueWorkItem(MaxInt);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, int.MaxValue);
+ }
+
+ [Test]
+ public void ActionT1()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _stp.QueueWorkItem(Not, true);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, false);
+ }
+
+ [Test]
+ public void ActionT2()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _stp.QueueWorkItem(Concat, "ABC", "xyz");
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, "ABCxyz");
+ }
+
+ [Test]
+ public void ActionT3()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _stp.QueueWorkItem(Substring, "ABCDEF", 1, 2);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, "BC");
+ }
+
+ [Test]
+ public void ActionT4()
+ {
+ _result = null;
+
+ int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ IWorkItemResult wir = _stp.QueueWorkItem(Subarray, numbers, 1, 2, 3);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, new int[] { 2, 3, 2, 3, 2, 3, });
+ }
+
+ private void MaxInt()
+ {
+ _result = int.MaxValue;
+ }
+
+ private void Not(bool flag)
+ {
+ _result = !flag;
+ }
+
+ private void Concat(string s1, string s2)
+ {
+ _result = s1 + s2;
+ }
+
+ private void Substring(string s, int startIndex, int length)
+ {
+ _result = s.Substring(startIndex, length);
+ }
+
+ private void Subarray(int[] numbers, int startIndex, int length, int repeat)
+ {
+ int[] result = new int[length * repeat];
+ for (int i = 0; i < repeat; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ result[i * length + j] = numbers[startIndex + j];
+ }
+ }
+
+ _result = result;
+ }
+ }
+}
diff --git a/STPTests/TestCancel.cs b/STPTests/TestCancel.cs
new file mode 100644
index 0000000..dfc3fa3
--- /dev/null
+++ b/STPTests/TestCancel.cs
@@ -0,0 +1,806 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Amib.Threading;
+
+namespace SmartThreadPoolTests
+{
+ ///
+ /// Summary description for TestCancel.
+ ///
+ [TestFixture]
+ [Category("TestCancel")]
+ public class TestCancel
+ {
+ ///
+ /// 1. Create STP in suspended mode
+ /// 2. Queue work item into the STP
+ /// 3. Cancel the work item
+ /// 4. Work item's GetResult should throw WorkItemCancelException
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void CancelInQueueWorkItem()
+ {
+ STPStartInfo stpStartInfo = new STPStartInfo();
+ stpStartInfo.StartSuspended = true;
+
+ SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+ IWorkItemResult wir = stp.QueueWorkItem(delegate(object state) { return null; });
+
+ wir.Cancel();
+
+ Assert.IsTrue(wir.IsCanceled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ finally
+ {
+ stp.Shutdown();
+ }
+ }
+
+ ///
+ /// 1. Create STP
+ /// 2. Queue work item that takes some time
+ /// 3. Wait for it to start
+ /// 4. Cancel the work item (soft)
+ /// 5. Work item's GetResult should throw WorkItemCancelException
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void CancelInProgressWorkItemSoft()
+ {
+ ManualResetEvent waitToStart = new ManualResetEvent(false);
+
+ SmartThreadPool stp = new SmartThreadPool();
+ IWorkItemResult wir = stp.QueueWorkItem(
+ delegate(object state) { waitToStart.Set(); Thread.Sleep(100); return null; }
+ );
+
+ waitToStart.WaitOne();
+
+ wir.Cancel(false);
+
+ Assert.IsTrue(wir.IsCanceled);
+
+ try
+ {
+ wir.GetResult();
+ }
+ finally
+ {
+ stp.Shutdown();
+ }
+ }
+
+ ///
+ /// 1. Create STP
+ /// 2. Queue work item that:
+ /// a. Sleep for 0.1 seconds
+ /// b. Increment the counter
+ /// 3. Wait for the work item to start
+ /// 4. Cancel the work item (abort)
+ /// 5. Make sure the work item result indicates the work item has been cancelled.
+ /// 6. Make sure the counter incrementation didn't happen
+ /// 7. Work item's GetResult should throw WorkItemCancelException
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void CancelInProgressWorkItemAbort()
+ {
+ ManualResetEvent waitToStart = new ManualResetEvent(false);
+ int counter = 0;
+
+ SmartThreadPool stp = new SmartThreadPool();
+ IWorkItemResult wir = stp.QueueWorkItem(
+ delegate(object state) { waitToStart.Set() ; Thread.Sleep(100); ++counter; return null; }
+ );
+
+ waitToStart.WaitOne();
+
+ wir.Cancel(true);
+
+ Assert.IsTrue(wir.IsCanceled);
+
+ Assert.AreEqual(counter, 0);
+
+ try
+ {
+ wir.GetResult();
+ }
+ finally
+ {
+ stp.Shutdown();
+ }
+ }
+
+ ///
+ /// 1. Create STP
+ /// 2. Queue work item that takes some time
+ /// 3. Wait for it to start
+ /// 4. Cancel the work item (soft)
+ /// 5. Make sure, in the work item, that SmartThreadPool.IsWorkItemCanceled is true
+ /// 5. Wait for the STP to get idle
+ /// 6. Work item's GetResult should throw WorkItemCancelException
+ ///
+ [Test]
+ public void CancelInProgressWorkItemSoftWithSample()
+ {
+ bool cancelled = false;
+ ManualResetEvent waitToStart = new ManualResetEvent(false);
+ ManualResetEvent waitToComplete = new ManualResetEvent(false);
+
+ SmartThreadPool stp = new SmartThreadPool();
+ IWorkItemResult wir = stp.QueueWorkItem(
+ delegate(object state) {
+ waitToStart.Set();
+ Thread.Sleep(100);
+ cancelled = SmartThreadPool.IsWorkItemCanceled;
+ waitToComplete.Set();
+ return null;
+ }
+ );
+
+ waitToStart.WaitOne();
+
+ wir.Cancel(false);
+
+ waitToComplete.WaitOne();
+
+ Assert.IsTrue(cancelled);
+
+ stp.Shutdown();
+ }
+ ///
+ /// 1. Create STP in suspended mode
+ /// 2. Queue work item into the STP
+ /// 3. Cancel the work item
+ /// 4. Start the STP
+ /// 5. Wait for the STP to get idle
+ /// 6. Work item's GetResult should throw WorkItemCancelException
+ /// 7. Cancel the work item again
+ /// 8. Work item's GetResult should throw WorkItemCancelException
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void CancelCanceledWorkItem()
+ {
+ STPStartInfo stpStartInfo = new STPStartInfo();
+ stpStartInfo.StartSuspended = true;
+
+ SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+ IWorkItemResult wir = stp.QueueWorkItem(delegate(object state) { return null; });
+
+ int counter = 0;
+
+ wir.Cancel();
+
+ try
+ {
+ wir.GetResult();
+ }
+ catch (WorkItemCancelException ce)
+ {
+ ce.GetHashCode();
+ ++counter;
+ }
+
+ Assert.AreEqual(counter, 1);
+
+ wir.Cancel();
+
+ try
+ {
+ wir.GetResult();
+ }
+ finally
+ {
+ stp.Shutdown();
+ }
+ }
+
+ ///
+ /// 1. Create STP
+ /// 2. Queue work item into the STP
+ /// 3. Wait for the STP to get idle
+ /// 4. Work item's GetResult should return value
+ /// 4. Cancel the work item
+ /// 5. Work item's GetResult should return value
+ ///
+ [Test]
+ public void CancelCompletedWorkItem()
+ {
+ SmartThreadPool stp = new SmartThreadPool();
+ IWorkItemResult wir = stp.QueueWorkItem(
+ delegate(object state) { return 1; }
+ );
+
+ stp.WaitForIdle();
+
+ Assert.AreEqual(wir.GetResult(), 1);
+
+ wir.Cancel();
+
+ Assert.AreEqual(wir.GetResult(), 1);
+
+ stp.Shutdown();
+ }
+
+ ///
+ /// 1. Zero counter
+ /// 2. Create STP
+ /// 3. Queue 10 work items, that sleep and then increment the counter, into the STP
+ /// 4. Cancel the STP
+ /// 5. Make sure the counter is still zero
+ ///
+ [Test]
+ public void CancelSTPWorkItems()
+ {
+ // I don't use lock on the counter, since any number above 0 is a failure.
+ // In the worst case counter will be equal to 1 which is still not 0.
+ int counter = 0;
+
+ SmartThreadPool stp = new SmartThreadPool();
+
+ for (int i = 0; i < 10; i++)
+ {
+ stp.QueueWorkItem(
+ delegate(object state) { Thread.Sleep(500); ++counter; return null; }
+ );
+ }
+
+ Thread.Sleep(100);
+
+ stp.Cancel(true);
+
+ Assert.AreEqual(counter, 0);
+
+ stp.Shutdown();
+ }
+
+ ///
+ /// 1. Zero counter
+ /// 2. Create STP
+ /// 3. Create a WIG
+ /// 4. Queue 10 work items, that sleep and then increment the counter, into the WIG
+ /// 5. Cancel the WIG
+ /// 6. Wait for the WIG to become idle
+ /// 7. Make sure the counter is still zero
+ ///
+ [Test]
+ public void CancelWIGWorkItems()
+ {
+ // I don't use lock on the counter, since any number above 0 is a failure.
+ // In the worst case counter will be equal to 1 which is still not 0.
+ int counter = 0;
+
+ SmartThreadPool stp = new SmartThreadPool();
+ IWorkItemsGroup wig = stp.CreateWorkItemsGroup(10);
+
+ for (int i = 0; i < 10; i++)
+ {
+ wig.QueueWorkItem(
+ delegate(object state) { Thread.Sleep(500); ++counter; return null; }
+ );
+ }
+
+ Thread.Sleep(100);
+
+ wig.Cancel(true);
+
+ Assert.AreEqual(counter, 0);
+
+ stp.Shutdown();
+ }
+
+ ///
+ /// 1. Zero global counter
+ /// 2. Create STP
+ /// 3. Create a WIG1 in suspended mode
+ /// 4. Create a WIG2 in suspended mode
+ /// 5. Queue 5 work items, that increment the global counter, into the WIG1
+ /// 6. Queue 7 work items, that increment the global counter, into the WIG2
+ /// 7. Cancel the WIG1
+ /// 8. Start the WIG1
+ /// 9. Start the WIG2
+ /// 10. Wait for the STP to get idle
+ /// 11. Make sure the global counter is 7
+ ///
+ [Test]
+ public void Cancel1WIGof2WorkItems()
+ {
+ int counter1 = 0;
+ int counter2 = 0;
+
+ SmartThreadPool stp = new SmartThreadPool();
+ IWorkItemsGroup wig1 = stp.CreateWorkItemsGroup(3);
+ IWorkItemsGroup wig2 = stp.CreateWorkItemsGroup(3);
+
+ for (int i = 0; i < 3; i++)
+ {
+ wig1.QueueWorkItem(
+ delegate(object state) { Interlocked.Increment(ref counter1); Thread.Sleep(500); Interlocked.Increment(ref counter1); return null; }
+ );
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ wig2.QueueWorkItem(
+ delegate(object state) { Thread.Sleep(500); Interlocked.Increment(ref counter2); return null; }
+ );
+ }
+
+ while (counter1 < 3)
+ {
+ Thread.Sleep(1);
+ }
+ wig1.Cancel(true);
+
+ stp.WaitForIdle();
+
+ Assert.AreEqual(3, counter1, "Cancelled WIG1");
+ Assert.AreEqual(3, counter2, "Normal WIG2");
+
+ stp.Shutdown();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////
+/*
+ private int _counter;
+
+ ///
+ /// Example of how to queue a work item and then cancel it while it is in the queue.
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void WorkItemCanceling()
+ {
+ // Create a SmartThreadPool with only one thread.
+ // It just to show how to use the work item canceling feature
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 1);
+
+ // Queue a work item that will occupy the thread in the pool
+ IWorkItemResult wir1 =
+ smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+
+ // Queue another work item that will wait for the first to complete
+ IWorkItemResult wir2 =
+ smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+
+ // Wait a while for the thread pool to start executing the first work item
+ Thread.Sleep(100);
+
+ try
+ {
+ // The first work item cannot be canceled since it is currently executing
+ if (!wir1.Cancel())
+ {
+ // Cancel the second work item while it still in the queue
+ if (wir2.Cancel())
+ {
+ // Retreiving result of a canceled work item throws WorkItemCancelException exception
+ wir2.GetResult();
+ }
+ }
+ }
+ finally
+ {
+ smartThreadPool.Shutdown();
+ }
+ }
+
+ ///
+ ///
+ [Test]
+ public void WorkItemCancelingAndInUseWorkerThreads()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 10);
+
+ IWorkItemResult [] wirs = new IWorkItemResult[100];
+ for(int i = 0; i < 100; ++i)
+ {
+ wirs[i] = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ }
+
+ // Wait a while for the thread pool to start executing the first work item
+ Thread.Sleep(100);
+
+ for(int i = 0; i < 100; ++i)
+ {
+ wirs[i].Cancel();
+ }
+
+ smartThreadPool.WaitForIdle(2000);
+
+ int inUseThreads = smartThreadPool.InUseThreads;
+
+ smartThreadPool.Shutdown();
+
+ Assert.AreEqual(0, inUseThreads);
+ }
+
+ private object DoSomeWork(object state)
+ {
+ Thread.Sleep(1000);
+ return 1;
+ }
+
+ ///
+ /// Check within the work item if it was cancelled
+ ///
+ [Test]
+ public void SampleIfWorkItemCancelled()
+ {
+ _counter = 0;
+ STPStartInfo stpStartInfo = new STPStartInfo();
+ stpStartInfo.StartSuspended = true;
+
+ // Create a SmartThreadPool with only one thread.
+ SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo);
+
+ IWorkItemResult[] wirs = new IWorkItemResult[100];
+ for (int i = 0; i < 100; ++i)
+ {
+ wirs[i] = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancelledWorkItem), null);
+ }
+
+ for (int i = 0; i < 50; ++i)
+ {
+ wirs[i].Cancel();
+ }
+
+ smartThreadPool.Start();
+
+ smartThreadPool.WaitForIdle();
+
+ smartThreadPool.Shutdown();
+
+ Assert.AreEqual(50, _counter);
+ }
+
+
+ ///
+ /// 1. Create STP
+ /// 2. Queue work item into the STP
+ /// 4. Cancel the work item
+ /// 5. Work item doesn't check for cancel
+ /// 6. Work item quits
+ /// 7. Make sure the work item result is ok, and not an exception
+ ///
+ [Test]
+ public void TestWorkItemCancelledWorkItemOK()
+ {
+ // Create a SmartThreadPool with only one thread.
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ AutoResetEvent start = new AutoResetEvent(false);
+
+ // Queue the work item
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoWaitForCancel), start);
+
+ // Wait for it to start executing
+ bool success = start.WaitOne(3000, false);
+
+ // Make sure it was started
+ Assert.IsTrue(success);
+
+ // Cancel the work item
+ wir.Cancel();
+
+ // Let it complete
+ start.Set();
+
+ // Wait for the work item to complete
+ smartThreadPool.WaitForIdle();
+
+ // Check the work item's result
+ // The work item started running after it was start its execution.
+ // Since the work item didn't sample the cancel, it was not aware that it
+ // was canceled. Therefore the result should be what the work item returned
+ // and not the cancel exeception
+ int result = (int)wir.GetResult(0, false);
+
+ smartThreadPool.Shutdown();
+
+ Assert.AreEqual(1, result);
+ }
+
+
+ ///
+ /// 1. Create STP
+ /// 2. Create WIG
+ /// 3. Queue work item into the WIG
+ /// 4. Cancel the work items in the WIG
+ /// 5. Work item doesn't check for cancel
+ /// 6. Work item quits
+ /// 7. Make sure the work item result is ok, and not an exception
+ ///
+ [Test]
+ public void TestWIGCancelledWorkItemOK()
+ {
+ // Create a SmartThreadPool with only one thread.
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ IWorkItemsGroup wig = smartThreadPool.CreateWorkItemsGroup(1);
+
+ AutoResetEvent start = new AutoResetEvent(false);
+
+ // Queue the work item
+ IWorkItemResult wir = wig.QueueWorkItem(new WorkItemCallback(this.DoWaitForCancel), start);
+
+ // Wait for it to start executing
+ bool success = start.WaitOne(3000, false);
+
+ // Make sure it was started
+ Assert.IsTrue(success);
+
+ // Cancel the work item
+ wig.Cancel();
+
+ // Let it complete
+ start.Set();
+
+ // Wait for the work item to complete
+ smartThreadPool.WaitForIdle();
+
+ // Check the work item's result
+ // The work item started running after it was start its execution.
+ // Since the work item didn't sample the cancel, it was not aware that it
+ // was canceled. Therefore the result should be what the work item returned
+ // and not the cancel exeception
+ int result = (int)wir.GetResult(0, false);
+
+ smartThreadPool.Shutdown();
+
+ Assert.AreEqual(1, result);
+ }
+
+
+ ///
+ /// 1. Create STP
+ /// 2. Queue work item into the STP
+ /// 3. Cancel the work items in the STP
+ /// 4. Work item doesn't check for cancel
+ /// 5. Work item quits
+ /// 6. Make sure the work item result is ok, and not an exception
+ ///
+ [Test]
+ public void TestSTPCancelledWorkItemOK()
+ {
+ // Create a SmartThreadPool with only one thread.
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ AutoResetEvent start = new AutoResetEvent(false);
+
+ // Queue the work item
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoWaitForCancel), start);
+
+ // Wait for it to start executing
+ bool success = start.WaitOne(3000, false);
+
+ // Make sure it was started
+ Assert.IsTrue(success);
+
+ // Cancel the work item
+ smartThreadPool.Cancel();
+
+ // Let it complete
+ start.Set();
+
+ // Wait for the work item to complete
+ smartThreadPool.WaitForIdle();
+
+ // Check the work item's result
+ // The work item started running after it was start its execution.
+ // Since the work item didn't sample the cancel, it was not aware that it
+ // was canceled. Therefore the result should be what the work item returned
+ // and not the cancel exeception
+ int result = (int)wir.GetResult(0, false);
+
+ smartThreadPool.Shutdown();
+
+ Assert.AreEqual(1, result);
+ }
+
+
+ ///
+ /// 1. Create STP
+ /// 2. Queue work item
+ /// 3. Cancel the work item
+ /// 4. Work item checks the cancel and quits
+ /// 5. Make sure the work item result throws exception
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void TestWorkItemCanceledWorkItemCancelException()
+ {
+ // Create a SmartThreadPool with only one thread.
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ AutoResetEvent start = new AutoResetEvent(false);
+
+ // Queue the work item
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancel), start);
+
+ // Wait for it to start executing
+ bool success = start.WaitOne(3000, false);
+
+ // Make sure it was started
+ Assert.IsTrue(success);
+
+ // Cancel the work item
+ wir.Cancel();
+
+ // Let it complete
+ start.Set();
+
+ // Wait for the work item to complete
+ smartThreadPool.WaitForIdle();
+
+ try
+ {
+ // Check the work item's result
+ // The work item started running after it was start its execution.
+ // The work item samples the cancel, therefore it is aware that it
+ // was canceled. Using the GetResult should throw the cancel exeception
+ wir.GetResult(0, false);
+ }
+ finally
+ {
+ smartThreadPool.Shutdown();
+ }
+ }
+
+ ///
+ /// 1. Create STP
+ /// 2. Create WIG
+ /// 3. Queue work item into the WIG
+ /// 4. Cancel the work items in the WIG
+ /// 5. Work item checks the cancel and quits
+ /// 6. Make sure the work item result throws exception
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void TestWIGCancelledWorkItemCancelException()
+ {
+ // Create a SmartThreadPool with only one thread.
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ IWorkItemsGroup wig = smartThreadPool.CreateWorkItemsGroup(1);
+
+ AutoResetEvent start = new AutoResetEvent(false);
+
+ // Queue the work item
+ IWorkItemResult wir = wig.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancel), start);
+
+ // Wait for it to start executing
+ bool success = start.WaitOne(3000, false);
+
+ // Make sure it was started
+ Assert.IsTrue(success);
+
+ // Cancel the work item
+ wig.Cancel();
+
+ // Let it complete
+ start.Set();
+
+ // Wait for the work item to complete
+ smartThreadPool.WaitForIdle();
+
+ try
+ {
+ // Check the work item's result
+ // The work item started running after it was start its execution.
+ // The work item samples the cancel, therefore it is aware that it
+ // was canceled. Using the GetResult should throw the cancel exeception
+ wir.GetResult(0, false);
+ }
+ finally
+ {
+ smartThreadPool.Shutdown();
+ }
+ }
+
+ ///
+ /// 1. Create STP
+ /// 3. Queue work item into the STP
+ /// 4. Cancel the work items in the STP
+ /// 5. Work item checks the cancel and quits
+ /// 6. Make sure the work item result throws exception
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemCancelException))]
+ public void TestSTPCancelledWorkItemCancelException()
+ {
+ // Create a SmartThreadPool with only one thread.
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ AutoResetEvent start = new AutoResetEvent(false);
+
+ // Queue the work item
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoCheckForCancel), start);
+
+ // Wait for it to start executing
+ bool success = start.WaitOne(3000, false);
+
+ // Make sure it was started
+ Assert.IsTrue(success);
+
+ // Cancel the work item
+ smartThreadPool.Cancel();
+
+ // Let it complete
+ start.Set();
+
+ // Wait for the work item to complete
+ smartThreadPool.WaitForIdle();
+
+ try
+ {
+ // Check the work item's result
+ // The work item started running after it was start its execution.
+ // The work item samples the cancel, therefore it is aware that it
+ // was canceled. Using the GetResult should throw the cancel exeception
+ wir.GetResult(0, false);
+
+ }
+ finally
+ {
+ smartThreadPool.Shutdown();
+ }
+ }
+
+ private object DoCheckForCancelledWorkItem(object state)
+ {
+ if (!SmartThreadPool.IsWorkItemCanceled)
+ {
+ Interlocked.Increment(ref _counter);
+ }
+
+ return null;
+ }
+
+ private object DoWaitForCancel(object state)
+ {
+ AutoResetEvent start = state as AutoResetEvent;
+
+ // Signal the test that the work item started
+ start.Set();
+
+ // Let the test run (or else the next line may reset the signaled event)
+ Thread.Sleep(10);
+
+ // Wait for the test to cancel the work item
+ start.WaitOne();
+
+ return 1;
+ }
+
+ private object DoCheckForCancel(object state)
+ {
+ AutoResetEvent start = state as AutoResetEvent;
+
+ // Signal the test that the work item started
+ start.Set();
+
+ // Let the test run (or else the next line may reset the signaled event)
+ Thread.Sleep(10);
+
+ // Wait for the test to cancel the work item
+ start.WaitOne();
+
+ // Sample if the work item was cancelled
+ bool cancelled = SmartThreadPool.IsWorkItemCanceled;
+
+ return 1;
+ }
+ */
+ }
+
+}
diff --git a/STPTests/TestConcurrencyChanges.cs b/STPTests/TestConcurrencyChanges.cs
new file mode 100644
index 0000000..c7fbc95
--- /dev/null
+++ b/STPTests/TestConcurrencyChanges.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Threading;
+using System.Diagnostics;
+
+using NUnit.Framework;
+
+using Amib.Threading;
+
+namespace SmartThreadPoolTests
+{
+ ///
+ /// Summary description for TestConcurrencyChanges.
+ ///
+ [TestFixture]
+ [Category("TestConcurrencyChanges")]
+ public class TestConcurrencyChanges
+ {
+ public TestConcurrencyChanges()
+ {
+ }
+
+ ///
+ /// Example of waiting for idle
+ ///
+ [Test]
+ public void TestMaxThreadsChange()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(1 * 1000, 1, 0);
+
+ bool success = false;
+
+ for (int i = 0; i < 100; ++i)
+ {
+ smartThreadPool.QueueWorkItem(
+ new WorkItemCallback(this.DoSomeWork),
+ null);
+ }
+
+ success = WaitForMaxThreadsValue(smartThreadPool, 1, 1 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.MaxThreads = 5;
+ success = WaitForMaxThreadsValue(smartThreadPool, 5, 2 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.MaxThreads = 25;
+ success = WaitForMaxThreadsValue(smartThreadPool, 25, 4 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.MaxThreads = 10;
+ success = WaitForMaxThreadsValue(smartThreadPool, 10, 10 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.Shutdown();
+ }
+
+ [Test]
+ public void TestMinThreadsChange()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(1 * 1000, 25, 0);
+
+ bool success = false;
+
+ success = WaitForMinThreadsValue(smartThreadPool, 0, 1 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.MinThreads = 5;
+ success = WaitForMinThreadsValue(smartThreadPool, 5, 2 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.MinThreads = 25;
+ success = WaitForMinThreadsValue(smartThreadPool, 25, 4 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.MinThreads = 10;
+ success = WaitForMinThreadsValue(smartThreadPool, 10, 10 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.Shutdown();
+ }
+
+ ///
+ /// Example of waiting for idle
+ ///
+ [Test]
+ public void TestConcurrencyChange()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10 * 1000, 1, 0);
+
+ bool success = false;
+
+ for (int i = 0; i < 100; ++i)
+ {
+ smartThreadPool.QueueWorkItem(
+ new WorkItemCallback(this.DoSomeWork),
+ null);
+ }
+
+ smartThreadPool.Concurrency = 1;
+ success = WaitForMaxThreadsValue(smartThreadPool, 1, 1 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.Concurrency = 5;
+ success = WaitForMaxThreadsValue(smartThreadPool, 5, 2 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.Concurrency = 25;
+ success = WaitForMaxThreadsValue(smartThreadPool, 25, 4 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.Concurrency = 10;
+ success = WaitForMaxThreadsValue(smartThreadPool, 10, 10 * 1000);
+ Assert.IsTrue(success);
+
+ smartThreadPool.Shutdown();
+ }
+
+
+ private bool WaitForMaxThreadsValue(SmartThreadPool smartThreadPool, int maxThreadsCount, int timeout)
+ {
+ DateTime end = DateTime.Now + new TimeSpan(0, 0, 0, 0, timeout);
+
+ bool success = false;
+ while(DateTime.Now <= end && !success)
+ {
+ success = (smartThreadPool.InUseThreads == maxThreadsCount);
+ Thread.Sleep(10);
+ }
+
+ return success;
+ }
+
+ private bool WaitForMinThreadsValue(SmartThreadPool smartThreadPool, int minThreadsCount, int timeout)
+ {
+ DateTime end = DateTime.Now + new TimeSpan(0, 0, 0, 0, timeout);
+
+ bool success = false;
+ while (DateTime.Now <= end && !success)
+ {
+ success = (smartThreadPool.ActiveThreads == minThreadsCount);
+ Thread.Sleep(10);
+ }
+
+ return success;
+ }
+
+
+ private int x = 0;
+ private object DoSomeWork(object state)
+ {
+ Debug.WriteLine(Interlocked.Increment(ref x));
+ Thread.Sleep(1000);
+ return 1;
+ }
+ }
+}
diff --git a/STPTests/TestFalseFillStateWithParams.cs b/STPTests/TestFalseFillStateWithParams.cs
new file mode 100644
index 0000000..58b2be9
--- /dev/null
+++ b/STPTests/TestFalseFillStateWithParams.cs
@@ -0,0 +1,246 @@
+using System;
+using Amib.Threading;
+using NUnit.Framework;
+using System.Net;
+
+namespace STPTests
+{
+ ///
+ /// Summary description for TestFalseFillStateWithArgs.
+ ///
+ [TestFixture]
+ [Category("TestFalseFillStateWithArgs")]
+ public class TestFalseFillStateWithArgs
+ {
+ private SmartThreadPool _stp;
+ private IWorkItemsGroup _wig;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+ _wig = _stp.CreateWorkItemsGroup(10);
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.WaitForIdle();
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void STPActionT0()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action0);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPActionT1()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action1, 17);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPActionT2()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action2, 'a', "bla bla");
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPActionT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _stp.QueueWorkItem(Action3, true, chars, obj);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPActionT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _stp.QueueWorkItem(Action4, long.MinValue, p, ip, guid);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPFuncT0()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func0));
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPFuncT1()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func1), 17);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPFuncT2()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func2), 'a', "bla bla");
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPFuncT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func3), true, chars, obj);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void STPFuncT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGActionT0()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(Action0);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGActionT1()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(Action1, 17);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGActionT2()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(Action2, 'a', "bla bla");
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGActionT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _wig.QueueWorkItem(Action3, true, chars, obj);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGActionT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _wig.QueueWorkItem(Action4, long.MinValue, p, ip, guid);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGFuncT0()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func0));
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGFuncT1()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func1), 17);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGFuncT2()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func2), 'a', "bla bla");
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGFuncT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func3), true, chars, obj);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void WIGFuncT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid);
+ Assert.IsNull(wir.State);
+ }
+
+
+ private void Action0()
+ {
+ }
+
+ private void Action1(int p1)
+ {
+ }
+
+ private void Action2(char p1, string p2)
+ {
+ }
+
+ private void Action3(bool p1, char[] p2, object p3)
+ {
+ }
+
+ private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ }
+
+ private int Func0()
+ {
+ return 0;
+ }
+
+ private bool Func1(int p1)
+ {
+ return true;
+ }
+
+ private string Func2(char p1, string p2)
+ {
+ return "value";
+ }
+
+ private char Func3(bool p1, char[] p2, object p3)
+ {
+ return 'z';
+ }
+
+ private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ return IPAddress.None;
+ }
+ }
+}
diff --git a/STPTests/TestFillStateWithParams.cs b/STPTests/TestFillStateWithParams.cs
new file mode 100644
index 0000000..f37b943
--- /dev/null
+++ b/STPTests/TestFillStateWithParams.cs
@@ -0,0 +1,207 @@
+using System;
+using Amib.Threading;
+using NUnit.Framework;
+using System.Net;
+
+namespace STPTests
+{
+ ///
+ /// Summary description for TestFillStateWithArgs.
+ ///
+ [TestFixture]
+ [Category("TestFillStateWithArgs")]
+ public class TestFillStateWithArgs
+ {
+ private SmartThreadPool _stp;
+
+ [SetUp]
+ public void Init()
+ {
+ STPStartInfo stpStartInfo = new STPStartInfo();
+ stpStartInfo.FillStateWithArgs = true;
+ _stp = new SmartThreadPool(stpStartInfo);
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.WaitForIdle();
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void ActionT0()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action0);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void ActionT1()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action1, 17);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 1);
+ Assert.AreEqual(args[0], 17);
+ }
+
+ [Test]
+ public void ActionT2()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action2, 'a', "bla bla");
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 2);
+ Assert.AreEqual(args[0], 'a');
+ Assert.AreEqual(args[1], "bla bla");
+ }
+
+ [Test]
+ public void ActionT3()
+ {
+ char[] chars = new char[] {'a', 'b'};
+ object obj = new object();
+
+ IWorkItemResult wir = _stp.QueueWorkItem(Action3, true, chars, obj);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 3);
+ Assert.AreEqual(args[0], true);
+ Assert.AreEqual(args[1], chars);
+ Assert.AreEqual(args[2], obj);
+ }
+
+ [Test]
+ public void ActionT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _stp.QueueWorkItem(Action4, long.MinValue, p, ip, guid);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 4);
+ Assert.AreEqual(args[0], long.MinValue);
+ Assert.AreEqual(args[1], p);
+ Assert.AreEqual(args[2], ip);
+ Assert.AreEqual(args[3], guid);
+ }
+
+ [Test]
+ public void FuncT0()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func0));
+ Assert.AreEqual(wir.State, null);
+ }
+
+ [Test]
+ public void FuncT1()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func1), 17);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 1);
+ Assert.AreEqual(args[0], 17);
+ }
+
+ [Test]
+ public void FuncT2()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func2), 'a', "bla bla");
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 2);
+ Assert.AreEqual(args[0], 'a');
+ Assert.AreEqual(args[1], "bla bla");
+ }
+
+ [Test]
+ public void FuncT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func3), true, chars, obj);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 3);
+ Assert.AreEqual(args[0], true);
+ Assert.AreEqual(args[1], chars);
+ Assert.AreEqual(args[2], obj);
+ }
+
+ [Test]
+ public void FuncT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid);
+
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 4);
+ Assert.AreEqual(args[0], long.MinValue);
+ Assert.AreEqual(args[1], p);
+ Assert.AreEqual(args[2], ip);
+ Assert.AreEqual(args[3], guid);
+ }
+
+
+ private void Action0()
+ {
+ }
+
+ private void Action1(int p1)
+ {
+ }
+
+ private void Action2(char p1, string p2)
+ {
+ }
+
+ private void Action3(bool p1, char [] p2, object p3)
+ {
+ }
+
+ private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ }
+
+ private int Func0()
+ {
+ return 0;
+ }
+
+ private bool Func1(int p1)
+ {
+ return true;
+ }
+
+ private string Func2(char p1, string p2)
+ {
+ return "value";
+ }
+
+ private char Func3(bool p1, char[] p2, object p3)
+ {
+ return 'z';
+ }
+
+ private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ return IPAddress.None;
+ }
+ }
+}
diff --git a/STPTests/TestFunc.cs b/STPTests/TestFunc.cs
new file mode 100644
index 0000000..cdc1345
--- /dev/null
+++ b/STPTests/TestFunc.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Amib.Threading;
+using NUnit.Framework;
+
+namespace STPTests
+{
+ ///
+ /// Summary description for TestCancel.
+ ///
+ [TestFixture]
+ [Category("TestFuncT")]
+ public class TestFuncT
+ {
+ [Test]
+ public void FuncT()
+ {
+ SmartThreadPool stp = new SmartThreadPool();
+ IWorkItemResult wir =
+ stp.QueueWorkItem(new Func(f), 1);
+
+ int y = wir.GetResult();
+
+ Assert.AreEqual(y, 2);
+
+ try
+ {
+ wir.GetResult();
+ }
+ finally
+ {
+ stp.Shutdown();
+ }
+ }
+
+ private int f(int x)
+ {
+ return x + 1;
+ }
+ }
+}
diff --git a/STPTests/TestFuncT.cs b/STPTests/TestFuncT.cs
new file mode 100644
index 0000000..4df77ef
--- /dev/null
+++ b/STPTests/TestFuncT.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using Amib.Threading;
+using NUnit.Framework;
+
+namespace SmartThreadPoolTests
+{
+ ///
+ /// Summary description for TestCancel.
+ ///
+ [TestFixture]
+ [Category("TestFuncT")]
+ public class TestFuncT
+ {
+ private SmartThreadPool _stp;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void FuncT0()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(MaxInt));
+
+ int result = wir.GetResult();
+
+ Assert.AreEqual(result, int.MaxValue);
+ }
+
+ [Test]
+ public void FuncT1()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Not), true);
+
+ bool result = wir.Result;
+
+ Assert.AreEqual(result, false);
+ }
+
+ [Test]
+ public void FuncT2()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(string.Concat), "ABC", "xyz");
+
+ string result = wir.Result;
+
+ Assert.AreEqual(result, "ABCxyz");
+ }
+
+ [Test]
+ public void FuncT3()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Substring), "ABCDEF", 1, 2);
+
+ string result = wir.Result;
+
+ Assert.AreEqual(result, "BC");
+ }
+
+ [Test]
+ public void FuncT4()
+ {
+ int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Subarray), numbers, 1, 2, 3);
+
+ int[] result = wir.Result;
+
+ Assert.AreEqual(result, new int[] { 2, 3, 2, 3, 2, 3, });
+ }
+
+ private int MaxInt()
+ {
+ return int.MaxValue;
+ }
+
+ private bool Not(bool flag)
+ {
+ return !flag;
+ }
+
+ private string Substring(string s, int startIndex, int length)
+ {
+ return s.Substring(startIndex, length);
+ }
+
+ private int[] Subarray(int[] numbers, int startIndex, int length, int repeat)
+ {
+ int[] result = new int[length * repeat];
+ for (int i = 0; i < repeat; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ result[i * length + j] = numbers[startIndex + j];
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/STPTests/TestGetResult.cs b/STPTests/TestGetResult.cs
index 393c02a..72b627d 100644
--- a/STPTests/TestGetResult.cs
+++ b/STPTests/TestGetResult.cs
@@ -42,144 +42,52 @@ namespace SmartThreadPoolTests
/// Example of how to queue a work item and then wait on a timeout for the result.
///
[Test]
- public void Timeout()
+ [ExpectedException(typeof(WorkItemTimeoutException))]
+ public void Timeout()
{
SmartThreadPool smartThreadPool = new SmartThreadPool();
- bool success = false;
-
IWorkItemResult wir =
smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
- try
- {
- wir.GetResult(500, true);
- }
- catch (WorkItemTimeoutException)
- {
- success = true;
- }
+ try
+ {
+ wir.GetResult(500, true);
+ }
+ finally
+ {
+ smartThreadPool.Shutdown();
+ }
+ }
- smartThreadPool.Shutdown();
+ ///
+ /// Example of how to interrupt the waiting for a work item to complete.
+ ///
+ [Test]
+ [ExpectedException(typeof(WorkItemTimeoutException))]
+ public void WorkItemWaitCanceling()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
- Assert.IsTrue(success);
- }
+ ManualResetEvent cancelWaitHandle = new ManualResetEvent(false);
- ///
- /// Example of how to queue a work item and then cancel it while it is in the queue.
- ///
- [Test]
- public void WorkItemCanceling()
- {
- // Create a SmartThreadPool with only one thread.
- // It just to show how to use the work item canceling feature
- SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 1);
-
- bool success = false;
-
- // Queue a work item that will occupy the thread in the pool
- IWorkItemResult wir1 =
- smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
-
- // Queue another work item that will wait for the first to complete
- IWorkItemResult wir2 =
- smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
-
- // Wait a while for the thread pool to start executing the first work item
- Thread.Sleep(100);
-
- // The first work item cannot be canceled since it is currently executing
- if (!wir1.Cancel())
- {
- // Cancel the second work item while it still in the queue
- if (wir2.Cancel())
- {
- try
- {
- // Retreiving result of a canceled work item throws an exception
- wir2.GetResult();
- }
- catch (WorkItemCancelException)
- {
- success = true;
- }
- }
- }
-
- smartThreadPool.Shutdown();
-
- Assert.IsTrue(success);
- }
-
- ///
- /// Example of how to interrupt the waiting for a work item to complete.
- ///
- [Test]
- public void WorkItemWaitCanceling()
- {
- SmartThreadPool smartThreadPool = new SmartThreadPool();
-
- ManualResetEvent cancelWaitHandle = new ManualResetEvent(false);
-
- bool success = false;
-
- // Queue a work item that will occupy the thread in the pool
- IWorkItemResult wir1 =
- smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
-
- // Queue another work item that will wait for the first to complete
- IWorkItemResult wir2 =
- smartThreadPool.QueueWorkItem(new WorkItemCallback(this.SignalCancel), cancelWaitHandle);
-
- try
- {
- wir1.GetResult(System.Threading.Timeout.Infinite, true, cancelWaitHandle);
- }
- catch (WorkItemTimeoutException)
- {
- success = true;
- }
-
- smartThreadPool.Shutdown();
-
- Assert.IsTrue(success);
- }
-
- ///
- /// Example of how to queue a work item and then cancel it while it is in the queue.
- ///
- [Test]
- public void WorkItemCancelingAndInUseWorkerThreads()
- {
- // Create a SmartThreadPool with only one thread.
- // It just to show how to use the work item canceling feature
- SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 10);
-
- IWorkItemResult [] wirs = new IWorkItemResult[100];
- for(int i = 0; i < 100; ++i)
- {
- wirs[i] = smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
- }
-
-
- // Wait a while for the thread pool to start executing the first work item
- Thread.Sleep(100);
-
- for(int i = 0; i < 100; ++i)
- {
- wirs[i].Cancel();
- }
-
- smartThreadPool.WaitForIdle(2000);
-
- int inUseThreads = smartThreadPool.InUseThreads;
-
- smartThreadPool.Shutdown();
-
- Assert.AreEqual(0, inUseThreads);
- }
+ // Queue a work item that will occupy the thread in the pool
+ IWorkItemResult wir1 =
+ smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ // Queue another work item that will wait for the first to complete
+ IWorkItemResult wir2 =
+ smartThreadPool.QueueWorkItem(new WorkItemCallback(this.SignalCancel), cancelWaitHandle);
+ try
+ {
+ wir1.GetResult(System.Threading.Timeout.Infinite, true, cancelWaitHandle);
+ }
+ finally
+ {
+ smartThreadPool.Shutdown();
+ }
+ }
private object DoSomeWork(object state)
{
@@ -187,12 +95,13 @@ namespace SmartThreadPoolTests
return 1;
}
- private object SignalCancel(object state)
- {
- ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
- Thread.Sleep(250);
- cancelWaitHandle.Set();
- return null;
- }
+ private object SignalCancel(object state)
+ {
+ ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
+ Thread.Sleep(250);
+ cancelWaitHandle.Set();
+ return null;
+ }
+
}
}
diff --git a/STPTests/TestMultipleWorkItems.cs b/STPTests/TestMultipleWorkItems.cs
index ea23375..cc3fd6b 100644
--- a/STPTests/TestMultipleWorkItems.cs
+++ b/STPTests/TestMultipleWorkItems.cs
@@ -228,5 +228,73 @@ namespace SmartThreadPoolTests
Thread.Sleep(1000);
return 1;
}
+
+ [Test]
+ public void WaitAllT()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ bool success = true;
+
+ IWorkItemResult[] wirs = new IWorkItemResult[5];
+
+ for (int i = 0; i < wirs.Length; ++i)
+ {
+ wirs[i] = smartThreadPool.QueueWorkItem(new Func(Math.Min), i, i + 1);
+ }
+
+ SmartThreadPool.WaitAll(wirs);
+
+ for (int i = 0; i < wirs.Length; ++i)
+ {
+ if (!wirs[i].IsCompleted)
+ {
+ success = false;
+ break;
+ }
+
+ int result = wirs[i].GetResult();
+ if (i != result)
+ {
+ success = false;
+ break;
+ }
+ }
+
+ smartThreadPool.Shutdown();
+
+ Assert.IsTrue(success);
+ }
+
+ [Test]
+ public void WaitAnyT()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+ bool success = false;
+
+ IWorkItemResult[] wirs = new IWorkItemResult[5];
+
+ for (int i = 0; i < wirs.Length; ++i)
+ {
+ wirs[i] = smartThreadPool.QueueWorkItem(new Func(Math.Max), i, i - 1);
+ }
+
+ int index = SmartThreadPool.WaitAny(wirs);
+
+ if (wirs[index].IsCompleted)
+ {
+ int result = wirs[index].GetResult();
+ if (index == result)
+ {
+ success = true;
+ }
+ }
+
+ smartThreadPool.Shutdown();
+
+ Assert.IsTrue(success);
+ }
+
}
}
diff --git a/STPTests/TestQueueWorkItem.cs b/STPTests/TestQueueWorkItem.cs
new file mode 100644
index 0000000..635f6db
--- /dev/null
+++ b/STPTests/TestQueueWorkItem.cs
@@ -0,0 +1,98 @@
+
+using NUnit.Framework;
+using Amib.Threading;
+
+namespace SmartThreadPoolTests
+{
+ ///
+ /// Tests for QueueWorkItem.
+ ///
+ [TestFixture]
+ [Category("TestQueueWorkItem")]
+ public class TestQueueWorkItem
+ {
+ private SmartThreadPool _stp;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.Shutdown();
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback);
+ [Test]
+ public void TestQueueWorkItemCall()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCall(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallPrio(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
+ [Test]
+ public void TestQueueWorkItemCallStat()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStat(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallStatPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPrio(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
+ [Test]
+ public void TestQueueWorkItemCallStatPost()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPost(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallStatPostPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPostPrio(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
+ [Test]
+ public void TestQueueWorkItemCallStatPostPflg()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPostPflg(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallStatPostPflgPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPostPflgPrio(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
+ [Test]
+ public void TestQueueWorkItemInfoCall()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemInfoCall(_stp);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
+ [Test]
+ public void TestQueueWorkItemInfoCallStat()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemInfoCallStat(_stp);
+ }
+ }
+}
diff --git a/STPTests/TestThreadsCreate.cs b/STPTests/TestThreadsCreate.cs
new file mode 100644
index 0000000..ec26400
--- /dev/null
+++ b/STPTests/TestThreadsCreate.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Amib.Threading;
+
+namespace SmartThreadPoolTests
+{
+ ///
+ ///
+ [TestFixture]
+ [Category("TestThreadsCreate")]
+ public class TestThreadsCreate
+ {
+ private bool _initSuccess;
+ private bool _workItemSuccess;
+ private bool _termSuccess;
+
+ private void ClearResults()
+ {
+ _initSuccess = false;
+ _workItemSuccess = false;
+ _termSuccess = false;
+ }
+
+ [Test]
+ public void TestThreadsEvents()
+ {
+ ClearResults();
+
+ SmartThreadPool stp = new SmartThreadPool();
+
+ stp.OnThreadInitialization += new ThreadInitializationHandler(OnInitialization);
+ stp.OnThreadTermination += new ThreadTerminationHandler(OnTermination);
+
+ stp.QueueWorkItem(new WorkItemCallback(DoSomeWork), null);
+
+ stp.WaitForIdle();
+ stp.Shutdown();
+
+ Assert.IsTrue(_initSuccess);
+ Assert.IsTrue(_workItemSuccess);
+ Assert.IsTrue(_termSuccess);
+ }
+
+ public void OnInitialization()
+ {
+ ThreadContextState.Current.Counter = 1234;
+ _initSuccess = true;
+ }
+
+ private object DoSomeWork(object state)
+ {
+ int counter = ThreadContextState.Current.Counter;
+ _workItemSuccess = (1234 == counter);
+
+ ThreadContextState.Current.Counter = 1111;
+ return 1;
+ }
+
+ public void OnTermination()
+ {
+ int counter = ThreadContextState.Current.Counter;
+ _termSuccess = (1111 == counter);
+ }
+ }
+
+ internal class ThreadContextState
+ {
+ // Each thread will have its own ThreadContextState object
+ [ThreadStatic]
+ private static ThreadContextState _threadContextState;
+
+ private int _counter = 0;
+
+ public int Counter
+ {
+ get { return _counter; }
+ set { _counter = value; }
+ }
+
+ // Static member so it can be used anywhere in code of the work item method
+ public static ThreadContextState Current
+ {
+ get
+ {
+ // If the _threadContextState is null then it was not yet initialized
+ // for this thread.
+ if (null == _threadContextState)
+ {
+ // Create a ThreadContextState object
+ _threadContextState = new ThreadContextState();
+ }
+ return _threadContextState;
+ }
+ }
+ }
+}
diff --git a/STPTests/TestWIGActionT.cs b/STPTests/TestWIGActionT.cs
new file mode 100644
index 0000000..9bc5b8f
--- /dev/null
+++ b/STPTests/TestWIGActionT.cs
@@ -0,0 +1,126 @@
+using Amib.Threading;
+using NUnit.Framework;
+
+namespace WorkItemsGroupTests
+{
+ ///
+ /// Summary description for TestCancel.
+ ///
+ [TestFixture]
+ [Category("TestWIGActionT")]
+ public class TestWIGActionT
+ {
+ private SmartThreadPool _stp;
+ private IWorkItemsGroup _wig;
+ private object _result;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+ _wig = _stp.CreateWorkItemsGroup(10);
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void ActionT0()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _wig.QueueWorkItem(MaxInt);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, int.MaxValue);
+ }
+
+ [Test]
+ public void ActionT1()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _wig.QueueWorkItem(Not, true);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, false);
+ }
+
+ [Test]
+ public void ActionT2()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _wig.QueueWorkItem(Concat, "ABC", "xyz");
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, "ABCxyz");
+ }
+
+ [Test]
+ public void ActionT3()
+ {
+ _result = null;
+
+ IWorkItemResult wir = _wig.QueueWorkItem(Substring, "ABCDEF", 1, 2);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, "BC");
+ }
+
+ [Test]
+ public void ActionT4()
+ {
+ _result = null;
+
+ int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ IWorkItemResult wir = _wig.QueueWorkItem(Subarray, numbers, 1, 2, 3);
+
+ wir.GetResult();
+
+ Assert.AreEqual(_result, new int[] { 2, 3, 2, 3, 2, 3, });
+ }
+
+ private void MaxInt()
+ {
+ _result = int.MaxValue;
+ }
+
+ private void Not(bool flag)
+ {
+ _result = !flag;
+ }
+
+ private void Concat(string s1, string s2)
+ {
+ _result = s1 + s2;
+ }
+
+ private void Substring(string s, int startIndex, int length)
+ {
+ _result = s.Substring(startIndex, length);
+ }
+
+ private void Subarray(int[] numbers, int startIndex, int length, int repeat)
+ {
+ int[] result = new int[length * repeat];
+ for (int i = 0; i < repeat; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ result[i * length + j] = numbers[startIndex + j];
+ }
+ }
+
+ _result = result;
+ }
+ }
+}
diff --git a/STPTests/TestWIGConcurrency.cs b/STPTests/TestWIGConcurrency.cs
index 1cf5b5b..5a429db 100644
--- a/STPTests/TestWIGConcurrency.cs
+++ b/STPTests/TestWIGConcurrency.cs
@@ -5,7 +5,7 @@ using NUnit.Framework;
using Amib.Threading;
-namespace SmartThreadPoolTests
+namespace WorkItemsGroupTests
{
///
/// Summary description for TestWIGConcurrency.
diff --git a/STPTests/TestWIGConcurrencyChanges.cs b/STPTests/TestWIGConcurrencyChanges.cs
new file mode 100644
index 0000000..bde3677
--- /dev/null
+++ b/STPTests/TestWIGConcurrencyChanges.cs
@@ -0,0 +1,213 @@
+using System;
+using System.Threading;
+using System.Diagnostics;
+
+using NUnit.Framework;
+
+using Amib.Threading;
+
+namespace WorkItemsGroupTests
+{
+ ///
+ /// Summary description for TestWIGConcurrencyChanges.
+ ///
+ [TestFixture]
+ [Category("TestWIGConcurrencyChanges")]
+ public class TestWIGConcurrencyChanges
+ {
+ public TestWIGConcurrencyChanges()
+ {
+ }
+
+ ///
+ /// Example of waiting for idle
+ ///
+ [Test]
+ public void TestWIGConcurrencyChange1WIG()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 1, 0);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ PauseSTP(smartThreadPool);
+ Thread.Sleep(100);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ IWorkItemsGroup wig = smartThreadPool.CreateWorkItemsGroup(1);
+ wig.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(1 == smartThreadPool.WaitingCallbacks);
+
+ wig.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(1 == smartThreadPool.WaitingCallbacks);
+
+ wig.Concurrency = 2;
+ Thread.Sleep(100);
+ Assert.IsTrue(2 == smartThreadPool.WaitingCallbacks);
+
+ ResumeSTP(smartThreadPool);
+ Thread.Sleep(100);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ PauseSTP(smartThreadPool);
+ wig.Concurrency = 1;
+
+ wig.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(1 == smartThreadPool.WaitingCallbacks);
+
+ wig.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(1 == smartThreadPool.WaitingCallbacks);
+
+ ResumeSTP(smartThreadPool);
+ Thread.Sleep(100);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ smartThreadPool.Shutdown();
+ }
+
+ ///
+ /// Example of waiting for idle
+ ///
+ [Test]
+ public void TestWIGConcurrencyChange2WIGs()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10 * 1000, 2, 0);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ PauseSTP(smartThreadPool);
+ PauseSTP(smartThreadPool);
+ Thread.Sleep(100);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ IWorkItemsGroup wig1 = smartThreadPool.CreateWorkItemsGroup(1);
+ IWorkItemsGroup wig2 = smartThreadPool.CreateWorkItemsGroup(1);
+
+ wig1.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(1 == smartThreadPool.WaitingCallbacks);
+
+ wig2.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(2 == smartThreadPool.WaitingCallbacks);
+
+ wig1.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(2 == smartThreadPool.WaitingCallbacks);
+
+ wig2.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(2 == smartThreadPool.WaitingCallbacks);
+
+ wig1.Concurrency = 2;
+ Thread.Sleep(100);
+ Assert.IsTrue(3 == smartThreadPool.WaitingCallbacks);
+
+ wig2.Concurrency = 2;
+ Thread.Sleep(100);
+ Assert.IsTrue(4 == smartThreadPool.WaitingCallbacks);
+
+ ResumeSTP(smartThreadPool);
+ Thread.Sleep(100);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ PauseSTP(smartThreadPool);
+ PauseSTP(smartThreadPool);
+ Thread.Sleep(100);
+ wig1.Concurrency = 1;
+
+ wig1.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(1 == smartThreadPool.WaitingCallbacks);
+
+ wig2.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+ Assert.IsTrue(2 == smartThreadPool.WaitingCallbacks);
+
+ ResumeSTP(smartThreadPool);
+ Thread.Sleep(100);
+ Assert.IsTrue(0 == smartThreadPool.WaitingCallbacks);
+
+ smartThreadPool.Shutdown();
+ }
+
+ private void PauseSTP(SmartThreadPool stp)
+ {
+ _pauseSTP.Reset();
+ stp.QueueWorkItem(
+ new WorkItemCallback(this.DoPauseSTP),
+ null);
+ }
+
+ private void ResumeSTP(SmartThreadPool stp)
+ {
+ _pauseSTP.Set();
+ }
+
+ private ManualResetEvent _pauseSTP = new ManualResetEvent(false);
+ private object DoPauseSTP(object state)
+ {
+ _pauseSTP.WaitOne();
+ return 1;
+ }
+
+ private object DoSomeWork(object state)
+ {
+ return 1;
+ }
+
+
+
+
+/*
+ ///
+ /// Example of waiting for idle
+ ///
+ [Test]
+ public void WaitForIdle()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
+
+ bool success = false;
+
+ for(int i = 0; i < 100; ++i)
+ {
+ smartThreadPool.QueueWorkItem(
+ new WorkItemCallback(this.DoSomeWork),
+ null);
+ }
+
+ success = !smartThreadPool.WaitForIdle(3500);
+ success = success && smartThreadPool.WaitForIdle(1000);
+
+ smartThreadPool.Shutdown();
+
+ Assert.IsTrue(success);
+ }
+
+ [Test]
+ public void WaitForIdleOnWrongThread()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
+
+ IWorkItemResult wir = smartThreadPool.QueueWorkItem(
+ new WorkItemCallback(this.DoWaitForIdle),
+ smartThreadPool);
+
+ Exception e;
+ wir.GetResult(out e);
+
+ smartThreadPool.Shutdown();
+
+ Assert.IsTrue(e is NotSupportedException);
+ }
+
+
+ private int x = 0;
+ private object DoSomeWork(object state)
+ {
+ Debug.WriteLine(Interlocked.Increment(ref x));
+ Thread.Sleep(1000);
+ return 1;
+ }
+
+ private object DoWaitForIdle(object state)
+ {
+ SmartThreadPool smartThreadPool = state as SmartThreadPool;
+ smartThreadPool.WaitForIdle();
+ return null;
+ }
+ */
+ }
+}
diff --git a/STPTests/TestWIGFillStateWithParams.cs b/STPTests/TestWIGFillStateWithParams.cs
new file mode 100644
index 0000000..4a9fc67
--- /dev/null
+++ b/STPTests/TestWIGFillStateWithParams.cs
@@ -0,0 +1,210 @@
+using System;
+using Amib.Threading;
+using NUnit.Framework;
+using System.Net;
+
+namespace STPTests
+{
+ ///
+ /// Summary description for TestWIGFillStateWithArgs.
+ ///
+ [TestFixture]
+ [Category("TestWIGFillStateWithArgs")]
+ public class TestWIGFillStateWithArgs
+ {
+ private SmartThreadPool _stp;
+ private IWorkItemsGroup _wig;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+
+ WIGStartInfo wigStartInfo = new WIGStartInfo();
+ wigStartInfo.FillStateWithArgs = true;
+ _wig = _stp.CreateWorkItemsGroup(10, wigStartInfo);
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.WaitForIdle();
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void ActionT0()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(Action0);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void ActionT1()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(Action1, 17);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 1);
+ Assert.AreEqual(args[0], 17);
+ }
+
+ [Test]
+ public void ActionT2()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(Action2, 'a', "bla bla");
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 2);
+ Assert.AreEqual(args[0], 'a');
+ Assert.AreEqual(args[1], "bla bla");
+ }
+
+ [Test]
+ public void ActionT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _wig.QueueWorkItem(Action3, true, chars, obj);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 3);
+ Assert.AreEqual(args[0], true);
+ Assert.AreEqual(args[1], chars);
+ Assert.AreEqual(args[2], obj);
+ }
+
+ [Test]
+ public void ActionT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _wig.QueueWorkItem(Action4, long.MinValue, p, ip, guid);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 4);
+ Assert.AreEqual(args[0], long.MinValue);
+ Assert.AreEqual(args[1], p);
+ Assert.AreEqual(args[2], ip);
+ Assert.AreEqual(args[3], guid);
+ }
+
+ [Test]
+ public void FuncT0()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func0));
+ Assert.AreEqual(wir.State, null);
+ }
+
+ [Test]
+ public void FuncT1()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func1), 17);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 1);
+ Assert.AreEqual(args[0], 17);
+ }
+
+ [Test]
+ public void FuncT2()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func2), 'a', "bla bla");
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 2);
+ Assert.AreEqual(args[0], 'a');
+ Assert.AreEqual(args[1], "bla bla");
+ }
+
+ [Test]
+ public void FuncT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func3), true, chars, obj);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 3);
+ Assert.AreEqual(args[0], true);
+ Assert.AreEqual(args[1], chars);
+ Assert.AreEqual(args[2], obj);
+ }
+
+ [Test]
+ public void FuncT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid);
+
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 4);
+ Assert.AreEqual(args[0], long.MinValue);
+ Assert.AreEqual(args[1], p);
+ Assert.AreEqual(args[2], ip);
+ Assert.AreEqual(args[3], guid);
+ }
+
+
+ private void Action0()
+ {
+ }
+
+ private void Action1(int p1)
+ {
+ }
+
+ private void Action2(char p1, string p2)
+ {
+ }
+
+ private void Action3(bool p1, char[] p2, object p3)
+ {
+ }
+
+ private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ }
+
+ private int Func0()
+ {
+ return 0;
+ }
+
+ private bool Func1(int p1)
+ {
+ return true;
+ }
+
+ private string Func2(char p1, string p2)
+ {
+ return "value";
+ }
+
+ private char Func3(bool p1, char[] p2, object p3)
+ {
+ return 'z';
+ }
+
+ private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ return IPAddress.None;
+ }
+ }
+}
diff --git a/STPTests/TestWIGFillStateWithParams1.cs b/STPTests/TestWIGFillStateWithParams1.cs
new file mode 100644
index 0000000..584f3a4
--- /dev/null
+++ b/STPTests/TestWIGFillStateWithParams1.cs
@@ -0,0 +1,207 @@
+using System;
+using Amib.Threading;
+using NUnit.Framework;
+using System.Net;
+
+namespace STPTests
+{
+ ///
+ /// Summary description for TestFillStateWithParams.
+ ///
+ [TestFixture]
+ [Category("TestFillStateWithParams")]
+ public class TestFillStateWithParams
+ {
+ private SmartThreadPool _stp;
+
+ [SetUp]
+ public void Init()
+ {
+ STPStartInfo stpStartInfo = new STPStartInfo();
+ stpStartInfo.FillStateWithParams = true;
+ _stp = new SmartThreadPool(stpStartInfo);
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.WaitForIdle();
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void ActionT0()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action0);
+ Assert.IsNull(wir.State);
+ }
+
+ [Test]
+ public void ActionT1()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action1, 17);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 1);
+ Assert.AreEqual(args[0], 17);
+ }
+
+ [Test]
+ public void ActionT2()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(Action2, 'a', "bla bla");
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 2);
+ Assert.AreEqual(args[0], 'a');
+ Assert.AreEqual(args[1], "bla bla");
+ }
+
+ [Test]
+ public void ActionT3()
+ {
+ char[] chars = new char[] {'a', 'b'};
+ object obj = new object();
+
+ IWorkItemResult wir = _stp.QueueWorkItem(Action3, true, chars, obj);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 3);
+ Assert.AreEqual(args[0], true);
+ Assert.AreEqual(args[1], chars);
+ Assert.AreEqual(args[2], obj);
+ }
+
+ [Test]
+ public void ActionT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _stp.QueueWorkItem(Action4, long.MinValue, p, ip, guid);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 4);
+ Assert.AreEqual(args[0], long.MinValue);
+ Assert.AreEqual(args[1], p);
+ Assert.AreEqual(args[2], ip);
+ Assert.AreEqual(args[3], guid);
+ }
+
+ [Test]
+ public void FuncT0()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func0));
+ Assert.AreEqual(wir.State, null);
+ }
+
+ [Test]
+ public void FuncT1()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func1), 17);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 1);
+ Assert.AreEqual(args[0], 17);
+ }
+
+ [Test]
+ public void FuncT2()
+ {
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func2), 'a', "bla bla");
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 2);
+ Assert.AreEqual(args[0], 'a');
+ Assert.AreEqual(args[1], "bla bla");
+ }
+
+ [Test]
+ public void FuncT3()
+ {
+ char[] chars = new char[] { 'a', 'b' };
+ object obj = new object();
+
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func3), true, chars, obj);
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 3);
+ Assert.AreEqual(args[0], true);
+ Assert.AreEqual(args[1], chars);
+ Assert.AreEqual(args[2], obj);
+ }
+
+ [Test]
+ public void FuncT4()
+ {
+ IntPtr p = new IntPtr(int.MaxValue);
+ Guid guid = Guid.NewGuid();
+
+ IPAddress ip = IPAddress.Parse("1.2.3.4");
+ IWorkItemResult wir = _stp.QueueWorkItem(new Func(Func4), long.MinValue, p, ip, guid);
+
+ object[] args = wir.State as object[];
+
+ Assert.IsNotNull(args);
+ Assert.AreEqual(args.Length, 4);
+ Assert.AreEqual(args[0], long.MinValue);
+ Assert.AreEqual(args[1], p);
+ Assert.AreEqual(args[2], ip);
+ Assert.AreEqual(args[3], guid);
+ }
+
+
+ private void Action0()
+ {
+ }
+
+ private void Action1(int p1)
+ {
+ }
+
+ private void Action2(char p1, string p2)
+ {
+ }
+
+ private void Action3(bool p1, char [] p2, object p3)
+ {
+ }
+
+ private void Action4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ }
+
+ private int Func0()
+ {
+ return 0;
+ }
+
+ private bool Func1(int p1)
+ {
+ return true;
+ }
+
+ private string Func2(char p1, string p2)
+ {
+ return "value";
+ }
+
+ private char Func3(bool p1, char[] p2, object p3)
+ {
+ return 'z';
+ }
+
+ private IPAddress Func4(long p1, IntPtr p2, IPAddress p3, Guid p4)
+ {
+ return IPAddress.None;
+ }
+ }
+}
diff --git a/STPTests/TestWIGFuncT.cs b/STPTests/TestWIGFuncT.cs
new file mode 100644
index 0000000..817f4eb
--- /dev/null
+++ b/STPTests/TestWIGFuncT.cs
@@ -0,0 +1,107 @@
+using Amib.Threading;
+using NUnit.Framework;
+
+namespace WorkItemsGroupTests
+{
+ [TestFixture]
+ [Category("TestWIGFuncT")]
+ public class TestWIGFuncT
+ {
+ private SmartThreadPool _stp;
+ private IWorkItemsGroup _wig;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+ _wig = _stp.CreateWorkItemsGroup(10);
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.Shutdown();
+ }
+
+ [Test]
+ public void FuncT0()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(MaxInt));
+
+ int result = wir.GetResult();
+
+ Assert.AreEqual(result, int.MaxValue);
+ }
+
+ [Test]
+ public void FuncT1()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Not), true);
+
+ bool result = wir.Result;
+
+ Assert.AreEqual(result, false);
+ }
+
+ [Test]
+ public void FuncT2()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(string.Concat), "ABC", "xyz");
+
+ string result = wir.Result;
+
+ Assert.AreEqual(result, "ABCxyz");
+ }
+
+ [Test]
+ public void FuncT3()
+ {
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Substring), "ABCDEF", 1, 2);
+
+ string result = wir.Result;
+
+ Assert.AreEqual(result, "BC");
+ }
+
+ [Test]
+ public void FuncT4()
+ {
+ int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ IWorkItemResult wir = _wig.QueueWorkItem(new Func(Subarray), numbers, 1, 2, 3);
+
+ int[] result = wir.Result;
+
+ Assert.AreEqual(result, new int[] { 2, 3, 2, 3, 2, 3, });
+ }
+
+ private int MaxInt()
+ {
+ return int.MaxValue;
+ }
+
+ private bool Not(bool flag)
+ {
+ return !flag;
+ }
+
+ private string Substring(string s, int startIndex, int length)
+ {
+ return s.Substring(startIndex, length);
+ }
+
+ private int[] Subarray(int[] numbers, int startIndex, int length, int repeat)
+ {
+ int[] result = new int[length * repeat];
+ for (int i = 0; i < repeat; i++)
+ {
+ for (int j = 0; j < length; j++)
+ {
+ result[i * length + j] = numbers[startIndex + j];
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/STPTests/TestWIGGetResult.cs b/STPTests/TestWIGGetResult.cs
index 852a060..acfb0cc 100644
--- a/STPTests/TestWIGGetResult.cs
+++ b/STPTests/TestWIGGetResult.cs
@@ -67,53 +67,6 @@ namespace WorkItemsGroupTests
Assert.IsTrue(success);
}
- ///
- /// Example of how to queue a work item and then cancel it while it is in the queue.
- ///
- [Test]
- public void WorkItemCanceling()
- {
- // Create a SmartThreadPool with only one thread.
- // It just to show how to use the work item canceling feature
- SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 1);
- IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
-
- bool success = false;
-
- // Queue a work item that will occupy the thread in the pool
- IWorkItemResult wir1 =
- workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
-
- // Queue another work item that will wait for the first to complete
- IWorkItemResult wir2 =
- workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
-
- // Wait a while for the thread pool to start executing the first work item
- Thread.Sleep(100);
-
- // The first work item cannot be canceled since it is currently executing
- if (!wir1.Cancel())
- {
- // Cancel the second work item while it still in the queue
- if (wir2.Cancel())
- {
- try
- {
- // Retreiving result of a canceled work item throws an exception
- wir2.GetResult();
- }
- catch (WorkItemCancelException)
- {
- success = true;
- }
- }
- }
-
- smartThreadPool.Shutdown();
-
- Assert.IsTrue(success);
- }
-
///
/// Example of how to interrupt the waiting for a work item to complete.
///
diff --git a/STPTests/TestWIGQueueWorkItem.cs b/STPTests/TestWIGQueueWorkItem.cs
new file mode 100644
index 0000000..a642ef8
--- /dev/null
+++ b/STPTests/TestWIGQueueWorkItem.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Amib.Threading;
+using SmartThreadPoolTests;
+
+namespace WorkItemsGroupTests
+{
+ ///
+ /// Tests for QueueWorkItem.
+ ///
+ [TestFixture]
+ [Category("TestQueueWorkItem")]
+ public class TestQueueWorkItem
+ {
+ private SmartThreadPool _stp;
+ private IWorkItemsGroup _wig;
+
+ [SetUp]
+ public void Init()
+ {
+ _stp = new SmartThreadPool();
+ _wig = _stp.CreateWorkItemsGroup(SmartThreadPool.DefaultMaxWorkerThreads);
+ }
+
+ [TearDown]
+ public void Fini()
+ {
+ _stp.Shutdown();
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback);
+ [Test]
+ public void TestQueueWorkItemCall()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCall(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallPrio(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
+ [Test]
+ public void TestQueueWorkItemCallStat()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStat(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallStatPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPrio(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
+ [Test]
+ public void TestQueueWorkItemCallStatPost()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPost(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallStatPostPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPostPrio(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
+ [Test]
+ public void TestQueueWorkItemCallStatPostPflg()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPostPflg(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
+ [Test]
+ public void TestQueueWorkItemCallStatPostPflgPrio()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemCallStatPostPflgPrio(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
+ [Test]
+ public void TestQueueWorkItemInfoCall()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemInfoCall(_wig);
+ }
+
+ //IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
+ [Test]
+ public void TestQueueWorkItemInfoCallStat()
+ {
+ QueueWorkItemHelper.TestQueueWorkItemInfoCallStat(_wig);
+ }
+ }
+}
diff --git a/STPTests/TestWIGWaitForIdle.cs b/STPTests/TestWIGWaitForIdle.cs
index 3a6841b..36a84ad 100644
--- a/STPTests/TestWIGWaitForIdle.cs
+++ b/STPTests/TestWIGWaitForIdle.cs
@@ -29,15 +29,17 @@ namespace WorkItemsGroupTests
IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
bool success = false;
+ ManualResetEvent isRunning = new ManualResetEvent(false);
- for(int i = 0; i < 100; ++i)
+ for (int i = 0; i < 4; ++i)
{
- workItemsGroup.QueueWorkItem(
- new WorkItemCallback(this.DoSomeWork),
- null);
+ workItemsGroup.QueueWorkItem(delegate { isRunning.WaitOne(); });
}
- success = !workItemsGroup.WaitForIdle(3500);
+ success = !workItemsGroup.WaitForIdle(1000);
+
+ isRunning.Set();
+
success = success && workItemsGroup.WaitForIdle(1000);
smartThreadPool.Shutdown();
@@ -61,7 +63,7 @@ namespace WorkItemsGroupTests
smartThreadPool.Shutdown();
Assert.IsNotNull(e);
- }
+ }
[Test]
public void WaitForIdleOnSTPThreadForAnotherWorkItemsGroup()
@@ -72,11 +74,11 @@ namespace WorkItemsGroupTests
workItemsGroup1.QueueWorkItem(
new WorkItemCallback(this.DoSomeWork),
- null);
+ 1000);
workItemsGroup1.QueueWorkItem(
new WorkItemCallback(this.DoSomeWork),
- null);
+ 1000);
IWorkItemResult wir = workItemsGroup2.QueueWorkItem(
new WorkItemCallback(this.DoWaitForIdle),
@@ -91,11 +93,16 @@ namespace WorkItemsGroupTests
}
- private int x = 0;
+ private int _x = 0;
private object DoSomeWork(object state)
- {
- Debug.WriteLine(Interlocked.Increment(ref x));
- Thread.Sleep(1000);
+ {
+ int sleepTime = (int)state;
+ int newX = Interlocked.Increment(ref _x);
+ Console.WriteLine("{0}: Enter, newX = {1}", DateTime.Now.ToLongTimeString(), newX);
+ Console.WriteLine("{0}: Sleeping for {1} ms", DateTime.Now.ToLongTimeString(), sleepTime);
+ Thread.Sleep(sleepTime);
+ newX = Interlocked.Increment(ref _x);
+ Console.WriteLine("{0}: Leave, newX = {1}", DateTime.Now.ToLongTimeString(), newX);
return 1;
}
@@ -105,5 +112,52 @@ namespace WorkItemsGroupTests
workItemsGroup.WaitForIdle();
return null;
}
+
+
+ [Test]
+ public void WaitForIdleWithCancel()
+ {
+ SmartThreadPool smartThreadPool = new SmartThreadPool(10 * 1000, 1, 1);
+ IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(2);
+
+ _x = 0;
+
+ IWorkItemResult wir1 = workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), 1000);
+ IWorkItemResult wir2 = workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), 1000);
+ IWorkItemResult wir3 = workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), 1000);
+
+ while (0 == _x)
+ {
+ Thread.Sleep(10);
+ }
+
+ Console.WriteLine("{0}: Cancelling WIG", DateTime.Now.ToLongTimeString());
+ workItemsGroup.Cancel();
+
+ // At this point:
+ // The first work item is running
+ // The second work item is cancelled, but waits in the STP queue
+ // The third work item is cancelled.
+
+ Assert.AreEqual(1, _x);
+
+ Assert.IsTrue(wir2.IsCanceled);
+
+ Assert.IsTrue(wir3.IsCanceled);
+
+ // Make sure the workItemsGroup is still idle
+ Assert.IsFalse(workItemsGroup.IsIdle);
+
+ Console.WriteLine("{0}: Waiting for 1st result", DateTime.Now.ToLongTimeString());
+ wir1.GetResult();
+
+ Assert.AreEqual(2, _x);
+
+ bool isIdle = workItemsGroup.WaitForIdle(100);
+
+ Assert.IsTrue(isIdle);
+
+ smartThreadPool.Shutdown();
+ }
}
}
diff --git a/STPTests/TestWaitForIdle.cs b/STPTests/TestWaitForIdle.cs
index 9731fc8..27d4ed7 100644
--- a/STPTests/TestWaitForIdle.cs
+++ b/STPTests/TestWaitForIdle.cs
@@ -28,15 +28,17 @@ namespace SmartThreadPoolTests
SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
bool success = false;
+ ManualResetEvent isRunning = new ManualResetEvent(false);
- for(int i = 0; i < 100; ++i)
+ for(int i = 0; i < 4; ++i)
{
- smartThreadPool.QueueWorkItem(
- new WorkItemCallback(this.DoSomeWork),
- null);
+ smartThreadPool.QueueWorkItem(delegate { isRunning.WaitOne(); });
}
- success = !smartThreadPool.WaitForIdle(3500);
+ success = !smartThreadPool.WaitForIdle(1000);
+
+ isRunning.Set();
+
success = success && smartThreadPool.WaitForIdle(1000);
smartThreadPool.Shutdown();
diff --git a/STPTests/TestWorkItemsGroups.cs b/STPTests/TestWorkItemsGroups.cs
index fd6db09..110a07d 100644
--- a/STPTests/TestWorkItemsGroups.cs
+++ b/STPTests/TestWorkItemsGroups.cs
@@ -70,53 +70,6 @@ namespace WorkItemsGroupTests
Assert.IsTrue(success);
}
- ///
- /// Example of how to queue a work item and then cancel it while it is in the queue.
- ///
- [Test]
- public void WorkItemCanceling()
- {
- // Create a SmartThreadPool with only one thread.
- // It just to show how to use the work item canceling feature
- SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 1);
- IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
-
- bool success = false;
-
- // Queue a work item that will occupy the thread in the pool
- IWorkItemResult wir1 =
- workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
-
- // Queue another work item that will wait for the first to complete
- IWorkItemResult wir2 =
- workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
-
- // Wait a while for the thread pool to start executing the first work item
- Thread.Sleep(100);
-
- // The first work item cannot be canceled since it is currently executing
- if (!wir1.Cancel())
- {
- // Cancel the second work item while it still in the queue
- if (wir2.Cancel())
- {
- try
- {
- // Retreiving result of a canceled work item throws an exception
- wir2.GetResult();
- }
- catch (WorkItemCancelException)
- {
- success = true;
- }
- }
- }
-
- smartThreadPool.Shutdown();
-
- Assert.IsTrue(success);
- }
-
///
/// Example of how to interrupt the waiting for a work item to complete.
///
diff --git a/SmartThreadPool.sln b/SmartThreadPool.sln
index 89065a9..b17b4c8 100644
--- a/SmartThreadPool.sln
+++ b/SmartThreadPool.sln
@@ -1,14 +1,53 @@
+
Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartThreadPool", "SmartThreadPool\SmartThreadPool.csproj", "{8684FC56-A679-4E2E-8F96-E172FB062EB6}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartThreadPoolCE", "SmartThreadPool\SmartThreadPoolCE.csproj", "{D81DD596-C71F-4AC2-816C-63C19589E7E0}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartThreadPool", "SmartThreadPool\SmartThreadPool.csproj", "{74D4C33F-7CC8-4B2A-A7DF-D8B6E63B6EBD}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STPExamples", "STPExamples\STPExamples.csproj", "{AE943A5A-7CFD-4E0D-BA51-FB763AAEA9A3}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STPTests", "STPTests\STPTests.csproj", "{6A3E4DBF-12AD-4636-ACB3-24B5172FAE03}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageControl", "UsageControl\UsageControl.csproj", "{C11A4561-CCB5-4C96-8DF2-B804031D89D8}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestSmartThreadPool", "TestSmartThreadPool\TestSmartThreadPool.csproj", "{976DB12F-9198-4AD9-981A-1652615C9B0D}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageControl", "UsageControl\UsageControl.csproj", "{C11A4561-CCB5-4C96-8DF2-B804031D89D8}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkItemsGroupDemo", "WorkItemsGroupDemo\WorkItemsGroupDemo.csproj", "{DC005A64-FAE9-4CFA-ADC8-F1D1FE7FE6CD}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "STPCEDemo", "STPCEDemo\STPCEDemo.csproj", "{356114BA-5F10-4DB6-9971-987CE11F765F}"
+ ProjectSection(WebsiteProperties) = preProject
+ Debug.AspNetCompiler.Debug = "True"
+ Release.AspNetCompiler.Debug = "False"
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -16,26 +55,41 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {8684FC56-A679-4E2E-8F96-E172FB062EB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8684FC56-A679-4E2E-8F96-E172FB062EB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8684FC56-A679-4E2E-8F96-E172FB062EB6}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {8684FC56-A679-4E2E-8F96-E172FB062EB6}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {D81DD596-C71F-4AC2-816C-63C19589E7E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D81DD596-C71F-4AC2-816C-63C19589E7E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D81DD596-C71F-4AC2-816C-63C19589E7E0}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {D81DD596-C71F-4AC2-816C-63C19589E7E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D81DD596-C71F-4AC2-816C-63C19589E7E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {74D4C33F-7CC8-4B2A-A7DF-D8B6E63B6EBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {74D4C33F-7CC8-4B2A-A7DF-D8B6E63B6EBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {74D4C33F-7CC8-4B2A-A7DF-D8B6E63B6EBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {74D4C33F-7CC8-4B2A-A7DF-D8B6E63B6EBD}.Release|Any CPU.Build.0 = Release|Any CPU
{AE943A5A-7CFD-4E0D-BA51-FB763AAEA9A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE943A5A-7CFD-4E0D-BA51-FB763AAEA9A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AE943A5A-7CFD-4E0D-BA51-FB763AAEA9A3}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {AE943A5A-7CFD-4E0D-BA51-FB763AAEA9A3}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {AE943A5A-7CFD-4E0D-BA51-FB763AAEA9A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AE943A5A-7CFD-4E0D-BA51-FB763AAEA9A3}.Release|Any CPU.Build.0 = Release|Any CPU
{6A3E4DBF-12AD-4636-ACB3-24B5172FAE03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A3E4DBF-12AD-4636-ACB3-24B5172FAE03}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {6A3E4DBF-12AD-4636-ACB3-24B5172FAE03}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {6A3E4DBF-12AD-4636-ACB3-24B5172FAE03}.Release|Any CPU.Build.0 = Debug|Any CPU
- {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {6A3E4DBF-12AD-4636-ACB3-24B5172FAE03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6A3E4DBF-12AD-4636-ACB3-24B5172FAE03}.Release|Any CPU.Build.0 = Release|Any CPU
{976DB12F-9198-4AD9-981A-1652615C9B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{976DB12F-9198-4AD9-981A-1652615C9B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {976DB12F-9198-4AD9-981A-1652615C9B0D}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {976DB12F-9198-4AD9-981A-1652615C9B0D}.Release|Any CPU.Build.0 = Debug|Any CPU
+ {976DB12F-9198-4AD9-981A-1652615C9B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {976DB12F-9198-4AD9-981A-1652615C9B0D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C11A4561-CCB5-4C96-8DF2-B804031D89D8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DC005A64-FAE9-4CFA-ADC8-F1D1FE7FE6CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DC005A64-FAE9-4CFA-ADC8-F1D1FE7FE6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DC005A64-FAE9-4CFA-ADC8-F1D1FE7FE6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DC005A64-FAE9-4CFA-ADC8-F1D1FE7FE6CD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {356114BA-5F10-4DB6-9971-987CE11F765F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {356114BA-5F10-4DB6-9971-987CE11F765F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {356114BA-5F10-4DB6-9971-987CE11F765F}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {356114BA-5F10-4DB6-9971-987CE11F765F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {356114BA-5F10-4DB6-9971-987CE11F765F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {356114BA-5F10-4DB6-9971-987CE11F765F}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SmartThreadPool/AssemblyInfo.cs b/SmartThreadPool/AssemblyInfo.cs
deleted file mode 100644
index 471d9c4..0000000
--- a/SmartThreadPool/AssemblyInfo.cs
+++ /dev/null
@@ -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("1.0.*")]
-
-//
-// 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\. 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("")]
diff --git a/SmartThreadPool/CallerThreadContext.cs b/SmartThreadPool/CallerThreadContext.cs
index 9fe7941..9298c0c 100644
--- a/SmartThreadPool/CallerThreadContext.cs
+++ b/SmartThreadPool/CallerThreadContext.cs
@@ -1,3 +1,6 @@
+
+#if !(WindowsCE)
+
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();
@@ -31,18 +34,20 @@ 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";
+ if (fi != null)
+ {
+ return (string) fi.GetValue(null);
+ }
+
+ return "HttpContext";
}
#endregion
#region Private fields
- private HttpContext _httpContext = null;
- private LogicalCallContext _callContext = null;
+ private HttpContext _httpContext;
+ private LogicalCallContext _callContext;
#endregion
@@ -118,15 +123,16 @@ namespace Amib.Threading
{
setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
}
-
- // Restore HttpContext
- if (callerThreadContext._httpContext != null)
- {
+
+ // Restore HttpContext
+ if (callerThreadContext._httpContext != null)
+ {
HttpContext.Current = callerThreadContext._httpContext;
- }
+ //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
+ }
}
}
#endregion
-
}
+#endif
diff --git a/SmartThreadPool/EventWaitHandle.cs b/SmartThreadPool/EventWaitHandle.cs
new file mode 100644
index 0000000..8fc52ef
--- /dev/null
+++ b/SmartThreadPool/EventWaitHandle.cs
@@ -0,0 +1,99 @@
+#if (WindowsCE)
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Amib.Threading.Internal
+{
+ ///
+ /// 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!
+ ///
+ public class EventWaitHandle
+ {
+ #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
+
+ 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
\ No newline at end of file
diff --git a/SmartThreadPool/EventWaitHandleFactory.cs b/SmartThreadPool/EventWaitHandleFactory.cs
new file mode 100644
index 0000000..3fe0a9c
--- /dev/null
+++ b/SmartThreadPool/EventWaitHandleFactory.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Diagnostics;
+
+namespace Amib.Threading.Internal
+{
+ ///
+ /// 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.
+ ///
+ public static class EventWaitHandleFactory
+ {
+ ///
+ /// Create a new AutoResetEvent object
+ ///
+ /// Return a new AutoResetEvent object
+ public static AutoResetEvent CreateAutoResetEvent()
+ {
+ AutoResetEvent waitHandle = new AutoResetEvent(false);
+
+#if (WindowsCE)
+ ReplaceEventHandle(waitHandle, false, false);
+#endif
+
+ return waitHandle;
+ }
+
+ ///
+ /// Create a new ManualResetEvent object
+ ///
+ /// Return a new ManualResetEvent object
+ public static ManualResetEvent CreateManualResetEvent(bool initialState)
+ {
+ ManualResetEvent waitHandle = new ManualResetEvent(initialState);
+
+#if (WindowsCE)
+ ReplaceEventHandle(waitHandle, true, initialState);
+#endif
+
+ return waitHandle;
+ }
+
+#if (WindowsCE)
+
+ ///
+ /// Replace the event handle
+ ///
+ /// The WaitHandle object which its handle needs to be replaced.
+ /// Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false)
+ /// The initial state of the event
+ 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
+
+ }
+}
diff --git a/SmartThreadPool/Exceptions.cs b/SmartThreadPool/Exceptions.cs
index 6ebca30..bda4bd7 100644
--- a/SmartThreadPool/Exceptions.cs
+++ b/SmartThreadPool/Exceptions.cs
@@ -1,81 +1,111 @@
-// Ami Bar
-// amibar@gmail.com
-
using System;
+#if !(WindowsCE)
using System.Runtime.Serialization;
+#endif
namespace Amib.Threading
{
- #region Exceptions
+ #region Exceptions
- ///
- /// Represents an exception in case IWorkItemResult.GetResult has been canceled
- ///
- [Serializable]
- public sealed class WorkItemCancelException : ApplicationException
- {
- 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)
- {
- }
- }
-
- ///
- /// Represents an exception in case IWorkItemResult.GetResult has been timed out
- ///
- [Serializable]
- public sealed class WorkItemTimeoutException : ApplicationException
+ ///
+ /// Represents an exception in case IWorkItemResult.GetResult has been canceled
+ ///
+ public sealed partial class WorkItemCancelException : ApplicationException
{
- public WorkItemTimeoutException() : base()
+ public WorkItemCancelException()
{
}
- public WorkItemTimeoutException(string message) : base(message)
+ public WorkItemCancelException(string message)
+ : base(message)
{
}
- public WorkItemTimeoutException(string message, Exception e) : base(message, e)
- {
- }
-
- public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) : base(si, sc)
+ public WorkItemCancelException(string message, Exception e)
+ : base(message, e)
{
}
}
- ///
- /// Represents an exception in case IWorkItemResult.GetResult has been timed out
- ///
- [Serializable]
- public sealed class WorkItemResultException : ApplicationException
- {
- public WorkItemResultException() : base()
- {
- }
+ ///
+ /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+ ///
+ public sealed partial class WorkItemTimeoutException : ApplicationException
+ {
+ public WorkItemTimeoutException()
+ {
+ }
- public WorkItemResultException(string message) : base(message)
- {
- }
+ public WorkItemTimeoutException(string message)
+ : base(message)
+ {
+ }
- public WorkItemResultException(string message, Exception e) : base(message, e)
- {
- }
+ public WorkItemTimeoutException(string message, Exception e)
+ : base(message, e)
+ {
+ }
+ }
- public WorkItemResultException(SerializationInfo si, StreamingContext sc) : base(si, sc)
- {
- }
- }
+ ///
+ /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+ ///
+ public sealed partial class WorkItemResultException : ApplicationException
+ {
+ public WorkItemResultException()
+ {
+ }
- #endregion
+ public WorkItemResultException(string message)
+ : base(message)
+ {
+ }
+
+ public WorkItemResultException(string message, Exception e)
+ : base(message, e)
+ {
+ }
+ }
+
+
+#if !(WindowsCE)
+ ///
+ /// Represents an exception in case IWorkItemResult.GetResult has been canceled
+ ///
+ [Serializable]
+ public sealed partial class WorkItemCancelException
+ {
+ public WorkItemCancelException(SerializationInfo si, StreamingContext sc)
+ : base(si, sc)
+ {
+ }
+ }
+
+ ///
+ /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+ ///
+ [Serializable]
+ public sealed partial class WorkItemTimeoutException
+ {
+ public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc)
+ : base(si, sc)
+ {
+ }
+ }
+
+ ///
+ /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+ ///
+ [Serializable]
+ public sealed partial class WorkItemResultException
+ {
+ public WorkItemResultException(SerializationInfo si, StreamingContext sc)
+ : base(si, sc)
+ {
+ }
+ }
+
+#endif
+
+ #endregion
}
diff --git a/SmartThreadPool/Interfaces.cs b/SmartThreadPool/Interfaces.cs
index 9170186..ad8c63f 100644
--- a/SmartThreadPool/Interfaces.cs
+++ b/SmartThreadPool/Interfaces.cs
@@ -1,6 +1,3 @@
-// Ami Bar
-// amibar@gmail.com
-
using System;
using System.Threading;
@@ -18,7 +15,13 @@ namespace Amib.Threading
/// A delegate to call after the WorkItemCallback completed
///
/// The work item result object
- public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
+ public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
+
+ ///
+ /// A delegate to call after the WorkItemCallback completed
+ ///
+ /// The work item result object
+ public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);
///
/// A delegate to call when a WorkItemsGroup becomes idle
@@ -26,10 +29,27 @@ namespace Amib.Threading
/// A reference to the WorkItemsGroup that became idle
public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup);
+ ///
+ /// A delegate to call after a thread is created, but before
+ /// it's first use.
+ ///
+ public delegate void ThreadInitializationHandler();
+
+ ///
+ /// A delegate to call when a thread is about to exit, after
+ /// it is no longer belong to the pool.
+ ///
+ public delegate void ThreadTerminationHandler();
+
#endregion
#region WorkItem Priority
+ ///
+ /// Defines the availeable priorities of a work item.
+ /// The higher the priority a work item has, the sooner
+ /// it will be executed.
+ ///
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
///
/// IWorkItemsGroup interface
+ /// Created by SmartThreadPool.CreateWorkItemsGroup()
///
public interface IWorkItemsGroup
{
@@ -62,28 +74,264 @@ namespace Amib.Threading
///
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);
+ ///
+ /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
+ ///
+ int Concurrency { get; set; }
- IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
- IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
+ ///
+ /// Get the number of work items waiting in the queue.
+ ///
+ int WaitingCallbacks { get; }
+ ///
+ /// Get an array with all the state objects of the currently running items.
+ /// The array represents a snap shot and impact performance.
+ ///
+ object[] GetStates();
+
+ ///
+ /// Get the WorkItemsGroup start information
+ ///
+ WIGStartInfo WIGStartInfo { get; }
+
+ ///
+ /// Starts to execute work items
+ ///
+ void Start();
+
+ ///
+ /// Cancel all the work items.
+ /// Same as Cancel(false)
+ ///
+ void Cancel();
+
+ ///
+ /// Cancel all work items using thread abortion
+ ///
+ /// True to stop work items by raising ThreadAbortException
+ void Cancel(bool abortExecution);
+
+ ///
+ /// Wait for all work item to complete.
+ ///
void WaitForIdle();
+
+ ///
+ /// Wait for all work item to complete, until timeout expired
+ ///
+ /// How long to wait for the work items to complete
+ /// Returns true if work items completed within the timeout, otherwise false.
bool WaitForIdle(TimeSpan timeout);
- bool WaitForIdle(int millisecondsTimeout);
- int WaitingCallbacks { get; }
- event WorkItemsGroupIdleHandler OnIdle;
+ ///
+ /// Wait for all work item to complete, until timeout expired
+ ///
+ /// How long to wait for the work items to complete in milliseconds
+ /// Returns true if work items completed within the timeout, otherwise false.
+ bool WaitForIdle(int millisecondsTimeout);
- void Cancel();
- void Start();
- }
+ ///
+ /// IsIdle is true when there are no work items running or queued.
+ ///
+ bool IsIdle { get; }
+
+ ///
+ /// 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.
+ ///
+ event WorkItemsGroupIdleHandler OnIdle;
+
+ #region QueueWorkItem
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback);
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ /// The priority of the work item
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority);
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state);
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ /// The work item priority
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// The work item priority
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// Indicates on which cases to call to the post execute callback
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// Indicates on which cases to call to the post execute callback
+ /// The work item priority
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
+
+ ///
+ /// Queue a work item
+ ///
+ /// Work item info
+ /// A callback to execute
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback);
+
+ ///
+ /// Queue a work item
+ ///
+ /// Work item information
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ /// Returns a work item result
+ IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
+
+ #endregion
+
+ #region QueueWorkItem(Action<...>)
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object, but its GetResult() will always return null
+ IWorkItemResult QueueWorkItem(Action action);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object, but its GetResult() will always return null
+ IWorkItemResult QueueWorkItem(Action action, T arg);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object, but its GetResult() will always return null
+ IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object, but its GetResult() will always return null
+ IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object, but its GetResult() will always return null
+ IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+ #endregion
+
+ #region QueueWorkItem(Func<...>)
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object.
+ /// its GetResult() returns a TResult object
+ IWorkItemResult QueueWorkItem(Func func);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object.
+ /// its GetResult() returns a TResult object
+ IWorkItemResult QueueWorkItem(Func func, T arg);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object.
+ /// its GetResult() returns a TResult object
+ IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object.
+ /// its GetResult() returns a TResult object
+ IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3);
+
+ ///
+ /// Queue a work item.
+ ///
+ /// Returns a IWorkItemResult object.
+ /// its GetResult() returns a TResult object
+ IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+ #endregion
+ }
#endregion
@@ -92,9 +340,24 @@ namespace Amib.Threading
[Flags]
public enum CallToPostExecute
{
+ ///
+ /// Never call to the PostExecute call back
+ ///
Never = 0x00,
+
+ ///
+ /// Call to the PostExecute only when the work item is cancelled
+ ///
WhenWorkItemCanceled = 0x01,
+
+ ///
+ /// Call to the PostExecute only when the work item is not cancelled
+ ///
WhenWorkItemNotCanceled = 0x02,
+
+ ///
+ /// Always call to the PostExecute
+ ///
Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
}
@@ -102,17 +365,44 @@ namespace Amib.Threading
#region IWorkItemResult interface
+ ///
+ /// The common interface of IWorkItemResult and IWorkItemResult
+ ///
+ public interface IWaitableResult
+ {
+ ///
+ /// This method intent is for internal use.
+ ///
+ ///
+ IWorkItemResult GetWorkItemResult();
+
+ ///
+ /// This method intent is for internal use.
+ ///
+ ///
+ IWorkItemResult GetWorkItemResultT();
+ }
+
+ ///
+ /// IWorkItemResult interface.
+ /// Created when a WorkItemCallback work item is queued.
+ ///
+ public interface IWorkItemResult : IWorkItemResult
+ {
+ }
+
///
- /// IWorkItemResult interface
+ /// IWorkItemResult interface.
+ /// Created when a Func work item is queued.
///
- public interface IWorkItemResult
+ public interface IWorkItemResult : IWaitableResult
{
///
/// Get the result of the work item.
/// If the work item didn't run yet then the caller waits.
///
/// The result of the work item
- object GetResult();
+ TResult GetResult();
///
/// Get the result of the work item.
@@ -120,7 +410,7 @@ namespace Amib.Threading
///
/// The result of the work item
/// On timeout throws WorkItemTimeoutException
- object GetResult(
+ TResult GetResult(
int millisecondsTimeout,
bool exitContext);
@@ -130,7 +420,7 @@ namespace Amib.Threading
///
/// The result of the work item
/// On timeout throws WorkItemTimeoutException
- object GetResult(
+ TResult GetResult(
TimeSpan timeout,
bool exitContext);
@@ -146,7 +436,7 @@ namespace Amib.Threading
/// The result of the work item
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
- object GetResult(
+ TResult GetResult(
int millisecondsTimeout,
bool exitContext,
WaitHandle cancelWaitHandle);
@@ -158,7 +448,7 @@ namespace Amib.Threading
/// The result of the work item
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
- object GetResult(
+ TResult GetResult(
TimeSpan timeout,
bool exitContext,
WaitHandle cancelWaitHandle);
@@ -169,16 +459,18 @@ namespace Amib.Threading
///
/// Filled with the exception if one was thrown
/// The result of the work item
- object GetResult(out Exception e);
+ TResult GetResult(out Exception e);
///
/// Get the result of the work item.
/// If the work item didn't run yet then the caller waits until timeout.
///
+ ///
+ ///
/// Filled with the exception if one was thrown
/// The result of the work item
/// On timeout throws WorkItemTimeoutException
- object GetResult(
+ TResult GetResult(
int millisecondsTimeout,
bool exitContext,
out Exception e);
@@ -187,10 +479,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.
///
+ ///
/// Filled with the exception if one was thrown
+ ///
/// The result of the work item
/// On timeout throws WorkItemTimeoutException
- object GetResult(
+ TResult GetResult(
TimeSpan timeout,
bool exitContext,
out Exception e);
@@ -208,7 +502,7 @@ namespace Amib.Threading
/// The result of the work item
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
- object GetResult(
+ TResult GetResult(
int millisecondsTimeout,
bool exitContext,
WaitHandle cancelWaitHandle,
@@ -219,10 +513,13 @@ namespace Amib.Threading
/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
///
/// The result of the work item
+ ///
/// Filled with the exception if one was thrown
+ ///
+ ///
/// On timeout throws WorkItemTimeoutException
/// On cancel throws WorkItemCancelException
- object GetResult(
+ TResult GetResult(
TimeSpan timeout,
bool exitContext,
WaitHandle cancelWaitHandle,
@@ -239,15 +536,29 @@ namespace Amib.Threading
bool IsCanceled { get; }
///
- /// 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.
///
object State { get; }
///
- /// Cancel the work item if it didn't start running yet.
+ /// Same as Cancel(false).
///
- /// Returns true on success or false if the work item is in progress or already completed
- bool Cancel();
+ bool Cancel();
+
+ ///
+ /// 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
+ ///
+ /// When true send an AbortException to the executing thread.
+ /// Returns true if the work item was not completed, otherwise false.
+ bool Cancel(bool abortExecution);
///
/// Get the work item's priority
@@ -257,7 +568,7 @@ namespace Amib.Threading
///
/// Return the result, same as GetResult()
///
- object Result { get; }
+ TResult Result { get; }
///
/// Returns the exception if occured otherwise returns null.
@@ -266,4 +577,22 @@ namespace Amib.Threading
}
#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 arg1, T2 arg2);
+ public delegate void Action(T1 arg1, T2 arg2, T3 arg3);
+ public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+ public delegate TResult Func();
+ public delegate TResult Func(T arg1);
+ public delegate TResult Func(T1 arg1, T2 arg2);
+ public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3);
+ public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+ #endregion
}
diff --git a/SmartThreadPool/PriorityQueue.cs b/SmartThreadPool/PriorityQueue.cs
index 8ff9da0..b7df9fd 100644
--- a/SmartThreadPool/PriorityQueue.cs
+++ b/SmartThreadPool/PriorityQueue.cs
@@ -1,6 +1,3 @@
-// Ami Bar
-// amibar@gmail.com
-
using System;
using System.Collections;
using System.Diagnostics;
@@ -25,17 +22,17 @@ namespace Amib.Threading.Internal
///
/// Work items queues. There is one for each type of priority
///
- private Queue [] _queues = new Queue[_queuesCount];
+ private readonly Queue [] _queues = new Queue[_queuesCount];
///
/// The total number of work items within the queues
///
- private int _workItemsCount = 0;
+ private int _workItemsCount;
///
/// Use with IEnumerable interface
///
- private int _version = 0;
+ private int _version;
#endregion
@@ -159,7 +156,7 @@ namespace Amib.Threading.Internal
///
private class PriorityQueueEnumerator : IEnumerator
{
- private PriorityQueue _priorityQueue;
+ private readonly PriorityQueue _priorityQueue;
private int _version;
private int _queueIndex;
private IEnumerator _enumerator;
diff --git a/SmartThreadPool/Properties/AssemblyInfo.cs b/SmartThreadPool/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..1317481
--- /dev/null
+++ b/SmartThreadPool/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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("Amib.Threading")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Amib.Threading")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")]
+
+// 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("2.0.0.0")]
+
diff --git a/SmartThreadPool/STPPerformanceCounter.cs b/SmartThreadPool/STPPerformanceCounter.cs
index 2ee88b4..d0d1baa 100644
--- a/SmartThreadPool/STPPerformanceCounter.cs
+++ b/SmartThreadPool/STPPerformanceCounter.cs
@@ -3,7 +3,17 @@ using System.Diagnostics;
namespace Amib.Threading.Internal
{
- internal enum STPPerformanceCounterType
+ 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 !(WindowsCE)
+
+ internal enum STPPerformanceCounterType
{
// Fields
ActiveThreads = 0,
@@ -37,7 +47,7 @@ namespace Amib.Threading.Internal
internal class STPPerformanceCounter
{
// Fields
- private PerformanceCounterType _pcType;
+ private readonly PerformanceCounterType _pcType;
protected string _counterHelp;
protected string _counterName;
@@ -47,9 +57,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 +86,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";
@@ -149,16 +159,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(
@@ -169,10 +181,6 @@ namespace Amib.Threading.Internal
_pcs.RawValue = _pcs.RawValue;
}
- ~STPInstancePerformanceCounter()
- {
- Close();
- }
public void Close()
{
@@ -186,9 +194,20 @@ namespace Amib.Threading.Internal
public void Dispose()
{
- Close();
- GC.SuppressFinalize(this);
+ Dispose(true);
}
+
+ public virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ Close();
+ }
+ }
+ _isDisposed = true;
+ }
public virtual void Increment()
{
@@ -209,27 +228,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()
@@ -239,8 +250,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)
@@ -265,23 +281,29 @@ namespace Amib.Threading.Internal
{
if (null != _pcs[i])
{
- _pcs[i].Close();
+ _pcs[i].Dispose();
}
}
_pcs = null;
}
}
- ~STPInstancePerformanceCounters()
- {
- Close();
- }
-
public void Dispose()
{
- Close();
- GC.SuppressFinalize(this);
+ Dispose(true);
}
+
+ public virtual void Dispose(bool disposing)
+ {
+ if (!_isDisposed)
+ {
+ if (disposing)
+ {
+ Close();
+ }
+ }
+ _isDisposed = true;
+ }
private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
{
@@ -319,22 +341,18 @@ namespace Amib.Threading.Internal
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds);
GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
}
- }
+ }
+ #endif
- internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, IDisposable
+ internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters
{
- 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() {}
@@ -342,6 +360,5 @@ namespace Amib.Threading.Internal
public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
- }
-
+ }
}
diff --git a/SmartThreadPool/STPStartInfo.cs b/SmartThreadPool/STPStartInfo.cs
index c205a69..133bbbe 100644
--- a/SmartThreadPool/STPStartInfo.cs
+++ b/SmartThreadPool/STPStartInfo.cs
@@ -1,6 +1,4 @@
-// Ami Bar
-// amibar@gmail.com
-
+using System;
using System.Threading;
namespace Amib.Threading
@@ -8,82 +6,220 @@ namespace Amib.Threading
///
/// Summary description for STPStartInfo.
///
- public class STPStartInfo : WIGStartInfo
- {
- ///
- /// Idle timeout in milliseconds.
- /// If a thread is idle for _idleTimeout milliseconds then
- /// it may quit.
- ///
- private int _idleTimeout;
+ public class STPStartInfo : WIGStartInfo
+ {
+ private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
+ private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
+ private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
+ private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
+ private string _pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
- ///
- /// The lower limit of threads in the pool.
- ///
- private int _minWorkerThreads;
+ public STPStartInfo()
+ {
+ }
- ///
- /// The upper limit of threads in the pool.
- ///
- private int _maxWorkerThreads;
-
- ///
- /// The priority of the threads in the pool
- ///
- private ThreadPriority _threadPriority;
-
- ///
- /// If this field is not null then the performance counters are enabled
- /// and use the string as the name of the instance.
- ///
- private string _pcInstanceName;
-
- public STPStartInfo() : base()
- {
- _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
- _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
- _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
- _threadPriority = SmartThreadPool.DefaultThreadPriority;
- _pcInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
- }
-
- public STPStartInfo(STPStartInfo stpStartInfo) : base(stpStartInfo)
+ public STPStartInfo(STPStartInfo stpStartInfo)
+ : base(stpStartInfo)
{
_idleTimeout = stpStartInfo._idleTimeout;
_minWorkerThreads = stpStartInfo._minWorkerThreads;
_maxWorkerThreads = stpStartInfo._maxWorkerThreads;
- _threadPriority = stpStartInfo._threadPriority;
- _pcInstanceName = stpStartInfo._pcInstanceName;
- }
+ _threadPriority = stpStartInfo._threadPriority;
+ _pcInstanceName = stpStartInfo._pcInstanceName;
+ }
- public int IdleTimeout
+ ///
+ /// Get/Set the idle timeout in milliseconds.
+ /// If a thread is idle (starved) longer than IdleTimeout then it may quit.
+ ///
+ public virtual int IdleTimeout
{
get { return _idleTimeout; }
set { _idleTimeout = value; }
}
- public int MinWorkerThreads
+ ///
+ /// Get/Set the lower limit of threads in the pool.
+ ///
+ public virtual int MinWorkerThreads
{
get { return _minWorkerThreads; }
set { _minWorkerThreads = value; }
}
- public int MaxWorkerThreads
+ ///
+ /// Get/Set the upper limit of threads in the pool.
+ ///
+ public virtual int MaxWorkerThreads
{
get { return _maxWorkerThreads; }
set { _maxWorkerThreads = value; }
}
- public ThreadPriority ThreadPriority
- {
- get { return _threadPriority; }
- set { _threadPriority = value; }
- }
+ ///
+ /// Get/Set the scheduling priority of the threads in the pool.
+ /// The Os handles the scheduling.
+ ///
+ public virtual ThreadPriority ThreadPriority
+ {
+ get { return _threadPriority; }
+ set { _threadPriority = value; }
+ }
- public string PerformanceCounterInstanceName
- {
- get { return _pcInstanceName; }
- set { _pcInstanceName = value; }
- }
- }
+ ///
+ /// Get/Set the performance counter instance name of this SmartThreadPool
+ /// The default is null which indicate not to use performance counters at all.
+ ///
+ public virtual string PerformanceCounterInstanceName
+ {
+ get { return _pcInstanceName; }
+ set { _pcInstanceName = value; }
+ }
+
+ ///
+ /// Get a readonly version of this STPStartInfo.
+ ///
+ /// Returns a readonly reference to this STPStartInfo
+ public new STPStartInfo AsReadOnly()
+ {
+ return new STPStartInfoRO(this);
+ }
+
+ #region STPStartInfoRO class
+
+ private class STPStartInfoRO : STPStartInfo
+ {
+ private readonly STPStartInfo _stpStartInfo;
+
+ public STPStartInfoRO(STPStartInfo stpStartInfo)
+ {
+ _stpStartInfo = stpStartInfo;
+ }
+
+ ///
+ /// Get/Set the idle timeout in milliseconds.
+ /// If a thread is idle (starved) longer than IdleTimeout then it may quit.
+ ///
+ public override int IdleTimeout
+ {
+ get { return _stpStartInfo.IdleTimeout; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the lower limit of threads in the pool.
+ ///
+ public override int MinWorkerThreads
+ {
+ get { return _stpStartInfo.MinWorkerThreads; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the upper limit of threads in the pool.
+ ///
+ public override int MaxWorkerThreads
+ {
+ get { return _stpStartInfo.MaxWorkerThreads; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the scheduling priority of the threads in the pool.
+ /// The Os handles the scheduling.
+ ///
+ public override ThreadPriority ThreadPriority
+ {
+ get { return _stpStartInfo.ThreadPriority; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the performance counter instance name of this SmartThreadPool
+ /// The default is null which indicate not to use performance counters at all.
+ ///
+ public override string PerformanceCounterInstanceName
+ {
+ get { return _stpStartInfo.PerformanceCounterInstanceName; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get if to use the caller's security context
+ ///
+ public override bool UseCallerCallContext
+ {
+ get { return _stpStartInfo.UseCallerCallContext; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get if to use the caller's HTTP context
+ ///
+ public override bool UseCallerHttpContext
+ {
+ get { return _stpStartInfo.UseCallerHttpContext; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get if to dispose of the state object of a work item
+ ///
+ public override bool DisposeOfStateObjects
+ {
+ get { return _stpStartInfo.DisposeOfStateObjects; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the run the post execute options
+ ///
+ public override CallToPostExecute CallToPostExecute
+ {
+ get { return _stpStartInfo.CallToPostExecute; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the default post execute callback
+ ///
+ public override PostExecuteWorkItemCallback PostExecuteWorkItemCallback
+ {
+ get { return _stpStartInfo.PostExecuteWorkItemCallback; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get if the work items execution should be suspended until the Start()
+ /// method is called.
+ ///
+ public override bool StartSuspended
+ {
+ get { return _stpStartInfo.StartSuspended; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the default priority that a work item gets when it is enqueued
+ ///
+ public override WorkItemPriority WorkItemPriority
+ {
+ get { return _stpStartInfo.WorkItemPriority; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Indicate if QueueWorkItem of Action<...>/Func<...> fill the
+ /// arguments as an object array into the state of the work item.
+ /// The arguments can be access later by IWorkItemResult.State.
+ ///
+ public override bool FillStateWithArgs
+ {
+ get { return _stpStartInfo.FillStateWithArgs; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+ }
+
+ #endregion
+ }
}
diff --git a/SmartThreadPool/SmartThreadPool.cs b/SmartThreadPool/SmartThreadPool.cs
index b627053..a192008 100644
--- a/SmartThreadPool/SmartThreadPool.cs
+++ b/SmartThreadPool/SmartThreadPool.cs
@@ -1,7 +1,6 @@
-// Ami Bar
-// amibar@gmail.com
-//
-// Smart thread pool in C#.
+#region Release History
+
+// Smart Thread Pool
// 7 Aug 2004 - Initial release
// 14 Sep 2004 - Bug fixes
// 15 Oct 2004 - Added new features
@@ -51,6 +50,22 @@
// their won't be a Performance Counter leak.
// - Added exception catch in case the Performance Counters cannot
// be created.
+//
+// 17 May 2008 - Changes:
+// - Changed the dispose behavior and removed the Finalizers.
+// - Enabled the change of the MaxThreads and MinThreads at run time.
+// - Enabled the change of the Concurrency of a IWorkItemsGroup at run
+// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency
+// refers to the MaxThreads.
+// - Improved the cancel behavior.
+// - Added events for thread creation and termination.
+// - Fixed the HttpContext context capture.
+// - Changed internal collections so they use generic collections
+// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup
+// - Added support for WinCE
+// - Added support for Action and Func
+
+#endregion
using System;
using System.Security;
@@ -67,9 +82,9 @@ namespace Amib.Threading
///
/// Smart thread pool class.
///
- public class SmartThreadPool : IWorkItemsGroup, IDisposable
+ public class SmartThreadPool : WorkItemsGroupBase, IDisposable
{
- #region Default Constants
+ #region Public Default Constants
///
/// Default minimum number of threads the thread pool contains. (0)
@@ -102,67 +117,122 @@ namespace Amib.Threading
public const bool DefaultDisposeOfStateObjects = false;
///
- /// The default option to run the post execute
+ /// The default option to run the post execute (CallToPostExecute.Always)
///
public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always;
///
- /// The default post execute method to run.
+ /// The default post execute method to run. (None)
/// When null it means not to call it.
///
- public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback = null;
+ public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback;
///
- /// The default work item priority
+ /// The default work item priority (WorkItemPriority.Normal)
///
public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;
///
/// The default is to work on work items as soon as they arrive
- /// and not to wait for the start.
+ /// and not to wait for the start. (false)
///
public const bool DefaultStartSuspended = false;
///
- /// The default is not to use the performance counters
+ /// The default name to use for the performance counters instance. (null)
///
- public static readonly string DefaultPerformanceCounterInstanceName = null;
+ public static readonly string DefaultPerformanceCounterInstanceName;
///
- /// The default thread priority
+ /// The default thread priority (ThreadPriority.Normal)
///
public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal;
+ ///
+ /// The default fill state with params. (false)
+ /// It is relevant only to QueueWorkItem of Action<...>/Func<...>
+ ///
+ public const bool DefaultFillStateWithArgs = false;
+
#endregion
- #region Member Variables
+ #region ThreadEntry class
+
+ private class ThreadEntry
+ {
+ ///
+ /// The thread creation time
+ ///
+ private DateTime _creationTime;
+
+ ///
+ /// The last time this thread has been running
+ /// It is updated by IAmAlive() method
+ ///
+ private DateTime _lastAliveTime;
+
+ ///
+ /// A reference to the current work item a thread from the thread pool
+ /// is executing.
+ ///
+ private WorkItem _currentWorkItem;
+
+ ///
+ /// 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.
+ ///
+ private SmartThreadPool _associatedSmartThreadPool;
+
+ public ThreadEntry(SmartThreadPool stp)
+ {
+ _associatedSmartThreadPool = stp;
+ _creationTime = DateTime.Now;
+ _lastAliveTime = DateTime.MinValue;
+ }
+
+ public WorkItem CurrentWorkItem
+ {
+ get { return _currentWorkItem; }
+ set { _currentWorkItem = value; }
+ }
+
+ public SmartThreadPool AssociatedSmartThreadPool
+ {
+ get { return _associatedSmartThreadPool; }
+ }
+
+ public void IAmAlive()
+ {
+ _lastAliveTime = DateTime.Now;
+ }
+ }
+
+ #endregion
+
+ #region Member Variables
///
- /// Contains the name of this instance of SmartThreadPool.
- /// Can be changed by the user.
+ /// Dictionary of all the threads in the thread pool.
///
- private string _name = "SmartThreadPool";
-
- ///
- /// Hashtable of all the threads in the thread pool.
- ///
- private Hashtable _workerThreads = Hashtable.Synchronized(new Hashtable());
+ private readonly SynchronizedDictionary _workerThreads = new SynchronizedDictionary();
///
/// Queue of work items.
///
- private WorkItemsQueue _workItemsQueue = new WorkItemsQueue();
+ private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue();
///
/// Count the work items handled.
/// Used by the performance counter.
///
- private long _workItemsProcessed = 0;
+ private long _workItemsProcessed;
///
/// Number of threads that currently work (not idle).
///
- private int _inUseWorkerThreads = 0;
+ private int _inUseWorkerThreads;
///
/// Start information to use.
@@ -170,43 +240,51 @@ namespace Amib.Threading
///
private STPStartInfo _stpStartInfo = new STPStartInfo();
+ ///
+ /// Stored the original reference to the provided _stpStartInfo.
+ /// It is used to change the MinThread and MaxThreads
+ ///
+ private STPStartInfo _stpStartInfoRW;
+
///
/// Total number of work items that are stored in the work items queue
/// plus the work items that the threads in the pool are working on.
///
- private int _currentWorkItemsCount = 0;
+ private int _currentWorkItemsCount;
///
/// Signaled when the thread pool is idle, i.e. no thread is busy
/// and the work items queue is empty
///
- private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
+ //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
+ private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
///
/// An event to signal all the threads to quit immediately.
///
- private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
+ //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
+ private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false);
+
+ ///
+ /// A flag to indicate if the Smart Thread Pool is now suspended.
+ ///
+ private bool _isSuspended;
///
/// A flag to indicate the threads to quit.
///
- private bool _shutdown = false;
+ private bool _shutdown;
///
/// Counts the threads created in the pool.
/// It is used to name the threads.
///
- private int _threadCounter = 0;
+ private int _threadCounter;
///
/// Indicate that the SmartThreadPool has been disposed
///
- private bool _isDisposed = false;
-
- ///
- /// Event to send that the thread pool is idle
- ///
- private event EventHandler _stpIdle;
+ private bool _isDisposed;
///
/// On idle event
@@ -218,34 +296,75 @@ namespace Amib.Threading
/// work item int the SmartThreadPool
/// This variable is used in case of Shutdown
///
- private Hashtable _workItemsGroups = Hashtable.Synchronized(new Hashtable());
+ private readonly SynchronizedDictionary _workItemsGroups = new SynchronizedDictionary();
- ///
- /// 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.
- ///
- [ThreadStatic]
- private static SmartThreadPool _smartThreadPool;
+ ///
+ /// A common object for all the work items int the STP
+ /// so we can mark them to cancel in O(1)
+ ///
+ private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup();
- ///
- /// A reference to the current work item a thread from the thread pool
- /// is executing.
- ///
- [ThreadStatic]
- private static WorkItem _currentWorkItem;
+ ///
+ /// STP performance counters
+ ///
+ private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance;
- ///
- /// STP performance counters
- ///
- private ISTPInstancePerformanceCounters _pcs = NullSTPInstancePerformanceCounters.Instance;
- #endregion
+#if (WindowsCE)
+ private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot();
+#else
+ [ThreadStatic]
+ private static ThreadEntry _threadEntry;
- #region Construction and Finalization
+#endif
- ///
+ ///
+ /// An event to call after a thread is created, but before
+ /// it's first use.
+ ///
+ private event ThreadInitializationHandler _onThreadInitialization;
+
+ ///
+ /// An event to call when a thread is about to exit, after
+ /// it is no longer belong to the pool.
+ ///
+ private event ThreadTerminationHandler _onThreadTermination;
+
+ #endregion
+
+ #region Per thread properties
+
+ ///
+ /// A reference to the current work item a thread from the thread pool
+ /// is executing.
+ ///
+ private static ThreadEntry CurrentThreadEntry
+ {
+#if (WindowsCE)
+ get
+ {
+ return Thread.GetData(_threadEntrySlot) as ThreadEntry;
+ }
+ set
+ {
+ Thread.SetData(_threadEntrySlot, value);
+ }
+#else
+ get
+ {
+ return _threadEntry;
+ }
+ set
+ {
+ _threadEntry = value;
+ }
+#endif
+ }
+ #endregion
+
+ #region Construction and Finalization
+
+ ///
/// Constructor
///
public SmartThreadPool()
@@ -294,9 +413,10 @@ namespace Amib.Threading
Initialize();
}
- ///
- /// Constructor
- ///
+ ///
+ /// Constructor
+ ///
+ /// A SmartThreadPool configuration that overrides the default behavior
public SmartThreadPool(STPStartInfo stpStartInfo)
{
_stpStartInfo = new STPStartInfo(stpStartInfo);
@@ -305,29 +425,54 @@ namespace Amib.Threading
private void Initialize()
{
+ Name = "SmartThreadPool";
ValidateSTPStartInfo();
+ // _stpStartInfoRW holds a read/write reference to the original _stpStartInfo.
+ _stpStartInfoRW = _stpStartInfo;
+
+ // From now on _stpStartInfo is readonly
+ _stpStartInfo = _stpStartInfo.AsReadOnly();
+
+ _isSuspended = _stpStartInfo.StartSuspended;
+
+#if (WindowsCE)
if (null != _stpStartInfo.PerformanceCounterInstanceName)
{
- try
- {
- _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
- }
- catch(Exception e)
- {
- Debug.WriteLine("Unable to create Performance Counters: " + e.ToString());
- _pcs = NullSTPInstancePerformanceCounters.Instance;
- }
- }
+ throw new NotSupportedException("Performance counters are not implemented for Compact Framework");
+ }
- StartOptimalNumberOfThreads();
+#else
+ if (null != _stpStartInfo.PerformanceCounterInstanceName)
+ {
+ try
+ {
+ _pcs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Unable to create Performance Counters: " + e);
+ _pcs = NullSTPInstancePerformanceCounters.Instance;
+ }
+ }
+#endif
+
+ // If the STP is not started suspended then start the threads.
+ if (!_isSuspended)
+ {
+ StartOptimalNumberOfThreads();
+ }
}
private void StartOptimalNumberOfThreads()
{
int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads);
threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads);
- StartThreads(threadsCount);
+ threadsCount -= _workerThreads.Count;
+ if (threadsCount > 0)
+ {
+ StartThreads(threadsCount);
+ }
}
private void ValidateSTPStartInfo()
@@ -352,7 +497,7 @@ namespace Amib.Threading
}
}
- private void ValidateCallback(Delegate callback)
+ private static void ValidateCallback(Delegate callback)
{
if(callback.GetInvocationList().Length > 1)
{
@@ -382,25 +527,14 @@ namespace Amib.Threading
/// Put a new work item in the queue
///
/// A work item to queue
- private void Enqueue(WorkItem workItem)
- {
- Enqueue(workItem, true);
- }
-
- ///
- /// Put a new work item in the queue
- ///
- /// A work item to queue
- internal void Enqueue(WorkItem workItem, bool incrementWorkItems)
+ internal override void Enqueue(WorkItem workItem)
{
// Make sure the workItem is not null
Debug.Assert(null != workItem);
- if (incrementWorkItems)
- {
- IncrementWorkItemsCount();
- }
+ IncrementWorkItemsCount();
+ workItem.CanceledSmartThreadPool = _canceledSmartThreadPool;
_workItemsQueue.EnqueueWorkItem(workItem);
workItem.WorkItemIsQueued();
@@ -419,8 +553,8 @@ namespace Amib.Threading
//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
if (count == 1)
{
- //Trace.WriteLine("STP is NOT idle");
- _isIdleWaitHandle.Reset();
+ IsIdle = false;
+ _isIdleWaitHandle.Reset();
}
}
@@ -428,16 +562,19 @@ namespace Amib.Threading
{
++_workItemsProcessed;
- // The counter counts even if the work item was cancelled
- _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
+ if (!_shutdown)
+ {
+ // The counter counts even if the work item was cancelled
+ _pcs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed);
+ }
int count = Interlocked.Decrement(ref _currentWorkItemsCount);
//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
if (count == 0)
{
- //Trace.WriteLine("STP is idle");
- _isIdleWaitHandle.Set();
- }
+ IsIdle = true;
+ _isIdleWaitHandle.Set();
+ }
}
internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup)
@@ -461,7 +598,7 @@ namespace Amib.Threading
{
// There is no need to lock the two methods together
// since only the current thread removes itself
- // and the _workerThreads is a synchronized hashtable
+ // and the _workerThreads is a synchronized dictionary
if (_workerThreads.Contains(Thread.CurrentThread))
{
_workerThreads.Remove(Thread.CurrentThread);
@@ -475,7 +612,7 @@ namespace Amib.Threading
/// The number of threads to start
private void StartThreads(int threadsCount)
{
- if (_stpStartInfo.StartSuspended)
+ if (_isSuspended)
{
return;
}
@@ -497,7 +634,7 @@ namespace Amib.Threading
}
// Create a new thread
- Thread workerThread = new Thread(new ThreadStart(ProcessQueuedItems));
+ Thread workerThread = new Thread(ProcessQueuedItems);
// Configure the new thread and start it
workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
@@ -506,9 +643,9 @@ namespace Amib.Threading
workerThread.Start();
++_threadCounter;
- // Add the new thread to the hashtable and update its creation
- // time.
- _workerThreads[workerThread] = DateTime.Now;
+ // Add it to the dictionary and update its creation time.
+ _workerThreads[workerThread] = new ThreadEntry(this);
+
_pcs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads);
}
}
@@ -519,8 +656,11 @@ namespace Amib.Threading
///
private void ProcessQueuedItems()
{
- // Initialize the _smartThreadPool variable
- _smartThreadPool = this;
+ // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks
+ // of the dictionary.
+ CurrentThreadEntry = _workerThreads[Thread.CurrentThread];
+
+ FireOnThreadInitialization();
try
{
@@ -531,14 +671,33 @@ namespace Amib.Threading
{
// Update the last time this thread was seen alive.
// It's good for debugging.
- _workerThreads[Thread.CurrentThread] = DateTime.Now;
+ CurrentThreadEntry.IAmAlive();
+
+ // The following block handles the when the MaxWorkerThreads has been
+ // incremented by the user at run-time.
+ // Double lock for quit.
+ if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads)
+ {
+ lock (_workerThreads.SyncRoot)
+ {
+ if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads)
+ {
+ // Inform that the thread is quiting and then quit.
+ // This method must be called within this lock or else
+ // more threads will quit and the thread pool will go
+ // below the lower limit.
+ InformCompleted();
+ break;
+ }
+ }
+ }
// Wait for a work item, shutdown, or timeout
WorkItem workItem = Dequeue();
// Update the last time this thread was seen alive.
// It's good for debugging.
- _workerThreads[Thread.CurrentThread] = DateTime.Now;
+ CurrentThreadEntry.IAmAlive();
// On timeout or shut down.
if (null == workItem)
@@ -572,6 +731,16 @@ namespace Amib.Threading
// Initialize the value to false
bInUseWorkerThreadsWasIncremented = false;
+ // Set the Current Work Item of the thread.
+ // Store the Current Work Item before the workItem.StartingWorkItem() is called,
+ // so WorkItem.Cancel can work when the work item is between InQueue and InProgress
+ // states.
+ // If the work item has been cancelled BEFORE the workItem.StartingWorkItem()
+ // (work item is in InQueue state) then workItem.StartingWorkItem() will return false.
+ // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then
+ // (work item is in InProgress state) then the thread will be aborted
+ CurrentThreadEntry.CurrentWorkItem = workItem;
+
// Change the state of the work item to 'in progress' if possible.
// We do it here so if the work item has been canceled we won't
// increment the _inUseWorkerThreads.
@@ -595,8 +764,7 @@ namespace Amib.Threading
// statement we will decrement it correctly.
bInUseWorkerThreadsWasIncremented = true;
- // Set the _currentWorkItem to the current work item
- _currentWorkItem = workItem;
+ workItem.FireWorkItemStarted();
ExecuteWorkItem(workItem);
}
@@ -607,14 +775,11 @@ namespace Amib.Threading
}
finally
{
- if (null != workItem)
- {
- workItem.DisposeOfState();
- }
+ workItem.DisposeOfState();
- // Set the _currentWorkItem to null, since we
+ // Set the CurrentWorkItem to null, since we
// no longer run user's code.
- _currentWorkItem = null;
+ CurrentThreadEntry.CurrentWorkItem = null;
// Decrement the _inUseWorkerThreads only if we had
// incremented it. Note the cancelled work items don't
@@ -639,7 +804,9 @@ namespace Amib.Threading
{
tae.GetHashCode();
// Handle the abort exception gracfully.
+#if !(WindowsCE)
Thread.ResetAbort();
+#endif
}
catch(Exception e)
{
@@ -648,6 +815,7 @@ namespace Amib.Threading
finally
{
InformCompleted();
+ FireOnThreadTermination();
}
}
@@ -658,10 +826,6 @@ namespace Amib.Threading
{
workItem.Execute();
}
- catch
- {
- throw;
- }
finally
{
_pcs.SampleWorkItemsProcessTime(workItem.ProcessTime);
@@ -673,261 +837,44 @@ namespace Amib.Threading
#region Public Methods
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- /// The priority of the work item
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, workItemPriority);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// Work item info
- /// A callback to execute
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- /// The work item priority
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, workItemPriority);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// Work item information
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, workItemInfo, callback, state);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(
- WorkItemCallback callback,
- object state,
- PostExecuteWorkItemCallback postExecuteWorkItemCallback)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// The work item priority
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(
- WorkItemCallback callback,
- object state,
- PostExecuteWorkItemCallback postExecuteWorkItemCallback,
- WorkItemPriority workItemPriority)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// Indicates on which cases to call to the post execute callback
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(
- WorkItemCallback callback,
- object state,
- PostExecuteWorkItemCallback postExecuteWorkItemCallback,
- CallToPostExecute callToPostExecute)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// Indicates on which cases to call to the post execute callback
- /// The work item priority
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(
- WorkItemCallback callback,
- object state,
- PostExecuteWorkItemCallback postExecuteWorkItemCallback,
- CallToPostExecute callToPostExecute,
- WorkItemPriority workItemPriority)
- {
- ValidateNotDisposed();
- ValidateCallback(callback);
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _stpStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
- Enqueue(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Wait for the thread pool to be idle
- ///
- public void WaitForIdle()
- {
- WaitForIdle(Timeout.Infinite);
- }
-
- ///
- /// Wait for the thread pool to be idle
- ///
- public bool WaitForIdle(TimeSpan timeout)
- {
- return WaitForIdle((int)timeout.TotalMilliseconds);
- }
-
- ///
- /// Wait for the thread pool to be idle
- ///
- public bool WaitForIdle(int millisecondsTimeout)
- {
- ValidateWaitForIdle();
- return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
- }
private void ValidateWaitForIdle()
{
- if(_smartThreadPool == this)
+ if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this)
{
throw new NotSupportedException(
- "WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
+ "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
}
}
- internal void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup)
+ internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup)
{
- ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, SmartThreadPool._currentWorkItem);
- if ((null != workItemsGroup) &&
- (null != SmartThreadPool._currentWorkItem) &&
- SmartThreadPool._currentWorkItem.WasQueuedBy(workItemsGroup))
+ if (null == CurrentThreadEntry)
+ {
+ return;
+ }
+
+ WorkItem workItem = CurrentThreadEntry.CurrentWorkItem;
+ ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem);
+ if ((null != workItemsGroup) &&
+ (null != workItem) &&
+ CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup))
{
- throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
+ throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
- private void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem)
+ private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem)
{
if ((null != workItemsGroup) &&
(null != workItem) &&
workItem.WasQueuedBy(workItemsGroup))
{
- throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it will cause may cause a deadlock");
+ throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
}
}
-
-
///
/// Force the SmartThreadPool to shutdown
///
@@ -936,7 +883,10 @@ namespace Amib.Threading
Shutdown(true, 0);
}
- public void Shutdown(bool forceAbort, TimeSpan timeout)
+ ///
+ /// Force the SmartThreadPool to shutdown with timeout
+ ///
+ public void Shutdown(bool forceAbort, TimeSpan timeout)
{
Shutdown(forceAbort, (int)timeout.TotalMilliseconds);
}
@@ -952,13 +902,14 @@ namespace Amib.Threading
if (NullSTPInstancePerformanceCounters.Instance != _pcs)
{
- _pcs.Dispose();
// Set the _pcs to "null" to stop updating the performance
// counters
_pcs = NullSTPInstancePerformanceCounters.Instance;
+
+ pcs.Dispose();
}
- Thread [] threads = null;
+ Thread [] threads;
lock(_workerThreads.SyncRoot)
{
// Shutdown the work items queue
@@ -1009,7 +960,12 @@ namespace Amib.Threading
// Abort the threads in the pool
foreach(Thread thread in threads)
{
- if ((thread != null) && thread.IsAlive)
+
+ if ((thread != null)
+#if !(WindowsCE)
+ && thread.IsAlive
+#endif
+ )
{
try
{
@@ -1028,28 +984,25 @@ namespace Amib.Threading
}
}
}
-
- // Dispose of the performance counters
- pcs.Dispose();
}
///
/// Wait for all work items to complete
///
- /// Array of work item result objects
+ /// Array of work item result objects
///
/// true when every work item in workItemResults has completed; otherwise false.
///
public static bool WaitAll(
- IWorkItemResult [] workItemResults)
+ IWaitableResult [] waitableResults)
{
- return WaitAll(workItemResults, Timeout.Infinite, true);
+ return WaitAll(waitableResults, Timeout.Infinite, true);
}
///
/// Wait for all work items to complete
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1058,17 +1011,17 @@ namespace Amib.Threading
/// true when every work item in workItemResults has completed; otherwise false.
///
public static bool WaitAll(
- IWorkItemResult [] workItemResults,
+ IWaitableResult [] waitableResults,
TimeSpan timeout,
bool exitContext)
{
- return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
+ return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext);
}
///
/// Wait for all work items to complete
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1078,18 +1031,18 @@ namespace Amib.Threading
/// true when every work item in workItemResults has completed; otherwise false.
///
public static bool WaitAll(
- IWorkItemResult [] workItemResults,
+ IWaitableResult[] waitableResults,
TimeSpan timeout,
bool exitContext,
WaitHandle cancelWaitHandle)
{
- return WaitAll(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
+ return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
}
///
/// Wait for all work items to complete
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1098,17 +1051,17 @@ namespace Amib.Threading
/// true when every work item in workItemResults has completed; otherwise false.
///
public static bool WaitAll(
- IWorkItemResult [] workItemResults,
+ IWaitableResult [] waitableResults,
int millisecondsTimeout,
bool exitContext)
{
- return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, null);
+ return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null);
}
///
/// Wait for all work items to complete
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1118,32 +1071,32 @@ namespace Amib.Threading
/// true when every work item in workItemResults has completed; otherwise false.
///
public static bool WaitAll(
- IWorkItemResult [] workItemResults,
+ IWaitableResult[] waitableResults,
int millisecondsTimeout,
bool exitContext,
WaitHandle cancelWaitHandle)
{
- return WorkItem.WaitAll(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
+ return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
}
///
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
///
- /// Array of work item result objects
+ /// Array of work item result objects
///
/// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
///
public static int WaitAny(
- IWorkItemResult [] workItemResults)
+ IWaitableResult [] waitableResults)
{
- return WaitAny(workItemResults, Timeout.Infinite, true);
+ return WaitAny(waitableResults, Timeout.Infinite, true);
}
///
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1152,17 +1105,17 @@ namespace Amib.Threading
/// 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.
///
public static int WaitAny(
- IWorkItemResult [] workItemResults,
+ IWaitableResult[] waitableResults,
TimeSpan timeout,
bool exitContext)
{
- return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext);
+ return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext);
}
///
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1172,18 +1125,18 @@ namespace Amib.Threading
/// 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.
///
public static int WaitAny(
- IWorkItemResult [] workItemResults,
+ IWaitableResult [] waitableResults,
TimeSpan timeout,
bool exitContext,
WaitHandle cancelWaitHandle)
{
- return WaitAny(workItemResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
+ return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
}
///
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1192,17 +1145,17 @@ namespace Amib.Threading
/// 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.
///
public static int WaitAny(
- IWorkItemResult [] workItemResults,
+ IWaitableResult [] waitableResults,
int millisecondsTimeout,
bool exitContext)
{
- return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, null);
+ return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null);
}
///
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -1212,111 +1165,167 @@ namespace Amib.Threading
/// 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.
///
public static int WaitAny(
- IWorkItemResult [] workItemResults,
+ IWaitableResult [] waitableResults,
int millisecondsTimeout,
bool exitContext,
WaitHandle cancelWaitHandle)
{
- return WorkItem.WaitAny(workItemResults, millisecondsTimeout, exitContext, cancelWaitHandle);
+ return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
}
+ ///
+ /// Creates a new WorkItemsGroup.
+ ///
+ /// The number of work items that can be run concurrently
+ /// A reference to the WorkItemsGroup
public IWorkItemsGroup CreateWorkItemsGroup(int concurrency)
{
IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo);
return workItemsGroup;
}
+ ///
+ /// Creates a new WorkItemsGroup.
+ ///
+ /// The number of work items that can be run concurrently
+ /// A WorkItemsGroup configuration that overrides the default behavior
+ /// A reference to the WorkItemsGroup
public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo)
{
IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo);
return workItemsGroup;
}
- public event WorkItemsGroupIdleHandler OnIdle
- {
- add
- {
- throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
- //_onIdle += value;
- }
- remove
- {
- throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
- //_onIdle -= value;
- }
- }
+ #region Fire Thread's Events
- public void Cancel()
- {
- ICollection workItemsGroups = _workItemsGroups.Values;
- foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
- {
- workItemsGroup.Cancel();
- }
- }
+ private void FireOnThreadInitialization()
+ {
+ if (null != _onThreadInitialization)
+ {
+ foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList())
+ {
+ try
+ {
+ tih();
+ }
+ catch (Exception e)
+ {
+ e.GetHashCode();
+ Debug.Assert(false);
+ throw;
+ }
+ }
+ }
+ }
- public void Start()
- {
- lock (this)
- {
- if (!this._stpStartInfo.StartSuspended)
- {
- return;
- }
- _stpStartInfo.StartSuspended = false;
- }
-
- ICollection workItemsGroups = _workItemsGroups.Values;
- foreach(WorkItemsGroup workItemsGroup in workItemsGroups)
- {
- workItemsGroup.OnSTPIsStarting();
- }
+ private void FireOnThreadTermination()
+ {
+ if (null != _onThreadTermination)
+ {
+ foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList())
+ {
+ try
+ {
+ tth();
+ }
+ catch (Exception e)
+ {
+ e.GetHashCode();
+ Debug.Assert(false);
+ throw;
+ }
+ }
+ }
+ }
- StartOptimalNumberOfThreads();
- }
+ #endregion
+
+ ///
+ /// This event is fired when a thread is created.
+ /// Use it to initialize a thread before the work items use it.
+ ///
+ public event ThreadInitializationHandler OnThreadInitialization
+ {
+ add { _onThreadInitialization += value; }
+ remove { _onThreadInitialization -= value; }
+ }
+
+ ///
+ /// This event is fired when a thread is terminating.
+ /// Use it for cleanup.
+ ///
+ public event ThreadTerminationHandler OnThreadTermination
+ {
+ add { _onThreadTermination += value; }
+ remove { _onThreadTermination -= value; }
+ }
+
+
+ internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig)
+ {
+ foreach (ThreadEntry threadEntry in _workerThreads.Values)
+ {
+ WorkItem workItem = threadEntry.CurrentWorkItem;
+ if (null != workItem &&
+ workItem.WasQueuedBy(wig) &&
+ !workItem.IsCanceled)
+ {
+ threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true);
+ }
+ }
+ }
+
+
#endregion
#region Properties
///
- /// Get/Set the name of the SmartThreadPool instance
- ///
- public string Name
- {
- get
- {
- return _name;
- }
-
- set
- {
- _name = value;
- }
- }
-
- ///
- /// Get the lower limit of threads in the pool.
+ /// Get/Set the lower limit of threads in the pool.
///
public int MinThreads
{
get
{
ValidateNotDisposed();
- return _stpStartInfo.MinWorkerThreads;
+ return _stpStartInfo.MinWorkerThreads;
}
+ set
+ {
+ Debug.Assert(value >= 0);
+ Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads);
+ if (_stpStartInfo.MaxWorkerThreads < value)
+ {
+ _stpStartInfoRW.MaxWorkerThreads = value;
+ }
+ _stpStartInfoRW.MinWorkerThreads = value;
+ StartOptimalNumberOfThreads();
+ }
}
- ///
- /// Get the upper limit of threads in the pool.
+ ///
+ /// Get/Set the upper limit of threads in the pool.
///
public int MaxThreads
{
get
{
ValidateNotDisposed();
- return _stpStartInfo.MaxWorkerThreads;
+ return _stpStartInfo.MaxWorkerThreads;
}
+
+ set
+ {
+ Debug.Assert(value > 0);
+ Debug.Assert(value >= _stpStartInfo.MinWorkerThreads);
+ if (_stpStartInfo.MinWorkerThreads > value)
+ {
+ _stpStartInfoRW.MinWorkerThreads = value;
+ }
+ _stpStartInfoRW.MaxWorkerThreads = value;
+ StartOptimalNumberOfThreads();
+ }
}
///
/// Get the number of threads in the thread pool.
@@ -1343,41 +1352,32 @@ namespace Amib.Threading
}
}
- ///
- /// Get the number of work items in the queue.
- ///
- public int WaitingCallbacks
- {
- get
- {
- ValidateNotDisposed();
- return _workItemsQueue.Count;
- }
- }
+ ///
+ /// Returns true if the current running work item has been cancelled.
+ /// Must be used within the work item's callback method.
+ /// The work item should sample this value in order to know if it
+ /// needs to quit before its completion.
+ ///
+ public static bool IsWorkItemCanceled
+ {
+ get
+ {
+ return CurrentThreadEntry.CurrentWorkItem.IsCanceled;
+ }
+ }
-
- public event EventHandler Idle
- {
- add
- {
- _stpIdle += value;
- }
-
- remove
- {
- _stpIdle -= value;
- }
- }
+ ///
+ /// Thread Pool start information (readonly)
+ ///
+ public STPStartInfo STPStartInfo
+ {
+ get { return _stpStartInfo; }
+ }
#endregion
#region IDisposable Members
- ~SmartThreadPool()
- {
- Dispose();
- }
-
public void Dispose()
{
if (!_isDisposed)
@@ -1393,8 +1393,14 @@ namespace Amib.Threading
_shuttingDownEvent = null;
}
_workerThreads.Clear();
+
+ if (null != _isIdleWaitHandle)
+ {
+ _isIdleWaitHandle.Close();
+ _isIdleWaitHandle = null;
+ }
+
_isDisposed = true;
- GC.SuppressFinalize(this);
}
}
@@ -1406,6 +1412,134 @@ namespace Amib.Threading
}
}
#endregion
+
+ #region WorkItemsGroupBase Overrides
+
+ ///
+ /// Get/Set the maximum number of work items that execute cocurrency on the thread pool
+ ///
+ public override int Concurrency
+ {
+ get { return MaxThreads; }
+ set { MaxThreads = value; }
+ }
+
+ ///
+ /// Get the number of work items in the queue.
+ ///
+ public override int WaitingCallbacks
+ {
+ get
+ {
+ ValidateNotDisposed();
+ return _workItemsQueue.Count;
+ }
+ }
+
+ ///
+ /// Get an array with all the state objects of the currently running items.
+ /// The array represents a snap shot and impact performance.
+ ///
+ public override object[] GetStates()
+ {
+ object[] states = _workItemsQueue.GetStates();
+ return states;
+ }
+
+ ///
+ /// WorkItemsGroup start information (readonly)
+ ///
+ public override WIGStartInfo WIGStartInfo
+ {
+ get { return _stpStartInfo; }
+ }
+
+ ///
+ /// Start the thread pool if it was started suspended.
+ /// If it is already running, this method is ignored.
+ ///
+ public override void Start()
+ {
+ if (!_isSuspended)
+ {
+ return;
+ }
+ _isSuspended = false;
+
+ ICollection workItemsGroups = _workItemsGroups.Values;
+ foreach (WorkItemsGroup workItemsGroup in workItemsGroups)
+ {
+ workItemsGroup.OnSTPIsStarting();
+ }
+
+ StartOptimalNumberOfThreads();
+ }
+
+ ///
+ /// Cancel all work items using thread abortion
+ ///
+ /// True to stop work items by raising ThreadAbortException
+ public override void Cancel(bool abortExecution)
+ {
+ _canceledSmartThreadPool.IsCanceled = true;
+ _canceledSmartThreadPool = new CanceledWorkItemsGroup();
+
+ ICollection workItemsGroups = _workItemsGroups.Values;
+ foreach (WorkItemsGroup workItemsGroup in workItemsGroups)
+ {
+ workItemsGroup.Cancel(abortExecution);
+ }
+
+ if (abortExecution)
+ {
+ foreach (ThreadEntry threadEntry in _workerThreads.Values)
+ {
+ WorkItem workItem = threadEntry.CurrentWorkItem;
+ if (null != workItem &&
+ threadEntry.AssociatedSmartThreadPool == this &&
+ !workItem.IsCanceled)
+ {
+ threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Wait for the thread pool to be idle
+ ///
+ public override bool WaitForIdle(int millisecondsTimeout)
+ {
+ ValidateWaitForIdle();
+ return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
+ }
+
+ ///
+ /// 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.
+ ///
+ public override event WorkItemsGroupIdleHandler OnIdle
+ {
+ add
+ {
+ throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
+ //_onIdle += value;
+ }
+ remove
+ {
+ throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature.");
+ //_onIdle -= value;
+ }
+ }
+
+ internal override void PreQueueWorkItem()
+ {
+ ValidateNotDisposed();
+ }
+
+ #endregion
}
#endregion
}
diff --git a/SmartThreadPool/SmartThreadPool.csproj b/SmartThreadPool/SmartThreadPool.csproj
index 1f25584..f63e13d 100644
--- a/SmartThreadPool/SmartThreadPool.csproj
+++ b/SmartThreadPool/SmartThreadPool.csproj
@@ -1,141 +1,70 @@

- Local
- 8.0.50727
- 2.0
- {8684FC56-A679-4E2E-8F96-E172FB062EB6}
Debug
AnyCPU
-
-
-
-
- SmartThreadPool
-
-
- JScript
- Grid
- IE50
- false
+ {74D4C33F-7CC8-4B2A-A7DF-D8B6E63B6EBD}
Library
- SmartThreadPool
- OnBuildSuccess
-
-
-
-
-
-
+ false
+ SmartThreadPool
+ Amib.Threading
- bin\Debug\
- false
- 285212672
- false
-
-
- DEBUG;TRACE
+ true
+ full
+ false
+ .\bin\Debug\
+ TRACE;DEBUG
- true
- 4096
- false
-
-
- false
- false
- false
- false
- 4
- full
- prompt
- bin\Release\
- false
- 285212672
- false
-
-
- TRACE
-
-
- false
- 4096
- false
-
-
+ pdbonly
true
- false
- false
- false
- 4
- none
- prompt
+ .\bin\Release\
+ TRACE
+
+
+ true
+ bin\DebugCE\
+ TRACE;DEBUG
+ full
+ AnyCPU
+
+
+ bin\ReleaseCE\
+ TRACE
+ true
+ pdbonly
+ AnyCPU
-
- System
-
-
- System.Data
-
-
- System.Web
-
-
- System.XML
-
+
+
+
+
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
-
- Code
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/SmartThreadPool/SmartThreadPoolCE.csproj b/SmartThreadPool/SmartThreadPoolCE.csproj
new file mode 100644
index 0000000..472b5cc
--- /dev/null
+++ b/SmartThreadPool/SmartThreadPoolCE.csproj
@@ -0,0 +1,104 @@
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {D81DD596-C71F-4AC2-816C-63C19589E7E0}
+ Library
+ Properties
+ Amib.Threading
+ SmartThreadPoolCE
+ {4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ WindowsCE
+ E2BECB1F-8C8C-41ba-B736-9BE7D946A398
+ 5.0
+ SmartThreadPoolCE
+ v2.0
+
+
+ false
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE;$(PlatformFamilyName)
+ true
+ true
+ prompt
+ 512
+ 4
+ Off
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;$(PlatformFamilyName)
+ true
+ true
+ prompt
+ 512
+ 4
+ Off
+
+
+ true
+ true
+ Off
+ bin\ReleaseCE\
+ TRACE;WindowsCE
+ true
+ 512
+ pdbonly
+ AnyCPU
+ prompt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SmartThreadPool/SynchronizedDictionary.cs b/SmartThreadPool/SynchronizedDictionary.cs
new file mode 100644
index 0000000..0cce19f
--- /dev/null
+++ b/SmartThreadPool/SynchronizedDictionary.cs
@@ -0,0 +1,89 @@
+using System.Collections.Generic;
+
+namespace Amib.Threading.Internal
+{
+ internal class SynchronizedDictionary
+ {
+ private readonly Dictionary _dictionary;
+ private readonly object _lock;
+
+ public SynchronizedDictionary()
+ {
+ _lock = new object();
+ _dictionary = new Dictionary();
+ }
+
+ 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.KeyCollection Keys
+ {
+ get
+ {
+ lock (_lock)
+ {
+ return _dictionary.Keys;
+ }
+ }
+ }
+
+ public Dictionary.ValueCollection Values
+ {
+ get
+ {
+ lock (_lock)
+ {
+ return _dictionary.Values;
+ }
+ }
+ }
+ public void Clear()
+ {
+ lock (_lock)
+ {
+ _dictionary.Clear();
+ }
+ }
+ }
+}
diff --git a/SmartThreadPool/WIGStartInfo.cs b/SmartThreadPool/WIGStartInfo.cs
index 5f73a9a..56205ac 100644
--- a/SmartThreadPool/WIGStartInfo.cs
+++ b/SmartThreadPool/WIGStartInfo.cs
@@ -1,5 +1,4 @@
-// Ami Bar
-// amibar@gmail.com
+using System;
namespace Amib.Threading
{
@@ -8,92 +7,204 @@ namespace Amib.Threading
///
public class WIGStartInfo
{
- ///
- /// Use the caller's security context
- ///
- private bool _useCallerCallContext;
+ private bool _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
+ private bool _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
+ private bool _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
+ private CallToPostExecute _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
+ private PostExecuteWorkItemCallback _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
+ private WorkItemPriority _workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
+ private bool _startSuspended = SmartThreadPool.DefaultStartSuspended;
+ private bool _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
- ///
- /// Use the caller's HTTP context
- ///
- private bool _useCallerHttpContext;
+ public WIGStartInfo()
+ {
+ }
- ///
- /// Dispose of the state object of a work item
- ///
- private bool _disposeOfStateObjects;
+ public WIGStartInfo(WIGStartInfo wigStartInfo)
+ {
+ _useCallerCallContext = wigStartInfo._useCallerCallContext;
+ _useCallerHttpContext = wigStartInfo._useCallerHttpContext;
+ _disposeOfStateObjects = wigStartInfo._disposeOfStateObjects;
+ _callToPostExecute = wigStartInfo._callToPostExecute;
+ _postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback;
+ _workItemPriority = wigStartInfo._workItemPriority;
+ _startSuspended = wigStartInfo._startSuspended;
+ _fillStateWithArgs = wigStartInfo._fillStateWithArgs;
+ }
- ///
- /// The option to run the post execute
- ///
- private CallToPostExecute _callToPostExecute;
-
- ///
- /// A post execute callback to call when none is provided in
- /// the QueueWorkItem method.
- ///
- private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
-
- ///
- /// Indicate the WorkItemsGroup to suspend the handling of the work items
- /// until the Start() method is called.
- ///
- private bool _startSuspended;
-
- public WIGStartInfo()
- {
- _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
- _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
- _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
- _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
- _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
- _startSuspended = SmartThreadPool.DefaultStartSuspended;
- }
-
- public WIGStartInfo(WIGStartInfo wigStartInfo)
- {
- _useCallerCallContext = wigStartInfo._useCallerCallContext;
- _useCallerHttpContext = wigStartInfo._useCallerHttpContext;
- _disposeOfStateObjects = wigStartInfo._disposeOfStateObjects;
- _callToPostExecute = wigStartInfo._callToPostExecute;
- _postExecuteWorkItemCallback = wigStartInfo._postExecuteWorkItemCallback;
- _startSuspended = wigStartInfo._startSuspended;
- }
-
- public bool UseCallerCallContext
+ ///
+ /// Get/Set if to use the caller's security context
+ ///
+ public virtual bool UseCallerCallContext
{
get { return _useCallerCallContext; }
set { _useCallerCallContext = value; }
}
- public bool UseCallerHttpContext
+ ///
+ /// Get/Set if to use the caller's HTTP context
+ ///
+ public virtual bool UseCallerHttpContext
{
get { return _useCallerHttpContext; }
set { _useCallerHttpContext = value; }
}
- public bool DisposeOfStateObjects
+ ///
+ /// Get/Set if to dispose of the state object of a work item
+ ///
+ public virtual bool DisposeOfStateObjects
{
get { return _disposeOfStateObjects; }
set { _disposeOfStateObjects = value; }
}
- public CallToPostExecute CallToPostExecute
+ ///
+ /// Get/Set the run the post execute options
+ ///
+ public virtual CallToPostExecute CallToPostExecute
{
get { return _callToPostExecute; }
set { _callToPostExecute = value; }
}
- public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
+ ///
+ /// Get/Set the default post execute callback
+ ///
+ public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback
{
get { return _postExecuteWorkItemCallback; }
set { _postExecuteWorkItemCallback = value; }
}
- public bool StartSuspended
+ ///
+ /// Get/Set if the work items execution should be suspended until the Start()
+ /// method is called.
+ ///
+ public virtual bool StartSuspended
{
get { return _startSuspended; }
set { _startSuspended = value; }
}
- }
+
+ ///
+ /// Get/Set the default priority that a work item gets when it is enqueued
+ ///
+ public virtual WorkItemPriority WorkItemPriority
+ {
+ get { return _workItemPriority; }
+ set { _workItemPriority = value; }
+ }
+
+ ///
+ /// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the
+ /// arguments as an object array into the state of the work item.
+ /// The arguments can be access later by IWorkItemResult.State.
+ ///
+ public virtual bool FillStateWithArgs
+ {
+ get { return _fillStateWithArgs; }
+ set { _fillStateWithArgs = value; }
+ }
+
+ ///
+ /// Get a readonly version of this WIGStartInfo
+ ///
+ /// Returns a readonly reference to this WIGStartInfoRO
+ public WIGStartInfo AsReadOnly()
+ {
+ return new WIGStartInfoRO(this);
+ }
+
+ #region WIGStartInfoRO class
+
+ ///
+ /// A readonly version of WIGStartInfo
+ ///
+ private class WIGStartInfoRO : WIGStartInfo
+ {
+ private readonly WIGStartInfo _wigStartInfoRO;
+
+ public WIGStartInfoRO(WIGStartInfo wigStartInfoRO)
+ {
+ _wigStartInfoRO = wigStartInfoRO;
+ }
+
+ ///
+ /// Get if to use the caller's security context
+ ///
+ public override bool UseCallerCallContext
+ {
+ get { return _wigStartInfoRO.UseCallerCallContext; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get if to use the caller's HTTP context
+ ///
+ public override bool UseCallerHttpContext
+ {
+ get { return _wigStartInfoRO.UseCallerHttpContext; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get if to dispose of the state object of a work item
+ ///
+ public override bool DisposeOfStateObjects
+ {
+ get { return _wigStartInfoRO.DisposeOfStateObjects; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the run the post execute options
+ ///
+ public override CallToPostExecute CallToPostExecute
+ {
+ get { return _wigStartInfoRO.CallToPostExecute; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the default post execute callback
+ ///
+ public override PostExecuteWorkItemCallback PostExecuteWorkItemCallback
+ {
+ get { return _wigStartInfoRO.PostExecuteWorkItemCallback; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get if the work items execution should be suspended until the Start()
+ /// method is called.
+ ///
+ public override bool StartSuspended
+ {
+ get { return _wigStartInfoRO.StartSuspended; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Get the default priority that a work item gets when it is enqueued
+ ///
+ public override WorkItemPriority WorkItemPriority
+ {
+ get { return _wigStartInfoRO.WorkItemPriority; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+
+ ///
+ /// Indicate if QueueWorkItem of Action<...>/Func<...> fill the
+ /// arguments as an object array into the state of the work item.
+ /// The arguments can be access later by IWorkItemResult.State.
+ ///
+ public override bool FillStateWithArgs
+ {
+ get { return _wigStartInfoRO.FillStateWithArgs; }
+ set { throw new NotSupportedException("This is a readonly instance and set is not supported"); }
+ }
+ }
+ #endregion
+ }
}
diff --git a/SmartThreadPool/WorkItem.cs b/SmartThreadPool/WorkItem.cs
index eb06ca8..7f4b524 100644
--- a/SmartThreadPool/WorkItem.cs
+++ b/SmartThreadPool/WorkItem.cs
@@ -1,6 +1,3 @@
-// Ami Bar
-// amibar@gmail.com
-
using System;
using System.Threading;
using System.Diagnostics;
@@ -16,9 +13,9 @@ namespace Amib.Threading.Internal
#endregion
- #region IInternalWorkItemResult interface
+ #region CanceledWorkItemsGroup class
- public class CanceledWorkItemsGroup
+ public class CanceledWorkItemsGroup
{
public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup();
@@ -28,9 +25,13 @@ namespace Amib.Threading.Internal
get { return _isCanceled; }
set { _isCanceled = value; }
}
- }
+ }
- internal interface IInternalWorkItemResult
+ #endregion
+
+ #region IInternalWorkItemResult interface
+
+ internal interface IInternalWorkItemResult
{
event WorkItemStateCallback OnWorkItemStarted;
event WorkItemStateCallback OnWorkItemCompleted;
@@ -38,43 +39,80 @@ namespace Amib.Threading.Internal
#endregion
- #region IWorkItem interface
+ #region IInternalWaitableResult interface
- public interface IWorkItem
- {
+ internal interface IInternalWaitableResult
+ {
+ ///
+ /// This method is intent for internal use.
+ ///
+ IWorkItemResult GetWorkItemResult();
+ }
- }
+ #endregion
- #endregion
+ #region IHasWorkItemPriority interface
- #region WorkItem class
+ public interface IHasWorkItemPriority
+ {
+ WorkItemPriority WorkItemPriority { get; }
+ }
- ///
+ #endregion
+
+ #region WorkItem class
+
+ ///
/// Holds a callback delegate and the state for that delegate.
///
- public class WorkItem : IHasWorkItemPriority, IWorkItem
+ public class WorkItem : IHasWorkItemPriority
{
#region WorkItemState enum
///
/// Indicates the state of the work item in the thread pool
///
- private enum WorkItemState
+ 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
+ #region Fields
///
/// Callback delegate for the callback.
///
- private WorkItemCallback _callback;
+ private readonly WorkItemCallback _callback;
///
/// State with which to call the callback delegate.
@@ -84,8 +122,9 @@ namespace Amib.Threading.Internal
///
/// Stores the caller's context
///
- private CallerThreadContext _callerContext;
-
+#if !(WindowsCE)
+ private readonly CallerThreadContext _callerContext;
+#endif
///
/// Holds the result of the mehtod
///
@@ -104,7 +143,7 @@ namespace Amib.Threading.Internal
///
/// A ManualResetEvent to indicate that the result is ready
///
- private ManualResetEvent _workItemCompleted;
+ private ManualResetEvent _workItemCompleted;
///
/// A reference count to the _workItemCompleted.
@@ -115,12 +154,12 @@ namespace Amib.Threading.Internal
///
/// Represents the result state of the work item
///
- private WorkItemResult _workItemResult;
+ private readonly WorkItemResult _workItemResult;
///
/// Work item info
///
- private WorkItemInfo _workItemInfo;
+ private readonly WorkItemInfo _workItemInfo;
///
/// Called when the WorkItem starts
@@ -138,11 +177,22 @@ namespace Amib.Threading.Internal
///
private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
+ ///
+ /// A reference to an object that indicates whatever the
+ /// SmartThreadPool has been canceled
+ ///
+ private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
+
///
/// The work item group this work item belong to.
- ///
///
- private IWorkItemsGroup _workItemsGroup;
+ private readonly IWorkItemsGroup _workItemsGroup;
+
+ ///
+ /// 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.
+ ///
+ private Thread _executingThread;
#region Performance Counter fields
@@ -193,6 +243,8 @@ namespace Amib.Threading.Internal
///
/// Initialize the callback holding object.
///
+ /// The workItemGroup of the workitem
+ /// The WorkItemInfo of te workitem
/// Callback delegate for the callback.
/// State with which to call the callback delegate.
///
@@ -207,10 +259,12 @@ namespace Amib.Threading.Internal
_workItemsGroup = workItemsGroup;
_workItemInfo = workItemInfo;
+#if !(WindowsCE)
if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
{
_callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
}
+#endif
_callback = callback;
_state = state;
@@ -220,8 +274,11 @@ namespace Amib.Threading.Internal
internal void Initialize()
{
- _workItemState = WorkItemState.InQueue;
- _workItemCompleted = null;
+ // 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;
}
@@ -237,17 +294,16 @@ namespace Amib.Threading.Internal
public CanceledWorkItemsGroup CanceledWorkItemsGroup
{
- get
- {
- return _canceledWorkItemsGroup;
- }
-
- set
- {
- _canceledWorkItemsGroup = value;
- }
+ get { return _canceledWorkItemsGroup; }
+ set { _canceledWorkItemsGroup = value; }
}
+ public CanceledWorkItemsGroup CanceledSmartThreadPool
+ {
+ get { return _canceledSmartThreadPool; }
+ set { _canceledSmartThreadPool = value; }
+ }
+
///
/// Change the state of the work item to in progress if it wasn't canceled.
///
@@ -275,6 +331,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);
}
@@ -321,41 +380,89 @@ namespace Amib.Threading.Internal
_workItemCompletedEvent(this);
}
}
- catch // Ignore exceptions
+ catch // Suppress exceptions
{}
}
+ internal void FireWorkItemStarted()
+ {
+ try
+ {
+ if (null != _workItemStartedEvent)
+ {
+ _workItemStartedEvent(this);
+ }
+ }
+ catch // Suppress exceptions
+ { }
+ }
+
///
/// Execute the work item
///
private void ExecuteWorkItem()
{
+
+#if !(WindowsCE)
CallerThreadContext ctc = null;
if (null != _callerContext)
{
ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
CallerThreadContext.Apply(_callerContext);
}
+#endif
Exception exception = null;
object result = null;
try
{
- result = _callback(_state);
+ try
+ {
+ result = _callback(_state);
+ }
+ catch (Exception e)
+ {
+ // Save the exception so we can rethrow it later
+ exception = e;
+ }
+
+ // Remove the value of the execution thread, so it will not be possible 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.
+ }
}
- catch (Exception e)
+ // We must treat the ThreadAbortException or else it will be stored in the exception variable
+ catch (ThreadAbortException tae)
{
- // Save the exception so we can rethrow it later
- exception = e;
+ // Check if the work item was cancelled
+ if ((string)tae.ExceptionState == "Cancel")
+ {
+#if !(WindowsCE)
+ Thread.ResetAbort();
+#endif
+ }
}
-
+
+#if !(WindowsCE)
if (null != _callerContext)
{
CallerThreadContext.Apply(ctc);
}
+#endif
- SetResult(result, exception);
+ if (!SmartThreadPool.IsWorkItemCanceled)
+ {
+ SetResult(result, exception);
+ }
}
///
@@ -367,7 +474,7 @@ namespace Amib.Threading.Internal
{
try
{
- _workItemInfo.PostExecuteWorkItemCallback(this._workItemResult);
+ _workItemInfo.PostExecuteWorkItemCallback(_workItemResult);
}
catch (Exception e)
{
@@ -380,6 +487,8 @@ namespace Amib.Threading.Internal
/// Set the result of the work item to return
///
/// The result of the work item
+ /// The exception that was throw while the workitem executed, null
+ /// if there was no exception.
internal void SetResult(object result, Exception exception)
{
_result = result;
@@ -399,33 +508,33 @@ namespace Amib.Threading.Internal
///
/// Wait for all work items to complete
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
///
/// A cancel wait handle to interrupt the wait if needed
///
- /// true when every work item in workItemResults has completed; otherwise false.
+ /// true when every work item in waitableResults has completed; otherwise false.
///
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 = EventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
}
else
{
@@ -448,7 +557,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))
@@ -458,8 +567,8 @@ namespace Amib.Threading.Internal
}
whs[0] = waitHandles[i];
- int result = WaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
- if((result > 0) || (WaitHandle.WaitTimeout == result))
+ int result = EventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext);
+ if ((result > 0) || (EventWaitHandle.WaitTimeout == result))
{
success = false;
break;
@@ -474,7 +583,7 @@ namespace Amib.Threading.Internal
}
}
// Release the wait handles
- ReleaseWaitHandles(workItemResults);
+ ReleaseWaitHandles(waitableResults);
return success;
}
@@ -482,7 +591,7 @@ namespace Amib.Threading.Internal
///
/// Waits for any of the work items in the specified array to complete, cancel, or timeout
///
- /// Array of work item result objects
+ /// Array of work item result objects
/// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.
///
/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false.
@@ -491,38 +600,38 @@ 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.
///
- internal static int WaitAny(
- IWorkItemResult [] workItemResults,
+ internal static int WaitAny(
+ 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 = EventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
// Treat cancel as timeout
if (null != cancelWaitHandle)
{
- if (result == workItemResults.Length)
+ if (result == waitableResults.Length)
{
- result = WaitHandle.WaitTimeout;
+ result = EventWaitHandle.WaitTimeout;
}
}
- ReleaseWaitHandles(workItemResults);
+ ReleaseWaitHandles(waitableResults);
return result;
}
@@ -530,16 +639,16 @@ namespace Amib.Threading.Internal
///
/// Fill an array of wait handles with the work items wait handles.
///
- /// An array of work item results
+ /// An array of work item results
/// An array of wait handles to fill
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();
}
@@ -548,31 +657,40 @@ namespace Amib.Threading.Internal
///
/// Release the work items' wait handles
///
- /// An array of work item results
- private static void ReleaseWaitHandles(IWorkItemResult [] workItemResults)
+ /// An array of work item results
+ 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()
+ private WorkItemState GetWorkItemState()
{
- if (_canceledWorkItemsGroup.IsCanceled)
- {
- return WorkItemState.Canceled;
- }
- return _workItemState;
+ lock (this)
+ {
+ if (WorkItemState.Completed == _workItemState || WorkItemState.InProgress == _workItemState)
+ {
+ return _workItemState;
+ }
+ if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled)
+ {
+ return WorkItemState.Canceled;
+ }
+
+ return _workItemState;
+ }
}
+
+
///
/// Sets the work item's state
///
@@ -581,7 +699,10 @@ namespace Amib.Threading.Internal
{
lock(this)
{
- _workItemState = workItemState;
+ if (IsValidStatesTransition(_workItemState, workItemState))
+ {
+ _workItemState = workItemState;
+ }
}
}
@@ -615,29 +736,61 @@ namespace Amib.Threading.Internal
/// Cancel the work item if it didn't start running yet.
///
/// Returns true on success or false if the work item is in progress or already completed
- private bool Cancel()
+ private bool Cancel(bool abortExecution)
{
- lock(this)
+#if (WindowsCE)
+ 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;
+ success = true;
+ break;
case WorkItemState.Completed:
- case WorkItemState.InProgress:
- //Debug.WriteLine("Work item cannot be canceled");
- return false;
+ //Debug.WriteLine("Work item cannot be canceled");
+ 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
+ {
+ success = true;
+ signalComplete = true;
+ }
+ 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;
}
///
@@ -651,7 +804,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)
{
@@ -704,7 +857,7 @@ namespace Amib.Threading.Internal
else
{
WaitHandle wh = GetWaitHandle();
- int result = WaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
+ int result = EventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle });
ReleaseWaitHandle();
switch(result)
@@ -715,7 +868,7 @@ namespace Amib.Threading.Internal
// work item (not the get result)
break;
case 1:
- case WaitHandle.WaitTimeout:
+ case EventWaitHandle.WaitTimeout:
throw new WorkItemTimeoutException("Work item timeout");
default:
Debug.Assert(false);
@@ -747,7 +900,7 @@ namespace Amib.Threading.Internal
{
if (null == _workItemCompleted)
{
- _workItemCompleted = new ManualResetEvent(IsCompleted);
+ _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted);
}
++_workItemCompletedRefCount;
}
@@ -844,12 +997,12 @@ namespace Amib.Threading.Internal
#region WorkItemResult class
- private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult
+ private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult
{
///
/// A back reference to the work item
///
- private WorkItem _workItem;
+ private readonly WorkItem _workItem;
public WorkItemResult(WorkItem workItem)
{
@@ -929,11 +1082,16 @@ namespace Amib.Threading.Internal
return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e);
}
- public bool Cancel()
+ public bool Cancel()
{
- return _workItem.Cancel();
+ return Cancel(false);
}
+ public bool Cancel(bool abortExecution)
+ {
+ return _workItem.Cancel(abortExecution);
+ }
+
public object State
{
get
@@ -998,7 +1156,21 @@ namespace Amib.Threading.Internal
}
#endregion
- }
+
+ #region IInternalWorkItemResult Members
+
+ public IWorkItemResult GetWorkItemResult()
+ {
+ return this;
+ }
+
+ public IWorkItemResult GetWorkItemResultT()
+ {
+ return new WorkItemResultTWrapper(this);
+ }
+
+ #endregion
+ }
#endregion
diff --git a/SmartThreadPool/WorkItemFactory.cs b/SmartThreadPool/WorkItemFactory.cs
index 1f96149..3f0dccd 100644
--- a/SmartThreadPool/WorkItemFactory.cs
+++ b/SmartThreadPool/WorkItemFactory.cs
@@ -1,6 +1,3 @@
-// Ami Bar
-// amibar@gmail.com
-
using System;
namespace Amib.Threading.Internal
@@ -12,6 +9,7 @@ namespace Amib.Threading.Internal
///
/// Create a new work item
///
+ /// The WorkItemsGroup of this workitem
/// Work item group start information
/// A callback to execute
/// Returns a work item
@@ -26,7 +24,8 @@ namespace Amib.Threading.Internal
///
/// Create a new work item
///
- /// Work item group start information
+ /// The WorkItemsGroup of this workitem
+ /// Work item group start information
/// A callback to execute
/// The priority of the work item
/// Returns a work item
@@ -42,7 +41,8 @@ namespace Amib.Threading.Internal
///
/// Create a new work item
///
- /// Work item group start information
+ /// The WorkItemsGroup of this workitem
+ /// Work item group start information
/// Work item info
/// A callback to execute
/// Returns a work item
@@ -63,7 +63,8 @@ namespace Amib.Threading.Internal
///
/// Create a new work item
///
- /// Work item group start information
+ /// The WorkItemsGroup of this workitem
+ /// Work item group start information
/// A callback to execute
///
/// The context object of the work item. Used for passing arguments to the work item.
@@ -76,13 +77,14 @@ namespace Amib.Threading.Internal
object state)
{
ValidateCallback(callback);
-
+
WorkItemInfo workItemInfo = new WorkItemInfo();
workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
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
///
/// Create a new work item
///
+ /// The work items group
/// Work item group start information
/// A callback to execute
///
@@ -131,6 +134,7 @@ namespace Amib.Threading.Internal
///
/// Create a new work item
///
+ /// The work items group
/// Work item group start information
/// Work item information
/// A callback to execute
@@ -138,28 +142,29 @@ namespace Amib.Threading.Internal
/// The context object of the work item. Used for passing arguments to the work item.
///
/// Returns a work item
- public static WorkItem CreateWorkItem(
- IWorkItemsGroup workItemsGroup,
- WIGStartInfo wigStartInfo,
- WorkItemInfo workItemInfo,
- WorkItemCallback callback,
- object state)
- {
- ValidateCallback(callback);
- ValidateCallback(workItemInfo.PostExecuteWorkItemCallback);
+ public static WorkItem CreateWorkItem(
+ IWorkItemsGroup workItemsGroup,
+ WIGStartInfo wigStartInfo,
+ WorkItemInfo workItemInfo,
+ WorkItemCallback callback,
+ object state)
+ {
+ ValidateCallback(callback);
+ ValidateCallback(workItemInfo.PostExecuteWorkItemCallback);
- WorkItem workItem = new WorkItem(
- workItemsGroup,
- new WorkItemInfo(workItemInfo),
- callback,
- state);
+ WorkItem workItem = new WorkItem(
+ workItemsGroup,
+ new WorkItemInfo(workItemInfo),
+ callback,
+ state);
- return workItem;
- }
+ return workItem;
+ }
///
/// Create a new work item
///
+ /// The work items group
/// Work item group start information
/// A callback to execute
///
@@ -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
///
/// Create a new work item
///
+ /// The work items group
/// Work item group start information
/// A callback to execute
///
@@ -239,6 +246,7 @@ namespace Amib.Threading.Internal
///
/// Create a new work item
///
+ /// The work items group
/// Work item group start information
/// A callback to execute
///
@@ -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
///
/// Create a new work item
///
+ /// The work items group
/// Work item group start information
/// A callback to execute
///
diff --git a/SmartThreadPool/WorkItemInfo.cs b/SmartThreadPool/WorkItemInfo.cs
index b07c3f0..8b0f3ee 100644
--- a/SmartThreadPool/WorkItemInfo.cs
+++ b/SmartThreadPool/WorkItemInfo.cs
@@ -1,6 +1,3 @@
-// Ami Bar
-// amibar@gmail.com
-
namespace Amib.Threading
{
#region WorkItemInfo class
@@ -61,37 +58,55 @@ namespace Amib.Threading
_workItemPriority = workItemInfo._workItemPriority;
}
- public bool UseCallerCallContext
+ ///
+ /// Get/Set if to use the caller's security context
+ ///
+ public bool UseCallerCallContext
{
get { return _useCallerCallContext; }
set { _useCallerCallContext = value; }
}
- public bool UseCallerHttpContext
+ ///
+ /// Get/Set if to use the caller's HTTP context
+ ///
+ public bool UseCallerHttpContext
{
get { return _useCallerHttpContext; }
set { _useCallerHttpContext = value; }
}
- public bool DisposeOfStateObjects
+ ///
+ /// Get/Set if to dispose of the state object of a work item
+ ///
+ public bool DisposeOfStateObjects
{
get { return _disposeOfStateObjects; }
set { _disposeOfStateObjects = value; }
}
- public CallToPostExecute CallToPostExecute
+ ///
+ /// Get/Set the run the post execute options
+ ///
+ public CallToPostExecute CallToPostExecute
{
get { return _callToPostExecute; }
set { _callToPostExecute = value; }
}
- public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
+ ///
+ /// Get/Set the post execute callback
+ ///
+ public PostExecuteWorkItemCallback PostExecuteWorkItemCallback
{
get { return _postExecuteWorkItemCallback; }
set { _postExecuteWorkItemCallback = value; }
}
- public WorkItemPriority WorkItemPriority
+ ///
+ /// Get/Set the work items priority
+ ///
+ public WorkItemPriority WorkItemPriority
{
get { return _workItemPriority; }
set { _workItemPriority = value; }
diff --git a/SmartThreadPool/WorkItemResultTWrapper.cs b/SmartThreadPool/WorkItemResultTWrapper.cs
new file mode 100644
index 0000000..d1eff95
--- /dev/null
+++ b/SmartThreadPool/WorkItemResultTWrapper.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Threading;
+
+namespace Amib.Threading.Internal
+{
+ #region WorkItemResultTWrapper class
+
+ internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult
+ {
+ private readonly IWorkItemResult _workItemResult;
+
+ public WorkItemResultTWrapper(IWorkItemResult workItemResult)
+ {
+ _workItemResult = workItemResult;
+ }
+
+ #region IWorkItemResult 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 GetWorkItemResultT()
+ {
+ return (IWorkItemResult)this;
+ }
+
+ #endregion
+
+ #endregion
+ }
+
+ #endregion
+
+}
diff --git a/SmartThreadPool/WorkItemsGroup.cs b/SmartThreadPool/WorkItemsGroup.cs
index c618199..576d084 100644
--- a/SmartThreadPool/WorkItemsGroup.cs
+++ b/SmartThreadPool/WorkItemsGroup.cs
@@ -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 description for WorkItemsGroup.
///
- public class WorkItemsGroup : IWorkItemsGroup
+ public class WorkItemsGroup : WorkItemsGroupBase
{
#region Private members
- private object _lock = new object();
- ///
- /// Contains the name of this instance of SmartThreadPool.
- /// Can be changed by the user.
- ///
- private string _name = "WorkItemsGroup";
+ private readonly object _lock = new object();
///
/// A reference to the SmartThreadPool instance that created this
/// WorkItemsGroup.
///
- private SmartThreadPool _stp;
+ private readonly SmartThreadPool _stp;
///
/// The OnIdle event
///
private event WorkItemsGroupIdleHandler _onIdle;
+ ///
+ /// A flag to indicate if the Work Items Group is now suspended.
+ ///
+ private bool _isSuspended;
+
///
/// Defines how many work items of this WorkItemsGroup can run at once.
///
@@ -44,7 +42,7 @@ namespace Amib.Threading.Internal
/// Priority queue to hold work items before they are passed
/// to the SmartThreadPool.
///
- private PriorityQueue _workItemsQueue;
+ private readonly PriorityQueue _workItemsQueue;
///
/// Indicate how many work items are waiting in the SmartThreadPool
@@ -63,12 +61,13 @@ namespace Amib.Threading.Internal
///
/// WorkItemsGroup start information
///
- private WIGStartInfo _workItemsGroupStartInfo;
+ private readonly WIGStartInfo _workItemsGroupStartInfo;
///
/// Signaled when all of the WorkItemsGroup's work item completed.
///
- private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
+ //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
+ private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
///
/// A common object for all the work items that this work items group
@@ -80,322 +79,156 @@ namespace Amib.Threading.Internal
#region Construction
- public WorkItemsGroup(
+ public WorkItemsGroup(
SmartThreadPool stp,
int concurrency,
WIGStartInfo wigStartInfo)
{
if (concurrency <= 0)
{
- throw new ArgumentOutOfRangeException("concurrency", concurrency, "concurrency must be greater than zero");
+ throw new ArgumentOutOfRangeException(
+ "concurrency",
+#if !(WindowsCE)
+ 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
- ///
- /// Get/Set the name of the SmartThreadPool instance
- ///
- public string Name
- {
- get
- {
- return _name;
- }
+ public override int Concurrency
+ {
+ get { return _concurrency; }
+ set
+ {
+ Debug.Assert(value > 0);
- set
- {
- _name = value;
- }
- }
+ int diff = value - _concurrency;
+ _concurrency = value;
+ if (diff > 0)
+ {
+ EnqueueToSTPNextNWorkItem(diff);
+ }
+ }
+ }
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
- {
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback);
- EnqueueToSTPNextWorkItem(workItem);
- return workItem.GetWorkItemResult();
- }
+ public override int WaitingCallbacks
+ {
+ get { return _workItemsQueue.Count; }
+ }
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- /// The priority of the work item
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
- {
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, workItemPriority);
- EnqueueToSTPNextWorkItem(workItem);
- return workItem.GetWorkItemResult();
- }
+ 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;
+ }
+ }
- ///
- /// Queue a work item
- ///
- /// Work item info
- /// A callback to execute
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
- {
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback);
- EnqueueToSTPNextWorkItem(workItem);
- return workItem.GetWorkItemResult();
- }
+ ///
+ /// WorkItemsGroup start information
+ ///
+ public override WIGStartInfo WIGStartInfo
+ {
+ get { return _workItemsGroupStartInfo; }
+ }
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
- {
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state);
- EnqueueToSTPNextWorkItem(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- /// The work item priority
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
- {
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, workItemPriority);
- EnqueueToSTPNextWorkItem(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// Work item information
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
- {
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, workItemInfo, callback, state);
- EnqueueToSTPNextWorkItem(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// Returns a work item result
- public IWorkItemResult QueueWorkItem(
- WorkItemCallback callback,
- object state,
- PostExecuteWorkItemCallback postExecuteWorkItemCallback)
- {
- WorkItem workItem = WorkItemFactory.CreateWorkItem(this, _workItemsGroupStartInfo, callback, state, postExecuteWorkItemCallback);
- EnqueueToSTPNextWorkItem(workItem);
- return workItem.GetWorkItemResult();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// The work item priority
- /// Returns a work item result
- 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();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// Indicates on which cases to call to the post execute callback
- /// Returns a work item result
- 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();
- }
-
- ///
- /// Queue a work item
- ///
- /// A callback to execute
- ///
- /// The context object of the work item. Used for passing arguments to the work item.
- ///
- ///
- /// A delegate to call after the callback completion
- ///
- /// Indicates on which cases to call to the post execute callback
- /// The work item priority
- /// Returns a work item result
- 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();
- }
-
- ///
- /// Wait for the thread pool to be idle
- ///
- public void WaitForIdle()
- {
- WaitForIdle(Timeout.Infinite);
- }
-
- ///
- /// Wait for the thread pool to be idle
- ///
- public bool WaitForIdle(TimeSpan timeout)
- {
- return WaitForIdle((int)timeout.TotalMilliseconds);
- }
-
- ///
- /// Wait for the thread pool to be idle
- ///
- public bool WaitForIdle(int millisecondsTimeout)
- {
- _stp.ValidateWorkItemsGroupWaitForIdle(this);
- return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
- }
-
- public int WaitingCallbacks
- {
- get
- {
- return _workItemsQueue.Count;
- }
- }
-
- public event WorkItemsGroupIdleHandler OnIdle
- {
- add
- {
- _onIdle += value;
- }
- remove
- {
- _onIdle -= value;
- }
- }
-
- public void Cancel()
- {
- lock(_lock)
- {
- _canceledWorkItemsGroup.IsCanceled = true;
- _workItemsQueue.Clear();
- _workItemsInStpQueue = 0;
- _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
- }
- }
-
- public void Start()
- {
- lock (this)
- {
- if (!_workItemsGroupStartInfo.StartSuspended)
- {
- return;
- }
- _workItemsGroupStartInfo.StartSuspended = false;
- }
+ ///
+ /// Start the Work Items Group if it was started suspended
+ ///
+ public override void Start()
+ {
+ // If the Work Items Group already started then quit
+ if (!_isSuspended)
+ {
+ return;
+ }
+ _isSuspended = false;
- for(int i = 0; i < _concurrency; ++i)
- {
- EnqueueToSTPNextWorkItem(null, false);
- }
+ EnqueueToSTPNextNWorkItem(_concurrency);
+ }
+
+ public override void Cancel(bool abortExecution)
+ {
+ lock (_lock)
+ {
+ _canceledWorkItemsGroup.IsCanceled = true;
+ _workItemsQueue.Clear();
+ _workItemsInStpQueue = 0;
+ _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
+ }
+
+ if (abortExecution)
+ {
+ _stp.CancelAbortWorkItemsGroup(this);
+ }
+ }
+
+ ///
+ /// Wait for the thread pool to be idle
+ ///
+ public override bool WaitForIdle(int millisecondsTimeout)
+ {
+ SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
+ return _isIdleWaitHandle.WaitOne(millisecondsTimeout, false);
+ }
+
+ public override event WorkItemsGroupIdleHandler OnIdle
+ {
+ add { _onIdle += value; }
+ remove { _onIdle -= value; }
}
- #endregion
+ #endregion
#region Private methods
- private void RegisterToWorkItemCompletion(IWorkItemResult wir)
+ 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()
+ public void OnSTPIsStarting()
{
- lock (this)
- {
- if (_workItemsGroupStartInfo.StartSuspended)
- {
- return;
- }
- }
+ if (_isSuspended)
+ {
+ return;
+ }
- for(int i = 0; i < _concurrency; ++i)
- {
- EnqueueToSTPNextWorkItem(null, false);
- }
+ EnqueueToSTPNextNWorkItem(_concurrency);
}
+ public void EnqueueToSTPNextNWorkItem(int count)
+ {
+ for (int i = 0; i < count; ++i)
+ {
+ EnqueueToSTPNextWorkItem(null, false);
+ }
+ }
+
private object FireOnIdle(object state)
{
FireOnIdleImpl(_onIdle);
@@ -417,8 +250,7 @@ namespace Amib.Threading.Internal
{
eh(this);
}
- // Ignore exceptions
- catch{}
+ catch { } // Suppress exceptions
}
}
@@ -435,7 +267,12 @@ namespace Amib.Threading.Internal
EnqueueToSTPNextWorkItem(null, true);
}
- private void EnqueueToSTPNextWorkItem(WorkItem workItem)
+ internal override void Enqueue(WorkItem workItem)
+ {
+ EnqueueToSTPNextWorkItem(workItem);
+ }
+
+ private void EnqueueToSTPNextWorkItem(WorkItem workItem)
{
EnqueueToSTPNextWorkItem(workItem, false);
}
@@ -475,8 +312,8 @@ namespace Amib.Threading.Internal
(0 == _workItemsInStpQueue))
{
_stp.RegisterWorkItemsGroup(this);
- Trace.WriteLine("WorkItemsGroup " + Name + " is NOT idle");
- _isIdleWaitHandle.Reset();
+ IsIdle = false;
+ _isIdleWaitHandle.Reset();
}
}
@@ -486,19 +323,31 @@ namespace Amib.Threading.Internal
if (0 == _workItemsInStpQueue)
{
_stp.UnregisterWorkItemsGroup(this);
- Trace.WriteLine("WorkItemsGroup " + Name + " is idle");
- _isIdleWaitHandle.Set();
- _stp.QueueWorkItem(new WorkItemCallback(this.FireOnIdle));
+ IsIdle = true;
+ _isIdleWaitHandle.Set();
+ if (decrementWorkItemsInStpQueue)
+ {
+ _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;
}
}
@@ -506,7 +355,7 @@ namespace Amib.Threading.Internal
}
#endregion
- }
+ }
#endregion
}
diff --git a/SmartThreadPool/WorkItemsGroupBase.cs b/SmartThreadPool/WorkItemsGroupBase.cs
new file mode 100644
index 0000000..4369360
--- /dev/null
+++ b/SmartThreadPool/WorkItemsGroupBase.cs
@@ -0,0 +1,448 @@
+using System;
+using System.Threading;
+
+namespace Amib.Threading.Internal
+{
+ public abstract class WorkItemsGroupBase : IWorkItemsGroup
+ {
+ #region Private Fields
+
+ ///
+ /// Contains the name of this instance of SmartThreadPool.
+ /// Can be changed by the user.
+ ///
+ private string _name = "WorkItemsGroupBase";
+
+ ///
+ /// Inidcates if the SmartThreadPool/WorkItemsGroup is idle.
+ /// Its value is true if the _isIdleWaitHandle is set.
+ ///
+ private bool _isIdle = true;
+
+ #endregion
+
+ #region IWorkItemsGroup Members
+
+ #region Public Methods
+
+ ///
+ /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance
+ ///
+ 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
+
+ ///
+ /// Cancel all the work items.
+ /// Same as Cancel(false)
+ ///
+ public virtual void Cancel()
+ {
+ Cancel(false);
+ }
+
+ ///
+ /// Wait for the SmartThreadPool/WorkItemsGroup to be idle
+ ///
+ public void WaitForIdle()
+ {
+ WaitForIdle(Timeout.Infinite);
+ }
+
+ ///
+ /// Wait for the SmartThreadPool/WorkItemsGroup to be idle
+ ///
+ public bool WaitForIdle(TimeSpan timeout)
+ {
+ return WaitForIdle((int)timeout.TotalMilliseconds);
+ }
+
+ ///
+ /// IsIdle is true when there are no work items running or queued.
+ ///
+ public bool IsIdle
+ {
+ get { return _isIdle; }
+ protected set { _isIdle = value; }
+ }
+
+ #endregion
+
+ #region QueueWorkItem
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ /// Returns a work item result
+ public IWorkItemResult QueueWorkItem(WorkItemCallback callback)
+ {
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ /// The priority of the work item
+ /// Returns a work item result
+ public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// Work item info
+ /// A callback to execute
+ /// Returns a work item result
+ public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ /// Returns a work item result
+ public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state)
+ {
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ /// The work item priority
+ /// Returns a work item result
+ public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// Work item information
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ /// Returns a work item result
+ public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// Returns a work item result
+ public IWorkItemResult QueueWorkItem(
+ WorkItemCallback callback,
+ object state,
+ PostExecuteWorkItemCallback postExecuteWorkItemCallback)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// The work item priority
+ /// Returns a work item result
+ 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();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// Indicates on which cases to call to the post execute callback
+ /// Returns a work item result
+ 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();
+ }
+
+ ///
+ /// Queue a work item
+ ///
+ /// A callback to execute
+ ///
+ /// The context object of the work item. Used for passing arguments to the work item.
+ ///
+ ///
+ /// A delegate to call after the callback completion
+ ///
+ /// Indicates on which cases to call to the post execute callback
+ /// The work item priority
+ /// Returns a work item result
+ 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)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ action.Invoke();
+ return null;
+ });
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ public IWorkItemResult QueueWorkItem(Action action, T arg)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ action.Invoke(arg);
+ return null;
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ action.Invoke(arg1, arg2);
+ return null;
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ action.Invoke(arg1, arg2, arg3);
+ return null;
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ public IWorkItemResult QueueWorkItem(
+ Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ action.Invoke(arg1, arg2, arg3, arg4);
+ return null;
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null);
+ Enqueue(workItem);
+ return workItem.GetWorkItemResult();
+ }
+
+ #endregion
+
+ #region QueueWorkItem(Func<...>)
+
+ public IWorkItemResult QueueWorkItem(Func func)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ return func.Invoke();
+ });
+ Enqueue(workItem);
+ return new WorkItemResultTWrapper(workItem.GetWorkItemResult());
+ }
+
+ public IWorkItemResult QueueWorkItem(Func func, T arg)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ return func.Invoke(arg);
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null);
+ Enqueue(workItem);
+ return new WorkItemResultTWrapper(workItem.GetWorkItemResult());
+ }
+
+ public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ return func.Invoke(arg1, arg2);
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null);
+ Enqueue(workItem);
+ return new WorkItemResultTWrapper(workItem.GetWorkItemResult());
+ }
+
+ public IWorkItemResult QueueWorkItem(
+ Func func, T1 arg1, T2 arg2, T3 arg3)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ return func.Invoke(arg1, arg2, arg3);
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null);
+ Enqueue(workItem);
+ return new WorkItemResultTWrapper(workItem.GetWorkItemResult());
+ }
+
+ public IWorkItemResult QueueWorkItem(
+ Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
+ {
+ PreQueueWorkItem();
+ WorkItem workItem = WorkItemFactory.CreateWorkItem(
+ this,
+ WIGStartInfo,
+ delegate
+ {
+ return func.Invoke(arg1, arg2, arg3, arg4);
+ },
+ WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null);
+ Enqueue(workItem);
+ return new WorkItemResultTWrapper(workItem.GetWorkItemResult());
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/SmartThreadPool/WorkItemsQueue.cs b/SmartThreadPool/WorkItemsQueue.cs
index 2dad878..811c82f 100644
--- a/SmartThreadPool/WorkItemsQueue.cs
+++ b/SmartThreadPool/WorkItemsQueue.cs
@@ -1,6 +1,3 @@
-// Ami Bar
-// amibar@gmail.com
-
using System;
using System.Threading;
@@ -18,7 +15,7 @@ namespace Amib.Threading.Internal
///
/// Waiters queue (implemented as stack).
///
- private WaiterEntry _headWaiterEntry = new WaiterEntry();
+ private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();
///
/// Waiters count
@@ -28,20 +25,50 @@ namespace Amib.Threading.Internal
///
/// Work items queue
///
- private PriorityQueue _workItems = new PriorityQueue();
+ private readonly PriorityQueue _workItems = new PriorityQueue();
///
/// Indicate that work items are allowed to be queued
///
private bool _isWorkItemsQueueActive = true;
- ///
- /// Each thread in the thread pool keeps its own waiter entry.
- ///
- [ThreadStatic]
- private static WaiterEntry _waiterEntry;
- ///
+#if (WindowsCE)
+ private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot();
+#else
+
+ [ThreadStatic]
+ private static WaiterEntry _waiterEntry;
+#endif
+
+
+ ///
+ /// Each thread in the thread pool keeps its own waiter entry.
+ ///
+ private static WaiterEntry CurrentWaiterEntry
+ {
+#if (WindowsCE)
+ get
+ {
+ return Thread.GetData(_waiterEntrySlot) as WaiterEntry;
+ }
+ set
+ {
+ Thread.SetData(_waiterEntrySlot, value);
+ }
+#else
+ get
+ {
+ return _waiterEntry;
+ }
+ set
+ {
+ _waiterEntry = value;
+ }
+#endif
+ }
+
+ ///
/// A flag that indicates if the WorkItemsQueue has been disposed.
///
private bool _isDisposed = false;
@@ -57,11 +84,7 @@ namespace Amib.Threading.Internal
{
get
{
- lock(this)
- {
- ValidateNotDisposed();
- return _workItems.Count;
- }
+ return _workItems.Count;
}
}
@@ -72,11 +95,7 @@ namespace Amib.Threading.Internal
{
get
{
- lock(this)
- {
- ValidateNotDisposed();
- return _waitersCount;
- }
+ return _waitersCount;
}
}
@@ -144,19 +163,19 @@ 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 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.
- WaiterEntry waiterEntry = null;
+ WaiterEntry waiterEntry;
WorkItem workItem = null;
lock(this)
@@ -169,19 +188,18 @@ namespace Amib.Threading.Internal
workItem = _workItems.Dequeue() as WorkItem;
return workItem;
}
- // No waiting work items ...
- else
- {
- // Get the wait entry for the waiters queue
- waiterEntry = GetThreadWaiterEntry();
- // Put the waiter with the other waiters
- PushWaiter(waiterEntry);
- }
+ // No waiting work items ...
+
+ // Get the wait 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 [] {
+ WaitHandle [] waitHandles = new WaitHandle[] {
waiterEntry.WaitHandle,
cancelEvent };
@@ -189,10 +207,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)
+ // It just doesn't work, I don't know why, so I have two lock(this)
// statments insted of one.
- int index = WaitHandle.WaitAny(
+ int index = EventWaitHandle.WaitAny(
waitHandles,
millisecondsTimeout,
true);
@@ -242,7 +260,7 @@ namespace Amib.Threading.Internal
/// Cleanup the work items queue, hence no more work
/// items are allowed to be queue
///
- protected virtual void Cleanup()
+ private void Cleanup()
{
lock(this)
{
@@ -279,6 +297,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 +322,14 @@ namespace Amib.Threading.Internal
///
/// 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 +451,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
///
/// Event to signal the waiter that it got the work item.
///
- private AutoResetEvent _waitHandle = new AutoResetEvent(false);
+ //private AutoResetEvent _waitHandle = new AutoResetEvent(false);
+ private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();
///
/// Flag to know if this waiter already quited from the queue
@@ -550,16 +584,14 @@ namespace Amib.Threading.Internal
public void Dispose()
{
- if (!_isDisposed)
+ lock (this)
{
- Close();
+ if (!_isDisposed)
+ {
+ Close();
+ }
_isDisposed = true;
}
- }
-
- ~WaiterEntry()
- {
- Dispose();
}
#endregion
@@ -574,14 +606,8 @@ namespace Amib.Threading.Internal
if (!_isDisposed)
{
Cleanup();
- _isDisposed = true;
- GC.SuppressFinalize(this);
}
- }
-
- ~WorkItemsQueue()
- {
- Cleanup();
+ _isDisposed = true;
}
private void ValidateNotDisposed()
diff --git a/TestSmartThreadPool/Form1.cs b/TestSmartThreadPool/Form1.cs
index e26b201..bdf9801 100644
--- a/TestSmartThreadPool/Form1.cs
+++ b/TestSmartThreadPool/Form1.cs
@@ -83,487 +83,488 @@ namespace TestSmartThreadPool
///
private void InitializeComponent()
{
- this.components = new System.ComponentModel.Container();
- System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form1));
- this.btnStart = new System.Windows.Forms.Button();
- this.btnStop = new System.Windows.Forms.Button();
- this.label1 = new System.Windows.Forms.Label();
- this.label3 = new System.Windows.Forms.Label();
- this.label4 = new System.Windows.Forms.Label();
- this.label2 = new System.Windows.Forms.Label();
- this.lblThreadsInPool = new System.Windows.Forms.Label();
- this.label5 = new System.Windows.Forms.Label();
- this.spinIdleTimeout = new System.Windows.Forms.NumericUpDown();
- this.spinMaxThreads = new System.Windows.Forms.NumericUpDown();
- this.spinMinThreads = new System.Windows.Forms.NumericUpDown();
- this.spinInterval = new System.Windows.Forms.NumericUpDown();
- this.lblThreadInUse = new System.Windows.Forms.Label();
- this.label7 = new System.Windows.Forms.Label();
- this.timerPoll = new System.Windows.Forms.Timer(this.components);
- this.spinConsumingTime = new System.Windows.Forms.NumericUpDown();
- this.label6 = new System.Windows.Forms.Label();
- this.lblWaitingCallbacks = new System.Windows.Forms.Label();
- this.label9 = new System.Windows.Forms.Label();
- this.label8 = new System.Windows.Forms.Label();
- this.label10 = new System.Windows.Forms.Label();
- this.lblWorkItemsGenerated = new System.Windows.Forms.Label();
- this.lblWorkItemsCompleted = new System.Windows.Forms.Label();
- this.groupBox2 = new System.Windows.Forms.GroupBox();
- this.groupBox3 = new System.Windows.Forms.GroupBox();
- this.groupBox1 = new System.Windows.Forms.GroupBox();
- this.usageThreadsInPool = new UsageControl.UsageControl();
- this.groupBox4 = new System.Windows.Forms.GroupBox();
- this.usageHistorySTP = new UsageControl.UsageHistoryControl();
- this.pcActiveThreads = new System.Diagnostics.PerformanceCounter();
- this.pcInUseThreads = new System.Diagnostics.PerformanceCounter();
- this.pcQueuedWorkItems = new System.Diagnostics.PerformanceCounter();
- this.pcCompletedWorkItems = new System.Diagnostics.PerformanceCounter();
- ((System.ComponentModel.ISupportInitialize)(this.spinIdleTimeout)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinMaxThreads)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinMinThreads)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinInterval)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinConsumingTime)).BeginInit();
- this.groupBox2.SuspendLayout();
- this.groupBox3.SuspendLayout();
- this.groupBox1.SuspendLayout();
- this.groupBox4.SuspendLayout();
- ((System.ComponentModel.ISupportInitialize)(this.pcActiveThreads)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.pcInUseThreads)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.pcQueuedWorkItems)).BeginInit();
- ((System.ComponentModel.ISupportInitialize)(this.pcCompletedWorkItems)).BeginInit();
- this.SuspendLayout();
- //
- // btnStart
- //
- this.btnStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.btnStart.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.btnStart.Location = new System.Drawing.Point(432, 352);
- this.btnStart.Name = "btnStart";
- this.btnStart.Size = new System.Drawing.Size(72, 24);
- this.btnStart.TabIndex = 0;
- this.btnStart.Text = "Start";
- this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
- //
- // btnStop
- //
- this.btnStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
- this.btnStop.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.btnStop.Location = new System.Drawing.Point(520, 352);
- this.btnStop.Name = "btnStop";
- this.btnStop.Size = new System.Drawing.Size(72, 24);
- this.btnStop.TabIndex = 1;
- this.btnStop.Text = "Stop";
- this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
- //
- // label1
- //
- this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label1.Location = new System.Drawing.Point(104, 256);
- this.label1.Name = "label1";
- this.label1.Size = new System.Drawing.Size(104, 24);
- this.label1.TabIndex = 2;
- this.label1.Text = "Minimum Threads";
- this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // label3
- //
- this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label3.Location = new System.Drawing.Point(104, 288);
- this.label3.Name = "label3";
- this.label3.Size = new System.Drawing.Size(104, 24);
- this.label3.TabIndex = 4;
- this.label3.Text = "Maximum Threads";
- this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // label4
- //
- this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label4.Location = new System.Drawing.Point(104, 320);
- this.label4.Name = "label4";
- this.label4.Size = new System.Drawing.Size(120, 24);
- this.label4.TabIndex = 5;
- this.label4.Text = "Idle timeout (Seconds)";
- this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // label2
- //
- this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label2.Location = new System.Drawing.Point(8, 16);
- this.label2.Name = "label2";
- this.label2.Size = new System.Drawing.Size(72, 24);
- this.label2.TabIndex = 3;
- this.label2.Text = "In pool (Red)";
- this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // lblThreadsInPool
- //
- this.lblThreadsInPool.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.lblThreadsInPool.Location = new System.Drawing.Point(80, 16);
- this.lblThreadsInPool.Name = "lblThreadsInPool";
- this.lblThreadsInPool.Size = new System.Drawing.Size(80, 24);
- this.lblThreadsInPool.TabIndex = 11;
- this.lblThreadsInPool.Text = "XXXXXXXXX";
- this.lblThreadsInPool.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
- //
- // label5
- //
- this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label5.Location = new System.Drawing.Point(336, 258);
- this.label5.Name = "label5";
- this.label5.Size = new System.Drawing.Size(280, 24);
- this.label5.TabIndex = 12;
- this.label5.Text = "Interval between work item production (milliseconds)";
- this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // spinIdleTimeout
- //
- this.spinIdleTimeout.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.spinIdleTimeout.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.spinIdleTimeout.Location = new System.Drawing.Point(8, 320);
- this.spinIdleTimeout.Minimum = new System.Decimal(new int[] {
- 1,
- 0,
- 0,
- 0});
- this.spinIdleTimeout.Name = "spinIdleTimeout";
- this.spinIdleTimeout.Size = new System.Drawing.Size(88, 29);
- this.spinIdleTimeout.TabIndex = 15;
- this.spinIdleTimeout.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
- this.spinIdleTimeout.Value = new System.Decimal(new int[] {
- 10,
- 0,
- 0,
- 0});
- //
- // spinMaxThreads
- //
- this.spinMaxThreads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.spinMaxThreads.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.spinMaxThreads.Location = new System.Drawing.Point(8, 288);
- this.spinMaxThreads.Minimum = new System.Decimal(new int[] {
- 1,
- 0,
- 0,
- 0});
- this.spinMaxThreads.Name = "spinMaxThreads";
- this.spinMaxThreads.Size = new System.Drawing.Size(88, 29);
- this.spinMaxThreads.TabIndex = 14;
- this.spinMaxThreads.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
- this.spinMaxThreads.Value = new System.Decimal(new int[] {
- 10,
- 0,
- 0,
- 0});
- this.spinMaxThreads.ValueChanged += new System.EventHandler(this.spinMaxThreads_ValueChanged);
- //
- // spinMinThreads
- //
- this.spinMinThreads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.spinMinThreads.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.spinMinThreads.Location = new System.Drawing.Point(8, 256);
- this.spinMinThreads.Name = "spinMinThreads";
- this.spinMinThreads.Size = new System.Drawing.Size(88, 29);
- this.spinMinThreads.TabIndex = 13;
- this.spinMinThreads.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
- this.spinMinThreads.ValueChanged += new System.EventHandler(this.spinMinThreads_ValueChanged);
- //
- // spinInterval
- //
- this.spinInterval.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.spinInterval.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.spinInterval.Increment = new System.Decimal(new int[] {
- 100,
- 0,
- 0,
- 0});
- this.spinInterval.Location = new System.Drawing.Point(240, 256);
- this.spinInterval.Maximum = new System.Decimal(new int[] {
- 100000,
- 0,
- 0,
- 0});
- this.spinInterval.Name = "spinInterval";
- this.spinInterval.Size = new System.Drawing.Size(88, 29);
- this.spinInterval.TabIndex = 16;
- this.spinInterval.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
- this.spinInterval.Value = new System.Decimal(new int[] {
- 100,
- 0,
- 0,
- 0});
- //
- // lblThreadInUse
- //
- this.lblThreadInUse.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.lblThreadInUse.Location = new System.Drawing.Point(80, 40);
- this.lblThreadInUse.Name = "lblThreadInUse";
- this.lblThreadInUse.Size = new System.Drawing.Size(80, 24);
- this.lblThreadInUse.TabIndex = 18;
- this.lblThreadInUse.Text = "XXXXXXXXX";
- this.lblThreadInUse.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
- //
- // label7
- //
- this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label7.Location = new System.Drawing.Point(8, 40);
- this.label7.Name = "label7";
- this.label7.Size = new System.Drawing.Size(80, 24);
- this.label7.TabIndex = 17;
- this.label7.Text = "Used (Green)";
- this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // timerPoll
- //
- this.timerPoll.Interval = 500;
- this.timerPoll.Tick += new System.EventHandler(this.timer1_Tick);
- //
- // spinConsumingTime
- //
- this.spinConsumingTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.spinConsumingTime.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.spinConsumingTime.Increment = new System.Decimal(new int[] {
- 100,
- 0,
- 0,
- 0});
- this.spinConsumingTime.Location = new System.Drawing.Point(240, 288);
- this.spinConsumingTime.Maximum = new System.Decimal(new int[] {
- 100000,
- 0,
- 0,
- 0});
- this.spinConsumingTime.Name = "spinConsumingTime";
- this.spinConsumingTime.Size = new System.Drawing.Size(88, 29);
- this.spinConsumingTime.TabIndex = 20;
- this.spinConsumingTime.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
- this.spinConsumingTime.Value = new System.Decimal(new int[] {
- 100,
- 0,
- 0,
- 0});
- //
- // label6
- //
- this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label6.Location = new System.Drawing.Point(336, 290);
- this.label6.Name = "label6";
- this.label6.Size = new System.Drawing.Size(216, 24);
- this.label6.TabIndex = 19;
- this.label6.Text = "Work item consuming time (milliseconds)";
- this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // lblWaitingCallbacks
- //
- this.lblWaitingCallbacks.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.lblWaitingCallbacks.Location = new System.Drawing.Point(64, 16);
- this.lblWaitingCallbacks.Name = "lblWaitingCallbacks";
- this.lblWaitingCallbacks.Size = new System.Drawing.Size(80, 24);
- this.lblWaitingCallbacks.TabIndex = 22;
- this.lblWaitingCallbacks.Text = "XXXXXXXXX";
- this.lblWaitingCallbacks.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
- //
- // label9
- //
- this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label9.Location = new System.Drawing.Point(8, 16);
- this.label9.Name = "label9";
- this.label9.Size = new System.Drawing.Size(48, 24);
- this.label9.TabIndex = 21;
- this.label9.Text = "Queued";
- this.label9.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // label8
- //
- this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label8.Location = new System.Drawing.Point(8, 40);
- this.label8.Name = "label8";
- this.label8.Size = new System.Drawing.Size(64, 24);
- this.label8.TabIndex = 25;
- this.label8.Text = "Generated";
- this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // label10
- //
- this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.label10.Location = new System.Drawing.Point(8, 64);
- this.label10.Name = "label10";
- this.label10.Size = new System.Drawing.Size(64, 24);
- this.label10.TabIndex = 26;
- this.label10.Text = "Completed";
- this.label10.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
- //
- // lblWorkItemsGenerated
- //
- this.lblWorkItemsGenerated.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.lblWorkItemsGenerated.Location = new System.Drawing.Point(64, 40);
- this.lblWorkItemsGenerated.Name = "lblWorkItemsGenerated";
- this.lblWorkItemsGenerated.Size = new System.Drawing.Size(80, 24);
- this.lblWorkItemsGenerated.TabIndex = 27;
- this.lblWorkItemsGenerated.Text = "XXXXXXXXX";
- this.lblWorkItemsGenerated.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
- //
- // lblWorkItemsCompleted
- //
- this.lblWorkItemsCompleted.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.lblWorkItemsCompleted.Location = new System.Drawing.Point(64, 64);
- this.lblWorkItemsCompleted.Name = "lblWorkItemsCompleted";
- this.lblWorkItemsCompleted.Size = new System.Drawing.Size(80, 24);
- this.lblWorkItemsCompleted.TabIndex = 28;
- this.lblWorkItemsCompleted.Text = "XXXXXXXXX";
- this.lblWorkItemsCompleted.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
- //
- // groupBox2
- //
- this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.groupBox2.Controls.Add(this.lblWaitingCallbacks);
- this.groupBox2.Controls.Add(this.label9);
- this.groupBox2.Controls.Add(this.label8);
- this.groupBox2.Controls.Add(this.label10);
- this.groupBox2.Controls.Add(this.lblWorkItemsGenerated);
- this.groupBox2.Controls.Add(this.lblWorkItemsCompleted);
- this.groupBox2.Location = new System.Drawing.Point(8, 144);
- this.groupBox2.Name = "groupBox2";
- this.groupBox2.Size = new System.Drawing.Size(152, 96);
- this.groupBox2.TabIndex = 33;
- this.groupBox2.TabStop = false;
- this.groupBox2.Text = "Work items";
- //
- // groupBox3
- //
- this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
- this.groupBox3.Controls.Add(this.lblThreadInUse);
- this.groupBox3.Controls.Add(this.label7);
- this.groupBox3.Controls.Add(this.lblThreadsInPool);
- this.groupBox3.Controls.Add(this.label2);
- this.groupBox3.Location = new System.Drawing.Point(176, 144);
- this.groupBox3.Name = "groupBox3";
- this.groupBox3.Size = new System.Drawing.Size(168, 72);
- this.groupBox3.TabIndex = 34;
- this.groupBox3.TabStop = false;
- this.groupBox3.Text = "Threads";
- //
- // groupBox1
- //
- this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)));
- this.groupBox1.Controls.Add(this.usageThreadsInPool);
- this.groupBox1.Location = new System.Drawing.Point(8, 8);
- this.groupBox1.Name = "groupBox1";
- this.groupBox1.Size = new System.Drawing.Size(80, 128);
- this.groupBox1.TabIndex = 35;
- this.groupBox1.TabStop = false;
- this.groupBox1.Text = "STP Usage";
- //
- // usageThreadsInPool
- //
- this.usageThreadsInPool.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.usageThreadsInPool.BackColor = System.Drawing.Color.Black;
- this.usageThreadsInPool.Location = new System.Drawing.Point(20, 16);
- this.usageThreadsInPool.Maximum = 25;
- this.usageThreadsInPool.Name = "usageThreadsInPool";
- this.usageThreadsInPool.Size = new System.Drawing.Size(41, 104);
- this.usageThreadsInPool.TabIndex = 37;
- this.usageThreadsInPool.Value1 = 1;
- this.usageThreadsInPool.Value2 = 24;
- //
- // groupBox4
- //
- this.groupBox4.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.groupBox4.Controls.Add(this.usageHistorySTP);
- this.groupBox4.Location = new System.Drawing.Point(104, 8);
- this.groupBox4.Name = "groupBox4";
- this.groupBox4.Size = new System.Drawing.Size(494, 128);
- this.groupBox4.TabIndex = 36;
- this.groupBox4.TabStop = false;
- this.groupBox4.Text = "STP Usage History";
- //
- // usageHistorySTP
- //
- this.usageHistorySTP.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
- | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.usageHistorySTP.BackColor = System.Drawing.Color.Black;
- this.usageHistorySTP.Location = new System.Drawing.Point(8, 16);
- this.usageHistorySTP.Maximum = 100;
- this.usageHistorySTP.Name = "usageHistorySTP";
- this.usageHistorySTP.Size = new System.Drawing.Size(480, 104);
- this.usageHistorySTP.TabIndex = 0;
- //
- // pcActiveThreads
- //
- this.pcActiveThreads.CategoryName = "SmartThreadPool";
- this.pcActiveThreads.CounterName = "Active threads";
- this.pcActiveThreads.InstanceName = "Test SmartThreadPool";
- //
- // pcInUseThreads
- //
- this.pcInUseThreads.CategoryName = "SmartThreadPool";
- this.pcInUseThreads.CounterName = "In use threads";
- this.pcInUseThreads.InstanceName = "Test SmartThreadPool";
- //
- // pcQueuedWorkItems
- //
- this.pcQueuedWorkItems.CategoryName = "SmartThreadPool";
- this.pcQueuedWorkItems.CounterName = "Work Items in queue";
- this.pcQueuedWorkItems.InstanceName = "Test SmartThreadPool";
- //
- // pcCompletedWorkItems
- //
- this.pcCompletedWorkItems.CategoryName = "SmartThreadPool";
- this.pcCompletedWorkItems.CounterName = "Work Items processed";
- this.pcCompletedWorkItems.InstanceName = "Test SmartThreadPool";
- //
- // Form1
- //
- this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
- this.ClientSize = new System.Drawing.Size(608, 382);
- this.Controls.Add(this.groupBox4);
- this.Controls.Add(this.groupBox1);
- this.Controls.Add(this.groupBox2);
- this.Controls.Add(this.groupBox3);
- this.Controls.Add(this.spinConsumingTime);
- this.Controls.Add(this.label6);
- this.Controls.Add(this.spinInterval);
- this.Controls.Add(this.spinIdleTimeout);
- this.Controls.Add(this.spinMaxThreads);
- this.Controls.Add(this.spinMinThreads);
- this.Controls.Add(this.label5);
- this.Controls.Add(this.label4);
- this.Controls.Add(this.label3);
- this.Controls.Add(this.label1);
- this.Controls.Add(this.btnStop);
- this.Controls.Add(this.btnStart);
- this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
- this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
- this.MinimumSize = new System.Drawing.Size(616, 416);
- this.Name = "Form1";
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
- this.Text = "Test Smart Thread Pool";
- this.Closing += new System.ComponentModel.CancelEventHandler(this.Form1_Closing);
- this.Load += new System.EventHandler(this.Form1_Load);
- ((System.ComponentModel.ISupportInitialize)(this.spinIdleTimeout)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinMaxThreads)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinMinThreads)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinInterval)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.spinConsumingTime)).EndInit();
- this.groupBox2.ResumeLayout(false);
- this.groupBox3.ResumeLayout(false);
- this.groupBox1.ResumeLayout(false);
- this.groupBox4.ResumeLayout(false);
- ((System.ComponentModel.ISupportInitialize)(this.pcActiveThreads)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.pcInUseThreads)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.pcQueuedWorkItems)).EndInit();
- ((System.ComponentModel.ISupportInitialize)(this.pcCompletedWorkItems)).EndInit();
- this.ResumeLayout(false);
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
+ this.btnStart = new System.Windows.Forms.Button();
+ this.btnStop = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.lblThreadsInPool = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.spinIdleTimeout = new System.Windows.Forms.NumericUpDown();
+ this.spinMaxThreads = new System.Windows.Forms.NumericUpDown();
+ this.spinMinThreads = new System.Windows.Forms.NumericUpDown();
+ this.spinInterval = new System.Windows.Forms.NumericUpDown();
+ this.lblThreadInUse = new System.Windows.Forms.Label();
+ this.label7 = new System.Windows.Forms.Label();
+ this.timerPoll = new System.Windows.Forms.Timer(this.components);
+ this.spinConsumingTime = new System.Windows.Forms.NumericUpDown();
+ this.label6 = new System.Windows.Forms.Label();
+ this.lblWaitingCallbacks = new System.Windows.Forms.Label();
+ this.label9 = new System.Windows.Forms.Label();
+ this.label8 = new System.Windows.Forms.Label();
+ this.label10 = new System.Windows.Forms.Label();
+ this.lblWorkItemsGenerated = new System.Windows.Forms.Label();
+ this.lblWorkItemsCompleted = new System.Windows.Forms.Label();
+ this.groupBox2 = new System.Windows.Forms.GroupBox();
+ this.groupBox3 = new System.Windows.Forms.GroupBox();
+ this.groupBox1 = new System.Windows.Forms.GroupBox();
+ this.usageThreadsInPool = new UsageControl.UsageControl();
+ this.groupBox4 = new System.Windows.Forms.GroupBox();
+ this.usageHistorySTP = new UsageControl.UsageHistoryControl();
+ this.pcActiveThreads = new System.Diagnostics.PerformanceCounter();
+ this.pcInUseThreads = new System.Diagnostics.PerformanceCounter();
+ this.pcQueuedWorkItems = new System.Diagnostics.PerformanceCounter();
+ this.pcCompletedWorkItems = new System.Diagnostics.PerformanceCounter();
+ ((System.ComponentModel.ISupportInitialize)(this.spinIdleTimeout)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinMaxThreads)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinMinThreads)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinInterval)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinConsumingTime)).BeginInit();
+ this.groupBox2.SuspendLayout();
+ this.groupBox3.SuspendLayout();
+ this.groupBox1.SuspendLayout();
+ this.groupBox4.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pcActiveThreads)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcInUseThreads)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcQueuedWorkItems)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcCompletedWorkItems)).BeginInit();
+ this.SuspendLayout();
+ //
+ // btnStart
+ //
+ this.btnStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnStart.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnStart.Location = new System.Drawing.Point(432, 352);
+ this.btnStart.Name = "btnStart";
+ this.btnStart.Size = new System.Drawing.Size(72, 24);
+ this.btnStart.TabIndex = 0;
+ this.btnStart.Text = "Start";
+ this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
+ //
+ // btnStop
+ //
+ this.btnStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnStop.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnStop.Location = new System.Drawing.Point(520, 352);
+ this.btnStop.Name = "btnStop";
+ this.btnStop.Size = new System.Drawing.Size(72, 24);
+ this.btnStop.TabIndex = 1;
+ this.btnStop.Text = "Stop";
+ this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label1.Location = new System.Drawing.Point(104, 256);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(104, 24);
+ this.label1.TabIndex = 2;
+ this.label1.Text = "Minimum Threads";
+ this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label3
+ //
+ this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label3.Location = new System.Drawing.Point(104, 288);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(104, 24);
+ this.label3.TabIndex = 4;
+ this.label3.Text = "Maximum Threads";
+ this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label4
+ //
+ this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label4.Location = new System.Drawing.Point(104, 320);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(120, 24);
+ this.label4.TabIndex = 5;
+ this.label4.Text = "Idle timeout (Seconds)";
+ this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ this.label4.Click += new System.EventHandler(this.label4_Click);
+ //
+ // label2
+ //
+ this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label2.Location = new System.Drawing.Point(8, 16);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(72, 24);
+ this.label2.TabIndex = 3;
+ this.label2.Text = "In pool (Red)";
+ this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // lblThreadsInPool
+ //
+ this.lblThreadsInPool.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblThreadsInPool.Location = new System.Drawing.Point(80, 16);
+ this.lblThreadsInPool.Name = "lblThreadsInPool";
+ this.lblThreadsInPool.Size = new System.Drawing.Size(80, 24);
+ this.lblThreadsInPool.TabIndex = 11;
+ this.lblThreadsInPool.Text = "XXXXXXXXX";
+ this.lblThreadsInPool.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // label5
+ //
+ this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label5.Location = new System.Drawing.Point(336, 258);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(280, 24);
+ this.label5.TabIndex = 12;
+ this.label5.Text = "Interval between work item production (milliseconds)";
+ this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // spinIdleTimeout
+ //
+ this.spinIdleTimeout.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.spinIdleTimeout.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.spinIdleTimeout.Location = new System.Drawing.Point(8, 320);
+ this.spinIdleTimeout.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinIdleTimeout.Name = "spinIdleTimeout";
+ this.spinIdleTimeout.Size = new System.Drawing.Size(88, 29);
+ this.spinIdleTimeout.TabIndex = 15;
+ this.spinIdleTimeout.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ this.spinIdleTimeout.Value = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ //
+ // spinMaxThreads
+ //
+ this.spinMaxThreads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.spinMaxThreads.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.spinMaxThreads.Location = new System.Drawing.Point(8, 288);
+ this.spinMaxThreads.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinMaxThreads.Name = "spinMaxThreads";
+ this.spinMaxThreads.Size = new System.Drawing.Size(88, 29);
+ this.spinMaxThreads.TabIndex = 14;
+ this.spinMaxThreads.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ this.spinMaxThreads.Value = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ this.spinMaxThreads.ValueChanged += new System.EventHandler(this.spinMaxThreads_ValueChanged);
+ //
+ // spinMinThreads
+ //
+ this.spinMinThreads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.spinMinThreads.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.spinMinThreads.Location = new System.Drawing.Point(8, 256);
+ this.spinMinThreads.Name = "spinMinThreads";
+ this.spinMinThreads.Size = new System.Drawing.Size(88, 29);
+ this.spinMinThreads.TabIndex = 13;
+ this.spinMinThreads.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ this.spinMinThreads.ValueChanged += new System.EventHandler(this.spinMinThreads_ValueChanged);
+ //
+ // spinInterval
+ //
+ this.spinInterval.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.spinInterval.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.spinInterval.Increment = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ this.spinInterval.Location = new System.Drawing.Point(240, 256);
+ this.spinInterval.Maximum = new decimal(new int[] {
+ 100000,
+ 0,
+ 0,
+ 0});
+ this.spinInterval.Name = "spinInterval";
+ this.spinInterval.Size = new System.Drawing.Size(88, 29);
+ this.spinInterval.TabIndex = 16;
+ this.spinInterval.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ this.spinInterval.Value = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ //
+ // lblThreadInUse
+ //
+ this.lblThreadInUse.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblThreadInUse.Location = new System.Drawing.Point(80, 40);
+ this.lblThreadInUse.Name = "lblThreadInUse";
+ this.lblThreadInUse.Size = new System.Drawing.Size(80, 24);
+ this.lblThreadInUse.TabIndex = 18;
+ this.lblThreadInUse.Text = "XXXXXXXXX";
+ this.lblThreadInUse.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // label7
+ //
+ this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label7.Location = new System.Drawing.Point(8, 40);
+ this.label7.Name = "label7";
+ this.label7.Size = new System.Drawing.Size(80, 24);
+ this.label7.TabIndex = 17;
+ this.label7.Text = "Used (Green)";
+ this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // timerPoll
+ //
+ this.timerPoll.Interval = 500;
+ this.timerPoll.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // spinConsumingTime
+ //
+ this.spinConsumingTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.spinConsumingTime.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.spinConsumingTime.Increment = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ this.spinConsumingTime.Location = new System.Drawing.Point(240, 288);
+ this.spinConsumingTime.Maximum = new decimal(new int[] {
+ 100000,
+ 0,
+ 0,
+ 0});
+ this.spinConsumingTime.Name = "spinConsumingTime";
+ this.spinConsumingTime.Size = new System.Drawing.Size(88, 29);
+ this.spinConsumingTime.TabIndex = 20;
+ this.spinConsumingTime.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
+ this.spinConsumingTime.Value = new decimal(new int[] {
+ 100,
+ 0,
+ 0,
+ 0});
+ //
+ // label6
+ //
+ this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label6.Location = new System.Drawing.Point(336, 290);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(216, 24);
+ this.label6.TabIndex = 19;
+ this.label6.Text = "Work item consuming time (milliseconds)";
+ this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // lblWaitingCallbacks
+ //
+ this.lblWaitingCallbacks.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblWaitingCallbacks.Location = new System.Drawing.Point(64, 16);
+ this.lblWaitingCallbacks.Name = "lblWaitingCallbacks";
+ this.lblWaitingCallbacks.Size = new System.Drawing.Size(80, 24);
+ this.lblWaitingCallbacks.TabIndex = 22;
+ this.lblWaitingCallbacks.Text = "XXXXXXXXX";
+ this.lblWaitingCallbacks.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // label9
+ //
+ this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label9.Location = new System.Drawing.Point(8, 16);
+ this.label9.Name = "label9";
+ this.label9.Size = new System.Drawing.Size(48, 24);
+ this.label9.TabIndex = 21;
+ this.label9.Text = "Queued";
+ this.label9.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label8
+ //
+ this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label8.Location = new System.Drawing.Point(8, 40);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(64, 24);
+ this.label8.TabIndex = 25;
+ this.label8.Text = "Generated";
+ this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label10
+ //
+ this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label10.Location = new System.Drawing.Point(8, 64);
+ this.label10.Name = "label10";
+ this.label10.Size = new System.Drawing.Size(64, 24);
+ this.label10.TabIndex = 26;
+ this.label10.Text = "Completed";
+ this.label10.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // lblWorkItemsGenerated
+ //
+ this.lblWorkItemsGenerated.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblWorkItemsGenerated.Location = new System.Drawing.Point(64, 40);
+ this.lblWorkItemsGenerated.Name = "lblWorkItemsGenerated";
+ this.lblWorkItemsGenerated.Size = new System.Drawing.Size(80, 24);
+ this.lblWorkItemsGenerated.TabIndex = 27;
+ this.lblWorkItemsGenerated.Text = "XXXXXXXXX";
+ this.lblWorkItemsGenerated.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // lblWorkItemsCompleted
+ //
+ this.lblWorkItemsCompleted.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblWorkItemsCompleted.Location = new System.Drawing.Point(64, 64);
+ this.lblWorkItemsCompleted.Name = "lblWorkItemsCompleted";
+ this.lblWorkItemsCompleted.Size = new System.Drawing.Size(80, 24);
+ this.lblWorkItemsCompleted.TabIndex = 28;
+ this.lblWorkItemsCompleted.Text = "XXXXXXXXX";
+ this.lblWorkItemsCompleted.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ //
+ // groupBox2
+ //
+ this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.groupBox2.Controls.Add(this.lblWaitingCallbacks);
+ this.groupBox2.Controls.Add(this.label9);
+ this.groupBox2.Controls.Add(this.label8);
+ this.groupBox2.Controls.Add(this.label10);
+ this.groupBox2.Controls.Add(this.lblWorkItemsGenerated);
+ this.groupBox2.Controls.Add(this.lblWorkItemsCompleted);
+ this.groupBox2.Location = new System.Drawing.Point(8, 144);
+ this.groupBox2.Name = "groupBox2";
+ this.groupBox2.Size = new System.Drawing.Size(152, 96);
+ this.groupBox2.TabIndex = 33;
+ this.groupBox2.TabStop = false;
+ this.groupBox2.Text = "Work items";
+ //
+ // groupBox3
+ //
+ this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.groupBox3.Controls.Add(this.lblThreadInUse);
+ this.groupBox3.Controls.Add(this.label7);
+ this.groupBox3.Controls.Add(this.lblThreadsInPool);
+ this.groupBox3.Controls.Add(this.label2);
+ this.groupBox3.Location = new System.Drawing.Point(176, 144);
+ this.groupBox3.Name = "groupBox3";
+ this.groupBox3.Size = new System.Drawing.Size(168, 72);
+ this.groupBox3.TabIndex = 34;
+ this.groupBox3.TabStop = false;
+ this.groupBox3.Text = "Threads";
+ //
+ // groupBox1
+ //
+ this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.groupBox1.Controls.Add(this.usageThreadsInPool);
+ this.groupBox1.Location = new System.Drawing.Point(8, 8);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new System.Drawing.Size(80, 128);
+ this.groupBox1.TabIndex = 35;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "STP Usage";
+ //
+ // usageThreadsInPool
+ //
+ this.usageThreadsInPool.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.usageThreadsInPool.BackColor = System.Drawing.Color.Black;
+ this.usageThreadsInPool.Location = new System.Drawing.Point(20, 16);
+ this.usageThreadsInPool.Maximum = 25;
+ this.usageThreadsInPool.Name = "usageThreadsInPool";
+ this.usageThreadsInPool.Size = new System.Drawing.Size(41, 104);
+ this.usageThreadsInPool.TabIndex = 37;
+ this.usageThreadsInPool.Value1 = 1;
+ this.usageThreadsInPool.Value2 = 24;
+ //
+ // groupBox4
+ //
+ this.groupBox4.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupBox4.Controls.Add(this.usageHistorySTP);
+ this.groupBox4.Location = new System.Drawing.Point(104, 8);
+ this.groupBox4.Name = "groupBox4";
+ this.groupBox4.Size = new System.Drawing.Size(494, 128);
+ this.groupBox4.TabIndex = 36;
+ this.groupBox4.TabStop = false;
+ this.groupBox4.Text = "STP Usage History";
+ //
+ // usageHistorySTP
+ //
+ this.usageHistorySTP.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.usageHistorySTP.BackColor = System.Drawing.Color.Black;
+ this.usageHistorySTP.Location = new System.Drawing.Point(8, 16);
+ this.usageHistorySTP.Maximum = 100;
+ this.usageHistorySTP.Name = "usageHistorySTP";
+ this.usageHistorySTP.Size = new System.Drawing.Size(480, 104);
+ this.usageHistorySTP.TabIndex = 0;
+ //
+ // pcActiveThreads
+ //
+ this.pcActiveThreads.CategoryName = "SmartThreadPool";
+ this.pcActiveThreads.CounterName = "Active threads";
+ this.pcActiveThreads.InstanceName = "Test SmartThreadPool";
+ //
+ // pcInUseThreads
+ //
+ this.pcInUseThreads.CategoryName = "SmartThreadPool";
+ this.pcInUseThreads.CounterName = "In use threads";
+ this.pcInUseThreads.InstanceName = "Test SmartThreadPool";
+ //
+ // pcQueuedWorkItems
+ //
+ this.pcQueuedWorkItems.CategoryName = "SmartThreadPool";
+ this.pcQueuedWorkItems.CounterName = "Work Items in queue";
+ this.pcQueuedWorkItems.InstanceName = "Test SmartThreadPool";
+ //
+ // pcCompletedWorkItems
+ //
+ this.pcCompletedWorkItems.CategoryName = "SmartThreadPool";
+ this.pcCompletedWorkItems.CounterName = "Work Items processed";
+ this.pcCompletedWorkItems.InstanceName = "Test SmartThreadPool";
+ //
+ // Form1
+ //
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.ClientSize = new System.Drawing.Size(608, 382);
+ this.Controls.Add(this.groupBox4);
+ this.Controls.Add(this.groupBox1);
+ this.Controls.Add(this.groupBox2);
+ this.Controls.Add(this.groupBox3);
+ this.Controls.Add(this.spinConsumingTime);
+ this.Controls.Add(this.label6);
+ this.Controls.Add(this.spinInterval);
+ this.Controls.Add(this.spinIdleTimeout);
+ this.Controls.Add(this.spinMaxThreads);
+ this.Controls.Add(this.spinMinThreads);
+ this.Controls.Add(this.label5);
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.btnStop);
+ this.Controls.Add(this.btnStart);
+ this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MinimumSize = new System.Drawing.Size(616, 416);
+ this.Name = "Form1";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Test Smart Thread Pool";
+ this.Closing += new System.ComponentModel.CancelEventHandler(this.Form1_Closing);
+ this.Load += new System.EventHandler(this.Form1_Load);
+ ((System.ComponentModel.ISupportInitialize)(this.spinIdleTimeout)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinMaxThreads)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinMinThreads)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinInterval)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinConsumingTime)).EndInit();
+ this.groupBox2.ResumeLayout(false);
+ this.groupBox3.ResumeLayout(false);
+ this.groupBox1.ResumeLayout(false);
+ this.groupBox4.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.pcActiveThreads)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcInUseThreads)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcQueuedWorkItems)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcCompletedWorkItems)).EndInit();
+ this.ResumeLayout(false);
}
#endregion
@@ -579,6 +580,7 @@ namespace TestSmartThreadPool
{
return;
}
+ Application.EnableVisualStyles();
Application.Run(new Form1());
}
@@ -586,7 +588,7 @@ namespace TestSmartThreadPool
// This method is a work around for the Peformance Counter issue.
// When the first SmartThreadPool is created with a Peformance
// Counter name on a machine, it creates the SmartThreadPool
- // Peformance Counter category. In this demo I use thes Performance
+ // Peformance Counter category. In this demo I am using the Performance
// Counters to update the GUI.
// The issue is that if this demo runs for the first time on the
// machine, it creates the Peformance Counter category and then
@@ -666,7 +668,7 @@ namespace TestSmartThreadPool
running = false;
workItemsProducerThread.Join();
- _smartThreadPool.Shutdown();
+ _smartThreadPool.Shutdown(true, 1000);
_smartThreadPool.Dispose();
_smartThreadPool = null;
GC.Collect();
@@ -682,8 +684,8 @@ namespace TestSmartThreadPool
private void UpdateControls(bool start)
{
running = start;
- spinMinThreads.Enabled = !start;
- spinMaxThreads.Enabled = !start;
+ //spinMinThreads.Enabled = !start;
+ //spinMaxThreads.Enabled = !start;
spinIdleTimeout.Enabled = !start;
btnStart.Enabled = !start;
@@ -708,6 +710,11 @@ namespace TestSmartThreadPool
{
spinMaxThreads.Value = spinMinThreads.Value;
}
+
+ if (null != _smartThreadPool)
+ {
+ _smartThreadPool.MinThreads = (int)spinMinThreads.Value;
+ }
}
private void spinMaxThreads_ValueChanged(object sender, System.EventArgs e)
@@ -717,7 +724,11 @@ namespace TestSmartThreadPool
spinMinThreads.Value = spinMaxThreads.Value;
}
usageThreadsInPool.Maximum = Convert.ToInt32(spinMaxThreads.Value);
- }
+ if (null != _smartThreadPool)
+ {
+ _smartThreadPool.MaxThreads = (int)spinMaxThreads.Value;
+ }
+ }
private void timer1_Tick(object sender, System.EventArgs e)
{
@@ -777,10 +788,15 @@ namespace TestSmartThreadPool
{
if (null != _smartThreadPool)
{
- _smartThreadPool.Shutdown();
+ _smartThreadPool.Shutdown(true, 1000);
_smartThreadPool = null;
_workItemsGroup = null;
}
}
+
+ private void label4_Click(object sender, EventArgs e)
+ {
+
+ }
}
}
diff --git a/TestSmartThreadPool/Form1.resx b/TestSmartThreadPool/Form1.resx
index 30f3ad2..5cfd218 100644
--- a/TestSmartThreadPool/Form1.resx
+++ b/TestSmartThreadPool/Form1.resx
@@ -3,7 +3,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
@@ -89,378 +109,34 @@
text/microsoft-resx
- 1.3
+ 2.0
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- Private
-
-
+
17, 17
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- Private
-
-
- 8, 8
-
-
- True
-
-
- False
-
-
- True
-
-
- Private
-
-
- Private
-
-
- 8, 8
-
-
- True
-
-
- False
-
-
- True
-
-
- Private
-
-
- Private
-
-
- 8, 8
-
-
- True
-
-
- False
-
-
- True
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- Private
-
-
- 8, 8
-
-
- True
-
-
- False
-
-
- True
-
-
- Private
-
-
- False
-
-
- Private
-
-
- Private
-
-
- Private
-
-
- Private
-
-
+
+
112, 17
-
-
- Private
-
-
- Private
-
-
+
+
245, 17
-
-
- Private
-
-
- Private
-
-
+
+
377, 17
-
-
- Private
-
-
- Private
-
-
+
+
533, 17
-
-
- False
-
-
- (Default)
-
-
- False
-
-
- False
-
-
- 8, 8
-
-
- True
-
-
- 59
-
-
- True
-
-
- Form1
-
-
- Private
-
-
+
+
+ 87
+
+
+
AAABAAIAICAQAAAAAADoAgAAJgAAABAQEAAAAAAAKAEAAA4DAAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC
AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/
diff --git a/TestSmartThreadPool/TestSmartThreadPool.csproj b/TestSmartThreadPool/TestSmartThreadPool.csproj
index fc57450..d771412 100644
--- a/TestSmartThreadPool/TestSmartThreadPool.csproj
+++ b/TestSmartThreadPool/TestSmartThreadPool.csproj
@@ -71,6 +71,16 @@
none
prompt
+
+ bin\ReleaseCE\
+ TRACE
+ 285212672
+ true
+
+
+ AnyCPU
+ prompt
+
System
diff --git a/UsageControl/Properties/Resources.Designer.cs b/UsageControl/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..79d225b
--- /dev/null
+++ b/UsageControl/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.42
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace UsageControl.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UsageControl.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/UsageControl/Properties/Resources.resx b/UsageControl/Properties/Resources.resx
new file mode 100644
index 0000000..7080a7d
--- /dev/null
+++ b/UsageControl/Properties/Resources.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/UsageControl/QueueUsageControl.Designer.cs b/UsageControl/QueueUsageControl.Designer.cs
new file mode 100644
index 0000000..2ca7559
--- /dev/null
+++ b/UsageControl/QueueUsageControl.Designer.cs
@@ -0,0 +1,59 @@
+namespace UsageControl
+{
+ partial class QueueUsageControl
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.SuspendLayout();
+ //
+ // timer1
+ //
+ this.timer1.Enabled = true;
+ this.timer1.Interval = 500;
+ this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // QueueUsageControl
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.Transparent;
+ this.Name = "QueueUsageControl";
+ this.Size = new System.Drawing.Size(167, 247);
+ this.Resize += new System.EventHandler(this.QueueUsageControl_Resize);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Timer timer1;
+
+
+ }
+}
diff --git a/UsageControl/QueueUsageControl.cs b/UsageControl/QueueUsageControl.cs
new file mode 100644
index 0000000..c17ddc3
--- /dev/null
+++ b/UsageControl/QueueUsageControl.cs
@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Text;
+using System.Windows.Forms;
+using System.Drawing.Drawing2D;
+using System.Diagnostics;
+using System.Reflection;
+using System.Drawing.Imaging;
+
+
+namespace UsageControl
+{
+ public partial class QueueUsageControl : UserControl
+ {
+ private List _queuedItems;
+ private int _maxItemsVisible = 0;
+ private int _lastVisibleItemIndex = 0;
+ private int _itemHeight = 0;
+ private Dictionary _imagesCache = new Dictionary();
+ private GraphicsPath _pathBorder;
+ private RectangleF [] _slots;
+ private bool _invalidate = true;
+
+ public QueueUsageControl()
+ {
+ //Debug.WriteLine("QueueUsageControl");
+
+ EnableDoubleBuffering();
+
+ Reset();
+
+ InitializeComponent();
+ ControlRezised();
+ }
+
+ public static Bitmap GenerateItemImage(string text, Color color, int width, int height, Font font)
+ {
+ //Debug.WriteLine("GenerateItemImage");
+
+ Bitmap itemImage = null;
+ Rectangle rc = new Rectangle(0, 0, width, height);
+ itemImage = new Bitmap(rc.Width, rc.Height);
+
+ /// Create button
+ rc.Inflate(-3, -3);
+ GraphicsPath path1 = GetPath(rc, 10);
+ rc.Inflate(0, 6);
+ LinearGradientBrush br1 = new LinearGradientBrush(rc, color, Color.White, LinearGradientMode.Vertical);
+ rc.Inflate(0, -6);
+
+ /// Create shadow
+ Rectangle rc2 = rc;
+ rc2.Offset(8, 8);
+ GraphicsPath path2 = GetPath(rc2, 20);
+ PathGradientBrush br2 = new PathGradientBrush(path2);
+ br2.CenterColor = ControlPaint.DarkDark(Color.Silver);
+ br2.SurroundColors = new Color[] { Color.White };
+
+ /// Create bubble
+ Rectangle rc3 = rc;
+ rc3.Inflate(-15, -rc.Height / 3);
+ rc3.Y = rc3.Y - 2;
+ //rc3.Height = rc3.Height;
+ GraphicsPath path3 = GetPath(rc3, rc3.Height);
+ LinearGradientBrush br3 = new LinearGradientBrush(rc3, Color.FromArgb(255, Color.White), Color.FromArgb(0, Color.White), LinearGradientMode.Vertical);
+
+ itemImage = new Bitmap(width - 2, height);
+ Graphics g = Graphics.FromImage(itemImage);
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.FillPath(br2, path2);
+ g.FillPath(br1, path1);
+ g.FillPath(br3, path3);
+
+ //SizeF size = g.MeasureString(text, font);
+ //int fontHeight = (int)size.Height + 5;
+
+ //g.DrawString(
+ // text,
+ // font,
+ // Brushes.Black,
+ // new RectangleF((rc.Width - size.Width) / 2, 2, width, fontHeight));
+
+ return itemImage;
+ }
+
+ private Bitmap GetItemsImage(Color color)
+ {
+ //Debug.WriteLine("GetItemsImage");
+
+ Bitmap itemImage = null;
+
+ if (!_imagesCache.ContainsKey(color))
+ {
+ Rectangle rc = new Rectangle(0, 0, this.Width, _itemHeight);
+ itemImage = new Bitmap(rc.Width, rc.Height);
+
+ /// Create button
+ rc.Inflate(-3, -3);
+ GraphicsPath path1 = GetPath(rc, 10);
+ rc.Inflate(0, 6);
+ LinearGradientBrush br1 = new LinearGradientBrush(rc, color, Color.White, LinearGradientMode.Vertical);
+ rc.Inflate(0, -6);
+
+ /// Create shadow
+ Rectangle rc2 = rc;
+ rc2.Offset(8, 8);
+ GraphicsPath path2 = GetPath(rc2, 20);
+ PathGradientBrush br2 = new PathGradientBrush(path2);
+ br2.CenterColor = ControlPaint.DarkDark(Color.Silver);
+ br2.SurroundColors = new Color[] { Color.White };
+
+ /// Create bubble
+ Rectangle rc3 = rc;
+ rc3.Inflate(-15, -rc.Height / 3);
+ rc3.Y = rc3.Y - 2;
+ //rc3.Height = rc3.Height;
+ GraphicsPath path3 = GetPath(rc3, rc3.Height);
+ LinearGradientBrush br3 = new LinearGradientBrush(rc3, Color.FromArgb(255, Color.White), Color.FromArgb(0, Color.White), LinearGradientMode.Vertical);
+
+ itemImage = new Bitmap(this.Width - 2, _itemHeight);
+ Graphics g = Graphics.FromImage(itemImage);
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.FillPath(br2, path2);
+ g.FillPath(br1, path1);
+ g.FillPath(br3, path3);
+
+ _imagesCache[color] = itemImage;
+ }
+
+ itemImage = _imagesCache[color];
+
+ return itemImage;
+ }
+
+ private void EnableDoubleBuffering()
+ {
+ //Debug.WriteLine("EnableDoubleBuffering");
+
+ // Set the value of the double-buffering style bits to true.
+ this.SetStyle(ControlStyles.DoubleBuffer |
+ ControlStyles.UserPaint |
+ ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.SupportsTransparentBackColor,
+ true);
+ this.UpdateStyles();
+ }
+
+ public void Reset()
+ {
+ //Debug.WriteLine("Reset");
+ _queuedItems = new List();
+ }
+
+ //public void Queue(string text, Color color, bool blink)
+ //{
+ // //Debug.WriteLine("Queue");
+ // _queuedItems.Add(new QueueUsageEntry(text, color, blink));
+ // //this.Invalidate();
+ //}
+
+ public void SetQueue(List list)
+ {
+ //Debug.WriteLine("SetQueue");
+ bool invalidate = false;
+
+ if (_queuedItems.Count != list.Count)
+ {
+ invalidate = true;
+ }
+ else
+ {
+ int counter = Math.Min(list.Count, _maxItemsVisible);
+ for (int i = 0; i < counter; i++)
+ {
+ if (_queuedItems[i] != list[i])
+ {
+ invalidate = true;
+ break;
+ }
+ }
+ }
+
+ if (invalidate)
+ {
+ //Debug.WriteLine("_invalidate = true");
+ _queuedItems = list;
+ _invalidate = invalidate;
+ }
+
+ //_queuedItems = list;
+ //_queuedItems.Clear();
+ //foreach (QueueUsageEntry entry in list)
+ //{
+ // _queuedItems.Add(entry);
+ //}
+ //_invalidate = true;
+ //this.Invalidate();
+ }
+
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ //Debug.WriteLine("OnPaint");
+
+ Bitmap bitmap = new Bitmap(Width, Height);
+ Graphics g = Graphics.FromImage(bitmap);
+
+ g.FillPath(Brushes.White, _pathBorder);
+
+ int counter = Math.Min(_maxItemsVisible, _queuedItems.Count);
+
+ int i = 0;
+ foreach (QueueUsageEntry entry in _queuedItems)
+ {
+ if (i > counter)
+ {
+ break;
+ }
+
+ //int top = 0;
+ //int bottom = 0;
+ //if (topdown)
+ //{
+ // bottom = 1;
+ // top = bottom;
+ // top += i * _itemHeight;
+ // bottom += (i + 1) * _itemHeight;
+ //}
+ //else
+ //{
+ // bottom = this.Height - 1;
+ // top = bottom;
+ // bottom -= i * _itemHeight;
+ // top -= (i + 1) * _itemHeight;
+ //}
+
+ //g.DrawLine(Pens.Black, 0, top, this.Width, top);
+
+
+ string text = (entry.IsExecuting ? ">" : "") + entry.Text;
+ if (i == _lastVisibleItemIndex)
+ {
+ text = "(" + (_queuedItems.Count - _maxItemsVisible) + ")...";
+ }
+
+ SizeF size = g.MeasureString(entry.Text, this.Font);
+ //Debug.WriteLine(size.Width);
+
+ Bitmap itemImage = GetItemsImage(entry.Color);
+
+ RectangleF slot = _slots[i];
+
+ //g.DrawImage(itemImage, -1, top);
+ g.DrawImage(itemImage, -1, slot.Top-2);
+
+ //g.DrawString(text, this.Font, Brushes.Black, new RectangleF((this.Width - size.Width) / 2, top + 2, this.Width, bottom));
+ g.DrawString(text, this.Font, Brushes.Black, slot);
+ ++i;
+ }
+
+ g.DrawPath(Pens.Black, _pathBorder);
+ //g.DrawRectangle(Pens.Black, border);
+
+ e.Graphics.DrawImage(bitmap, 0, 0);
+ }
+
+ private static GraphicsPath GetPath(Rectangle rc, int r)
+ {
+ //Debug.WriteLine("GetPath");
+ int x = rc.X, y = rc.Y, w = rc.Width, h = rc.Height;
+ GraphicsPath path = new GraphicsPath();
+ path.AddArc(x, y, r, r, 180, 90); //Upper left corner
+ path.AddArc(x + w - r, y, r, r, 270, 90); //Upper right corner
+ path.AddArc(x + w - r, y + h - r, r, r, 0, 90); //Lower right corner
+ path.AddArc(x, y + h - r, r, r, 90, 90); //Lower left corner
+ path.CloseFigure();
+ return path;
+ }
+
+ private void ControlRezised()
+ {
+ //Debug.WriteLine("ControlRezised");
+ Graphics g = Graphics.FromHwnd(this.Handle);
+ SizeF size = g.MeasureString("X", this.Font);
+ g.Dispose();
+ _itemHeight = (int)size.Height + 5;
+ _maxItemsVisible = this.Height / _itemHeight - 1;
+ _lastVisibleItemIndex = _maxItemsVisible;
+ _imagesCache = new Dictionary();
+
+ Rectangle border = new Rectangle(0, 0, this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1);
+ _pathBorder = GetPath(border, 20);
+
+ PrepareSlots();
+
+ }
+
+ private void PrepareSlots()
+ {
+ bool topdown = true;
+ _slots = new RectangleF[_maxItemsVisible+5];
+ for (int i = 0; i < _slots.Length; i++)
+ {
+ int top = 0;
+ int bottom = 0;
+ if (topdown)
+ {
+ bottom = 1;
+ top = bottom;
+ top += i * _itemHeight;
+ bottom += (i + 1) * _itemHeight;
+ }
+ else
+ {
+ bottom = this.Height - 1;
+ top = bottom;
+ bottom -= i * _itemHeight;
+ top -= (i + 1) * _itemHeight;
+ }
+
+ _slots[i] = new RectangleF((this.Width - 55) / 2, top + 2, this.Width, bottom);
+ }
+ }
+
+ private void QueueUsageControl_Resize(object sender, EventArgs e)
+ {
+ //Debug.WriteLine("QueueUsageControl_Resize");
+ ControlRezised();
+ }
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ if (_invalidate)
+ {
+ _invalidate = false;
+ }
+ Invalidate();
+ }
+ }
+}
diff --git a/UsageControl/QueueUsageControl.resx b/UsageControl/QueueUsageControl.resx
new file mode 100644
index 0000000..fce63f7
--- /dev/null
+++ b/UsageControl/QueueUsageControl.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/UsageControl/QueueUsageEntry.cs b/UsageControl/QueueUsageEntry.cs
new file mode 100644
index 0000000..8ed1b51
--- /dev/null
+++ b/UsageControl/QueueUsageEntry.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Drawing;
+
+namespace UsageControl
+{
+ public partial class QueueUsageControl
+ {
+ public class QueueUsageEntry
+ {
+ private string _text;
+ private Color _color;
+ private bool _isExecuting;
+
+ public QueueUsageEntry(
+ string text,
+ Color color) : this (text, color, false)
+ {
+ }
+
+ public QueueUsageEntry(
+ string text,
+ Color color,
+ bool blink)
+ {
+ _text = text;
+ _color = color;
+ _isExecuting = blink;
+ }
+
+ public Color Color
+ {
+ get { return _color; }
+ }
+
+ public string Text
+ {
+ get { return _text; }
+ }
+
+ public bool IsExecuting
+ {
+ get { return _isExecuting; }
+ set { _isExecuting = value; }
+ }
+ }
+ }
+}
diff --git a/UsageControl/UsageControl.csproj b/UsageControl/UsageControl.csproj
index 404f499..2f5558e 100644
--- a/UsageControl/UsageControl.csproj
+++ b/UsageControl/UsageControl.csproj
@@ -73,6 +73,16 @@
none
prompt
+
+ bin\ReleaseCE\
+ TRACE
+ 285212672
+ true
+
+
+ AnyCPU
+ prompt
+
System
@@ -94,12 +104,35 @@
Code
+
+ True
+ True
+ Resources.resx
+
+
+ UserControl
+
+
+ QueueUsageControl.cs
+
+
+ UserControl
+
UserControl
UserControl
+
+ Designer
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ Designer
+ QueueUsageControl.cs
+
UsageControl.cs
Designer
diff --git a/WorkItemsGroupDemo/Form1.Designer.cs b/WorkItemsGroupDemo/Form1.Designer.cs
new file mode 100644
index 0000000..df40449
--- /dev/null
+++ b/WorkItemsGroupDemo/Form1.Designer.cs
@@ -0,0 +1,1269 @@
+namespace WorkItemsGroupDemo
+{
+ partial class Form1
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.btnStart = new System.Windows.Forms.Button();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.btnPause = new System.Windows.Forms.Button();
+ this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
+ this.btnCancel1 = new System.Windows.Forms.Button();
+ this.btnCancel2 = new System.Windows.Forms.Button();
+ this.btnCancel3 = new System.Windows.Forms.Button();
+ this.spinCon6 = new System.Windows.Forms.NumericUpDown();
+ this.spinCon1 = new System.Windows.Forms.NumericUpDown();
+ this.spinCon3 = new System.Windows.Forms.NumericUpDown();
+ this.spinCon2 = new System.Windows.Forms.NumericUpDown();
+ this.spinProduction6 = new System.Windows.Forms.NumericUpDown();
+ this.spinDuration6 = new System.Windows.Forms.NumericUpDown();
+ this.btnCancel6 = new System.Windows.Forms.Button();
+ this.spinDuration1 = new System.Windows.Forms.NumericUpDown();
+ this.spinProduction1 = new System.Windows.Forms.NumericUpDown();
+ this.spinDuration2 = new System.Windows.Forms.NumericUpDown();
+ this.spinProduction2 = new System.Windows.Forms.NumericUpDown();
+ this.spinDuration3 = new System.Windows.Forms.NumericUpDown();
+ this.spinProduction3 = new System.Windows.Forms.NumericUpDown();
+ this.spinIdleTimeout = new System.Windows.Forms.NumericUpDown();
+ this.lblStatus1 = new System.Windows.Forms.Label();
+ this.lblStatus2 = new System.Windows.Forms.Label();
+ this.lblStatus3 = new System.Windows.Forms.Label();
+ this.lblStatus6 = new System.Windows.Forms.Label();
+ this.lblWaitingCallbacks = new System.Windows.Forms.Label();
+ this.lblWorkItemsGenerated = new System.Windows.Forms.Label();
+ this.lblWorkItemsCompleted = new System.Windows.Forms.Label();
+ this.lblThreadInUse = new System.Windows.Forms.Label();
+ this.lblThreadsInPool = new System.Windows.Forms.Label();
+ this.comboWIPriority6 = new System.Windows.Forms.ComboBox();
+ this.comboWIPriority1 = new System.Windows.Forms.ComboBox();
+ this.comboWIPriority2 = new System.Windows.Forms.ComboBox();
+ this.comboWIPriority3 = new System.Windows.Forms.ComboBox();
+ this.btnMode = new System.Windows.Forms.Button();
+ this.groupBox4 = new System.Windows.Forms.GroupBox();
+ this.groupBox1 = new System.Windows.Forms.GroupBox();
+ this.groupBox2 = new System.Windows.Forms.GroupBox();
+ this.label6 = new System.Windows.Forms.Label();
+ this.label7 = new System.Windows.Forms.Label();
+ this.label11 = new System.Windows.Forms.Label();
+ this.groupBox3 = new System.Windows.Forms.GroupBox();
+ this.label12 = new System.Windows.Forms.Label();
+ this.label15 = new System.Windows.Forms.Label();
+ this.groupWIGQueues = new System.Windows.Forms.GroupBox();
+ this.label13 = new System.Windows.Forms.Label();
+ this.label16 = new System.Windows.Forms.Label();
+ this.label17 = new System.Windows.Forms.Label();
+ this.groupBox7 = new System.Windows.Forms.GroupBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.label8 = new System.Windows.Forms.Label();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.label9 = new System.Windows.Forms.Label();
+ this.label10 = new System.Windows.Forms.Label();
+ this.label14 = new System.Windows.Forms.Label();
+ this.label18 = new System.Windows.Forms.Label();
+ this.label19 = new System.Windows.Forms.Label();
+ this.timer2 = new System.Windows.Forms.Timer(this.components);
+ this.panelWIGsCtrls = new System.Windows.Forms.Panel();
+ this.pcActiveThreads = new System.Diagnostics.PerformanceCounter();
+ this.pcInUseThreads = new System.Diagnostics.PerformanceCounter();
+ this.pcQueuedWorkItems = new System.Diagnostics.PerformanceCounter();
+ this.pcCompletedWorkItems = new System.Diagnostics.PerformanceCounter();
+ this.timerPoll = new System.Windows.Forms.Timer(this.components);
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.panel2 = new System.Windows.Forms.Panel();
+ this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
+ this.queueUsageControl6 = new UsageControl.QueueUsageControl();
+ this.queueUsageControl2 = new UsageControl.QueueUsageControl();
+ this.queueUsageControl1 = new UsageControl.QueueUsageControl();
+ this.queueUsageControl3 = new UsageControl.QueueUsageControl();
+ this.usageThreadsInPool = new UsageControl.UsageControl();
+ this.usageHistorySTP = new UsageControl.UsageHistoryControl();
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon6)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon3)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon2)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction6)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration6)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration2)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction2)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration3)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction3)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinIdleTimeout)).BeginInit();
+ this.groupBox4.SuspendLayout();
+ this.groupBox1.SuspendLayout();
+ this.groupBox2.SuspendLayout();
+ this.groupBox3.SuspendLayout();
+ this.groupWIGQueues.SuspendLayout();
+ this.groupBox7.SuspendLayout();
+ this.panelWIGsCtrls.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pcActiveThreads)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcInUseThreads)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcQueuedWorkItems)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcCompletedWorkItems)).BeginInit();
+ this.panel1.SuspendLayout();
+ this.panel2.SuspendLayout();
+ this.tableLayoutPanel1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // btnStart
+ //
+ this.btnStart.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.btnStart.Location = new System.Drawing.Point(6, 12);
+ this.btnStart.Name = "btnStart";
+ this.btnStart.Size = new System.Drawing.Size(80, 23);
+ this.btnStart.TabIndex = 0;
+ this.btnStart.Text = "Start STP";
+ this.toolTip1.SetToolTip(this.btnStart, "Start/Stop the Smart Thread Pool");
+ this.btnStart.UseVisualStyleBackColor = true;
+ this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
+ //
+ // timer1
+ //
+ this.timer1.Enabled = true;
+ this.timer1.Interval = 500;
+ this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // btnPause
+ //
+ this.btnPause.AutoSize = true;
+ this.btnPause.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.btnPause.Location = new System.Drawing.Point(8, 47);
+ this.btnPause.Margin = new System.Windows.Forms.Padding(2);
+ this.btnPause.Name = "btnPause";
+ this.btnPause.Size = new System.Drawing.Size(80, 23);
+ this.btnPause.TabIndex = 1;
+ this.btnPause.Text = "Pause STP";
+ this.toolTip1.SetToolTip(this.btnPause, "Pause/Resume the Smart Thread Pool");
+ this.btnPause.UseVisualStyleBackColor = true;
+ this.btnPause.Click += new System.EventHandler(this.btnPause_Click);
+ //
+ // btnCancel1
+ //
+ this.btnCancel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.btnCancel1.Location = new System.Drawing.Point(175, 5);
+ this.btnCancel1.Name = "btnCancel1";
+ this.btnCancel1.Size = new System.Drawing.Size(20, 23);
+ this.btnCancel1.TabIndex = 6;
+ this.toolTip1.SetToolTip(this.btnCancel1, "Cancel all work items of Work Items Group #1");
+ this.btnCancel1.UseVisualStyleBackColor = true;
+ this.btnCancel1.Click += new System.EventHandler(this.btnCancel1_Click);
+ //
+ // btnCancel2
+ //
+ this.btnCancel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.btnCancel2.Location = new System.Drawing.Point(175, 38);
+ this.btnCancel2.Name = "btnCancel2";
+ this.btnCancel2.Size = new System.Drawing.Size(20, 23);
+ this.btnCancel2.TabIndex = 7;
+ this.toolTip1.SetToolTip(this.btnCancel2, "Cancel all work items of Work Items Group #2");
+ this.btnCancel2.UseVisualStyleBackColor = true;
+ this.btnCancel2.Click += new System.EventHandler(this.btnCancel2_Click);
+ //
+ // btnCancel3
+ //
+ this.btnCancel3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.btnCancel3.Location = new System.Drawing.Point(175, 71);
+ this.btnCancel3.Name = "btnCancel3";
+ this.btnCancel3.Size = new System.Drawing.Size(20, 23);
+ this.btnCancel3.TabIndex = 8;
+ this.toolTip1.SetToolTip(this.btnCancel3, "Cancel all work items of Work Items Group #3");
+ this.btnCancel3.UseVisualStyleBackColor = true;
+ this.btnCancel3.Click += new System.EventHandler(this.btnCancel3_Click);
+ //
+ // spinCon6
+ //
+ this.spinCon6.Location = new System.Drawing.Point(83, 32);
+ this.spinCon6.Maximum = new decimal(new int[] {
+ 20,
+ 0,
+ 0,
+ 0});
+ this.spinCon6.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinCon6.Name = "spinCon6";
+ this.spinCon6.Size = new System.Drawing.Size(42, 20);
+ this.spinCon6.TabIndex = 1;
+ this.toolTip1.SetToolTip(this.spinCon6, "Change the concurrency of the Smart Thread Pool (Max working threads)");
+ this.spinCon6.Value = new decimal(new int[] {
+ 5,
+ 0,
+ 0,
+ 0});
+ this.spinCon6.ValueChanged += new System.EventHandler(this.spinCon_ValueChanged);
+ //
+ // spinCon1
+ //
+ this.spinCon1.Location = new System.Drawing.Point(83, 6);
+ this.spinCon1.Maximum = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ this.spinCon1.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinCon1.Name = "spinCon1";
+ this.spinCon1.Size = new System.Drawing.Size(42, 20);
+ this.spinCon1.TabIndex = 3;
+ this.toolTip1.SetToolTip(this.spinCon1, "Change the concurrency of Work Items Group #1");
+ this.spinCon1.Value = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinCon1.ValueChanged += new System.EventHandler(this.spinCon_ValueChanged);
+ //
+ // spinCon3
+ //
+ this.spinCon3.Location = new System.Drawing.Point(83, 72);
+ this.spinCon3.Maximum = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ this.spinCon3.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinCon3.Name = "spinCon3";
+ this.spinCon3.Size = new System.Drawing.Size(42, 20);
+ this.spinCon3.TabIndex = 5;
+ this.toolTip1.SetToolTip(this.spinCon3, "Change the concurrency of Work Items Group #3");
+ this.spinCon3.Value = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinCon3.ValueChanged += new System.EventHandler(this.spinCon_ValueChanged);
+ //
+ // spinCon2
+ //
+ this.spinCon2.Location = new System.Drawing.Point(83, 39);
+ this.spinCon2.Maximum = new decimal(new int[] {
+ 10,
+ 0,
+ 0,
+ 0});
+ this.spinCon2.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinCon2.Name = "spinCon2";
+ this.spinCon2.Size = new System.Drawing.Size(42, 20);
+ this.spinCon2.TabIndex = 4;
+ this.toolTip1.SetToolTip(this.spinCon2, "Change the concurrency of Work Items Group #2");
+ this.spinCon2.Value = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinCon2.ValueChanged += new System.EventHandler(this.spinCon_ValueChanged);
+ //
+ // spinProduction6
+ //
+ this.spinProduction6.Location = new System.Drawing.Point(405, 31);
+ this.spinProduction6.Maximum = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinProduction6.Name = "spinProduction6";
+ this.spinProduction6.Size = new System.Drawing.Size(56, 20);
+ this.spinProduction6.TabIndex = 4;
+ this.toolTip1.SetToolTip(this.spinProduction6, "The number of work items to produce and queue per second into the Smart Thread P" +
+ "ool");
+ //
+ // spinDuration6
+ //
+ this.spinDuration6.Increment = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinDuration6.Location = new System.Drawing.Point(522, 31);
+ this.spinDuration6.Maximum = new decimal(new int[] {
+ 2000,
+ 0,
+ 0,
+ 0});
+ this.spinDuration6.Name = "spinDuration6";
+ this.spinDuration6.Size = new System.Drawing.Size(56, 20);
+ this.spinDuration6.TabIndex = 5;
+ this.toolTip1.SetToolTip(this.spinDuration6, "The duration of each work item queue into the Smart Thread Pool");
+ this.spinDuration6.Value = new decimal(new int[] {
+ 1000,
+ 0,
+ 0,
+ 0});
+ //
+ // btnCancel6
+ //
+ this.btnCancel6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.btnCancel6.Location = new System.Drawing.Point(175, 29);
+ this.btnCancel6.Name = "btnCancel6";
+ this.btnCancel6.Size = new System.Drawing.Size(20, 23);
+ this.btnCancel6.TabIndex = 2;
+ this.toolTip1.SetToolTip(this.btnCancel6, "Cancel all work items of work items in Smart Thread Pool and its corrosponding Wo" +
+ "rk Items Groups.");
+ this.btnCancel6.UseVisualStyleBackColor = true;
+ this.btnCancel6.Click += new System.EventHandler(this.btnCancel6_Click);
+ //
+ // spinDuration1
+ //
+ this.spinDuration1.Increment = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinDuration1.Location = new System.Drawing.Point(522, 7);
+ this.spinDuration1.Maximum = new decimal(new int[] {
+ 2000,
+ 0,
+ 0,
+ 0});
+ this.spinDuration1.Name = "spinDuration1";
+ this.spinDuration1.Size = new System.Drawing.Size(56, 20);
+ this.spinDuration1.TabIndex = 15;
+ this.toolTip1.SetToolTip(this.spinDuration1, "The duration of each work item queued into the Work Items Group #1");
+ this.spinDuration1.Value = new decimal(new int[] {
+ 1000,
+ 0,
+ 0,
+ 0});
+ //
+ // spinProduction1
+ //
+ this.spinProduction1.Location = new System.Drawing.Point(405, 7);
+ this.spinProduction1.Maximum = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinProduction1.Name = "spinProduction1";
+ this.spinProduction1.Size = new System.Drawing.Size(56, 20);
+ this.spinProduction1.TabIndex = 12;
+ this.toolTip1.SetToolTip(this.spinProduction1, "The number of work items to produce and queue per second into Work Items Group #1" +
+ "");
+ //
+ // spinDuration2
+ //
+ this.spinDuration2.Increment = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinDuration2.Location = new System.Drawing.Point(522, 40);
+ this.spinDuration2.Maximum = new decimal(new int[] {
+ 2000,
+ 0,
+ 0,
+ 0});
+ this.spinDuration2.Name = "spinDuration2";
+ this.spinDuration2.Size = new System.Drawing.Size(56, 20);
+ this.spinDuration2.TabIndex = 16;
+ this.toolTip1.SetToolTip(this.spinDuration2, "The duration of each work item queued into the Work Items Group #2");
+ this.spinDuration2.Value = new decimal(new int[] {
+ 1000,
+ 0,
+ 0,
+ 0});
+ //
+ // spinProduction2
+ //
+ this.spinProduction2.Location = new System.Drawing.Point(405, 40);
+ this.spinProduction2.Maximum = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinProduction2.Name = "spinProduction2";
+ this.spinProduction2.Size = new System.Drawing.Size(56, 20);
+ this.spinProduction2.TabIndex = 13;
+ this.toolTip1.SetToolTip(this.spinProduction2, "The number of work items to produce and queue per second into Work Items Group #2" +
+ "");
+ //
+ // spinDuration3
+ //
+ this.spinDuration3.Increment = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinDuration3.Location = new System.Drawing.Point(522, 73);
+ this.spinDuration3.Maximum = new decimal(new int[] {
+ 2000,
+ 0,
+ 0,
+ 0});
+ this.spinDuration3.Name = "spinDuration3";
+ this.spinDuration3.Size = new System.Drawing.Size(56, 20);
+ this.spinDuration3.TabIndex = 17;
+ this.toolTip1.SetToolTip(this.spinDuration3, "The duration of each work item queued into the Work Items Group #3");
+ this.spinDuration3.Value = new decimal(new int[] {
+ 1000,
+ 0,
+ 0,
+ 0});
+ //
+ // spinProduction3
+ //
+ this.spinProduction3.Location = new System.Drawing.Point(405, 73);
+ this.spinProduction3.Maximum = new decimal(new int[] {
+ 50,
+ 0,
+ 0,
+ 0});
+ this.spinProduction3.Name = "spinProduction3";
+ this.spinProduction3.Size = new System.Drawing.Size(56, 20);
+ this.spinProduction3.TabIndex = 14;
+ this.toolTip1.SetToolTip(this.spinProduction3, "The number of work items to produce and queue per second into Work Items Group #3" +
+ "");
+ //
+ // spinIdleTimeout
+ //
+ this.spinIdleTimeout.Location = new System.Drawing.Point(101, 123);
+ this.spinIdleTimeout.Maximum = new decimal(new int[] {
+ 25,
+ 0,
+ 0,
+ 0});
+ this.spinIdleTimeout.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.spinIdleTimeout.Name = "spinIdleTimeout";
+ this.spinIdleTimeout.Size = new System.Drawing.Size(56, 20);
+ this.spinIdleTimeout.TabIndex = 3;
+ this.toolTip1.SetToolTip(this.spinIdleTimeout, "The idle timeout of the Smart Thread Pool in seconds.");
+ this.spinIdleTimeout.Value = new decimal(new int[] {
+ 5,
+ 0,
+ 0,
+ 0});
+ //
+ // lblStatus1
+ //
+ this.lblStatus1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.lblStatus1.Location = new System.Drawing.Point(17, 372);
+ this.lblStatus1.Name = "lblStatus1";
+ this.lblStatus1.Size = new System.Drawing.Size(72, 13);
+ this.lblStatus1.TabIndex = 37;
+ this.lblStatus1.Text = "Idle";
+ this.lblStatus1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.toolTip1.SetToolTip(this.lblStatus1, "The state of the Work Items Group #1");
+ //
+ // lblStatus2
+ //
+ this.lblStatus2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.lblStatus2.Location = new System.Drawing.Point(102, 372);
+ this.lblStatus2.Name = "lblStatus2";
+ this.lblStatus2.Size = new System.Drawing.Size(72, 13);
+ this.lblStatus2.TabIndex = 38;
+ this.lblStatus2.Text = "Idle";
+ this.lblStatus2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.toolTip1.SetToolTip(this.lblStatus2, "The state of the Work Items Group #2");
+ //
+ // lblStatus3
+ //
+ this.lblStatus3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.lblStatus3.Location = new System.Drawing.Point(186, 371);
+ this.lblStatus3.Name = "lblStatus3";
+ this.lblStatus3.Size = new System.Drawing.Size(72, 13);
+ this.lblStatus3.TabIndex = 39;
+ this.lblStatus3.Text = "Idle";
+ this.lblStatus3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.toolTip1.SetToolTip(this.lblStatus3, "The state of the Work Items Group #3");
+ //
+ // lblStatus6
+ //
+ this.lblStatus6.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.lblStatus6.Location = new System.Drawing.Point(39, 371);
+ this.lblStatus6.Name = "lblStatus6";
+ this.lblStatus6.Size = new System.Drawing.Size(72, 13);
+ this.lblStatus6.TabIndex = 42;
+ this.lblStatus6.Text = "Idle";
+ this.lblStatus6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.toolTip1.SetToolTip(this.lblStatus6, "The state of the Smart Thread Pool");
+ //
+ // lblWaitingCallbacks
+ //
+ this.lblWaitingCallbacks.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblWaitingCallbacks.Location = new System.Drawing.Point(80, 16);
+ this.lblWaitingCallbacks.Name = "lblWaitingCallbacks";
+ this.lblWaitingCallbacks.Size = new System.Drawing.Size(80, 24);
+ this.lblWaitingCallbacks.TabIndex = 22;
+ this.lblWaitingCallbacks.Text = "XXXXXXXXX";
+ this.lblWaitingCallbacks.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.toolTip1.SetToolTip(this.lblWaitingCallbacks, "The number of queued items in the Smart Thread Pool queue");
+ //
+ // lblWorkItemsGenerated
+ //
+ this.lblWorkItemsGenerated.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblWorkItemsGenerated.Location = new System.Drawing.Point(80, 40);
+ this.lblWorkItemsGenerated.Name = "lblWorkItemsGenerated";
+ this.lblWorkItemsGenerated.Size = new System.Drawing.Size(80, 24);
+ this.lblWorkItemsGenerated.TabIndex = 27;
+ this.lblWorkItemsGenerated.Text = "XXXXXXXXX";
+ this.lblWorkItemsGenerated.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.toolTip1.SetToolTip(this.lblWorkItemsGenerated, "The total number of generated work items (including Work Items Groups) that were " +
+ "generated since \'Start STP\' was pressed.");
+ //
+ // lblWorkItemsCompleted
+ //
+ this.lblWorkItemsCompleted.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblWorkItemsCompleted.Location = new System.Drawing.Point(80, 64);
+ this.lblWorkItemsCompleted.Name = "lblWorkItemsCompleted";
+ this.lblWorkItemsCompleted.Size = new System.Drawing.Size(80, 24);
+ this.lblWorkItemsCompleted.TabIndex = 28;
+ this.lblWorkItemsCompleted.Text = "XXXXXXXXX";
+ this.lblWorkItemsCompleted.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.toolTip1.SetToolTip(this.lblWorkItemsCompleted, "The total number of work items completed.");
+ //
+ // lblThreadInUse
+ //
+ this.lblThreadInUse.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblThreadInUse.Location = new System.Drawing.Point(80, 40);
+ this.lblThreadInUse.Name = "lblThreadInUse";
+ this.lblThreadInUse.Size = new System.Drawing.Size(80, 24);
+ this.lblThreadInUse.TabIndex = 18;
+ this.lblThreadInUse.Text = "XXXXXXXXX";
+ this.lblThreadInUse.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.toolTip1.SetToolTip(this.lblThreadInUse, "The number of busy threads in the Smart Thread Pool");
+ //
+ // lblThreadsInPool
+ //
+ this.lblThreadsInPool.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblThreadsInPool.Location = new System.Drawing.Point(80, 16);
+ this.lblThreadsInPool.Name = "lblThreadsInPool";
+ this.lblThreadsInPool.Size = new System.Drawing.Size(80, 24);
+ this.lblThreadsInPool.TabIndex = 11;
+ this.lblThreadsInPool.Text = "XXXXXXXXX";
+ this.lblThreadsInPool.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+ this.toolTip1.SetToolTip(this.lblThreadsInPool, "The number of thread in the Smart Thread Pool (Busy and idle)");
+ //
+ // comboWIPriority6
+ //
+ this.comboWIPriority6.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboWIPriority6.FormattingEnabled = true;
+ this.comboWIPriority6.Items.AddRange(new object[] {
+ "Lowest (Lo)",
+ "Normal (N)",
+ "Highest (Hi)"});
+ this.comboWIPriority6.Location = new System.Drawing.Point(236, 30);
+ this.comboWIPriority6.MaxDropDownItems = 5;
+ this.comboWIPriority6.Name = "comboWIPriority6";
+ this.comboWIPriority6.Size = new System.Drawing.Size(111, 21);
+ this.comboWIPriority6.TabIndex = 3;
+ this.toolTip1.SetToolTip(this.comboWIPriority6, "The priority of work items generated to the Smart Thread Pool.");
+ //
+ // comboWIPriority1
+ //
+ this.comboWIPriority1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboWIPriority1.FormattingEnabled = true;
+ this.comboWIPriority1.Items.AddRange(new object[] {
+ "Lowest (Lo)",
+ "Normal (N)",
+ "Highest (Hi)"});
+ this.comboWIPriority1.Location = new System.Drawing.Point(236, 6);
+ this.comboWIPriority1.MaxDropDownItems = 5;
+ this.comboWIPriority1.Name = "comboWIPriority1";
+ this.comboWIPriority1.Size = new System.Drawing.Size(111, 21);
+ this.comboWIPriority1.TabIndex = 9;
+ this.toolTip1.SetToolTip(this.comboWIPriority1, "The priority of work items generated to Work Items Group #1");
+ //
+ // comboWIPriority2
+ //
+ this.comboWIPriority2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboWIPriority2.FormattingEnabled = true;
+ this.comboWIPriority2.Items.AddRange(new object[] {
+ "Lowest (Lo)",
+ "Normal (N)",
+ "Highest (Hi)"});
+ this.comboWIPriority2.Location = new System.Drawing.Point(236, 39);
+ this.comboWIPriority2.MaxDropDownItems = 5;
+ this.comboWIPriority2.Name = "comboWIPriority2";
+ this.comboWIPriority2.Size = new System.Drawing.Size(111, 21);
+ this.comboWIPriority2.TabIndex = 10;
+ this.toolTip1.SetToolTip(this.comboWIPriority2, "The priority of work items generated to Work Items Group #2");
+ //
+ // comboWIPriority3
+ //
+ this.comboWIPriority3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboWIPriority3.FormattingEnabled = true;
+ this.comboWIPriority3.Items.AddRange(new object[] {
+ "Lowest (Lo)",
+ "Normal (N)",
+ "Highest (Hi)"});
+ this.comboWIPriority3.Location = new System.Drawing.Point(236, 72);
+ this.comboWIPriority3.MaxDropDownItems = 5;
+ this.comboWIPriority3.Name = "comboWIPriority3";
+ this.comboWIPriority3.Size = new System.Drawing.Size(111, 21);
+ this.comboWIPriority3.TabIndex = 11;
+ this.toolTip1.SetToolTip(this.comboWIPriority3, "The priority of work items generated to Work Items Group #3");
+ //
+ // btnMode
+ //
+ this.btnMode.AutoSize = true;
+ this.btnMode.Location = new System.Drawing.Point(9, 84);
+ this.btnMode.Name = "btnMode";
+ this.btnMode.Size = new System.Drawing.Size(81, 23);
+ this.btnMode.TabIndex = 2;
+ this.btnMode.Text = "Advanced >>";
+ this.toolTip1.SetToolTip(this.btnMode, "In advance mode the Work Items Group feature is enabled.");
+ this.btnMode.UseVisualStyleBackColor = true;
+ this.btnMode.Click += new System.EventHandler(this.btnMode_Click);
+ //
+ // groupBox4
+ //
+ this.groupBox4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupBox4.Controls.Add(this.usageHistorySTP);
+ this.groupBox4.Location = new System.Drawing.Point(102, 12);
+ this.groupBox4.Name = "groupBox4";
+ this.groupBox4.Size = new System.Drawing.Size(520, 128);
+ this.groupBox4.TabIndex = 1;
+ this.groupBox4.TabStop = false;
+ this.groupBox4.Text = "STP Usage History";
+ //
+ // groupBox1
+ //
+ this.groupBox1.Controls.Add(this.usageThreadsInPool);
+ this.groupBox1.Location = new System.Drawing.Point(6, 12);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new System.Drawing.Size(90, 128);
+ this.groupBox1.TabIndex = 0;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "STP Usage";
+ //
+ // groupBox2
+ //
+ this.groupBox2.Controls.Add(this.lblWaitingCallbacks);
+ this.groupBox2.Controls.Add(this.label6);
+ this.groupBox2.Controls.Add(this.label7);
+ this.groupBox2.Controls.Add(this.label11);
+ this.groupBox2.Controls.Add(this.lblWorkItemsGenerated);
+ this.groupBox2.Controls.Add(this.lblWorkItemsCompleted);
+ this.groupBox2.Location = new System.Drawing.Point(3, 175);
+ this.groupBox2.Name = "groupBox2";
+ this.groupBox2.Size = new System.Drawing.Size(169, 96);
+ this.groupBox2.TabIndex = 46;
+ this.groupBox2.TabStop = false;
+ this.groupBox2.Text = "Work items in STP";
+ //
+ // label6
+ //
+ this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label6.Location = new System.Drawing.Point(8, 16);
+ this.label6.Name = "label6";
+ this.label6.Size = new System.Drawing.Size(48, 24);
+ this.label6.TabIndex = 21;
+ this.label6.Text = "Queued";
+ this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label7
+ //
+ this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label7.Location = new System.Drawing.Point(8, 40);
+ this.label7.Name = "label7";
+ this.label7.Size = new System.Drawing.Size(64, 24);
+ this.label7.TabIndex = 25;
+ this.label7.Text = "Generated";
+ this.label7.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label11
+ //
+ this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label11.Location = new System.Drawing.Point(8, 64);
+ this.label11.Name = "label11";
+ this.label11.Size = new System.Drawing.Size(64, 24);
+ this.label11.TabIndex = 26;
+ this.label11.Text = "Completed";
+ this.label11.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // groupBox3
+ //
+ this.groupBox3.Controls.Add(this.lblThreadInUse);
+ this.groupBox3.Controls.Add(this.label12);
+ this.groupBox3.Controls.Add(this.lblThreadsInPool);
+ this.groupBox3.Controls.Add(this.label15);
+ this.groupBox3.Location = new System.Drawing.Point(3, 277);
+ this.groupBox3.Name = "groupBox3";
+ this.groupBox3.Size = new System.Drawing.Size(169, 72);
+ this.groupBox3.TabIndex = 47;
+ this.groupBox3.TabStop = false;
+ this.groupBox3.Text = "Threads";
+ //
+ // label12
+ //
+ this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label12.Location = new System.Drawing.Point(8, 40);
+ this.label12.Name = "label12";
+ this.label12.Size = new System.Drawing.Size(80, 24);
+ this.label12.TabIndex = 17;
+ this.label12.Text = "Used (Green)";
+ this.label12.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label15
+ //
+ this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label15.Location = new System.Drawing.Point(8, 16);
+ this.label15.Name = "label15";
+ this.label15.Size = new System.Drawing.Size(72, 24);
+ this.label15.TabIndex = 3;
+ this.label15.Text = "In pool (Red)";
+ this.label15.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // groupWIGQueues
+ //
+ this.groupWIGQueues.Controls.Add(this.label13);
+ this.groupWIGQueues.Controls.Add(this.label16);
+ this.groupWIGQueues.Controls.Add(this.label17);
+ this.groupWIGQueues.Controls.Add(this.queueUsageControl2);
+ this.groupWIGQueues.Controls.Add(this.queueUsageControl1);
+ this.groupWIGQueues.Controls.Add(this.queueUsageControl3);
+ this.groupWIGQueues.Controls.Add(this.lblStatus1);
+ this.groupWIGQueues.Controls.Add(this.lblStatus2);
+ this.groupWIGQueues.Controls.Add(this.lblStatus3);
+ this.groupWIGQueues.Dock = System.Windows.Forms.DockStyle.Left;
+ this.groupWIGQueues.Location = new System.Drawing.Point(178, 3);
+ this.groupWIGQueues.Name = "groupWIGQueues";
+ this.groupWIGQueues.Size = new System.Drawing.Size(275, 391);
+ this.groupWIGQueues.TabIndex = 48;
+ this.groupWIGQueues.TabStop = false;
+ this.groupWIGQueues.Text = "Work Items Groups Queues";
+ //
+ // label13
+ //
+ this.label13.Location = new System.Drawing.Point(17, 19);
+ this.label13.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label13.Name = "label13";
+ this.label13.Size = new System.Drawing.Size(72, 13);
+ this.label13.TabIndex = 40;
+ this.label13.Text = "#1";
+ this.label13.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label16
+ //
+ this.label16.Location = new System.Drawing.Point(102, 19);
+ this.label16.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label16.Name = "label16";
+ this.label16.Size = new System.Drawing.Size(72, 13);
+ this.label16.TabIndex = 41;
+ this.label16.Text = "#2";
+ this.label16.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label17
+ //
+ this.label17.Location = new System.Drawing.Point(186, 19);
+ this.label17.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label17.Name = "label17";
+ this.label17.Size = new System.Drawing.Size(72, 13);
+ this.label17.TabIndex = 42;
+ this.label17.Text = "#3";
+ this.label17.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // groupBox7
+ //
+ this.groupBox7.Controls.Add(this.queueUsageControl6);
+ this.groupBox7.Controls.Add(this.lblStatus6);
+ this.groupBox7.Dock = System.Windows.Forms.DockStyle.Left;
+ this.groupBox7.Location = new System.Drawing.Point(459, 3);
+ this.groupBox7.Name = "groupBox7";
+ this.groupBox7.Size = new System.Drawing.Size(151, 391);
+ this.groupBox7.TabIndex = 50;
+ this.groupBox7.TabStop = false;
+ this.groupBox7.Text = "Smart Thread Pool Queue";
+ //
+ // label3
+ //
+ this.label3.Location = new System.Drawing.Point(4, 73);
+ this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(72, 18);
+ this.label3.TabIndex = 2;
+ this.label3.Text = "WIG #3";
+ this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label4
+ //
+ this.label4.Location = new System.Drawing.Point(4, 40);
+ this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(72, 18);
+ this.label4.TabIndex = 1;
+ this.label4.Text = "WIG #2";
+ this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label5
+ //
+ this.label5.Location = new System.Drawing.Point(4, 7);
+ this.label5.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(72, 18);
+ this.label5.TabIndex = 0;
+ this.label5.Text = "WIG #1";
+ this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label8
+ //
+ this.label8.Location = new System.Drawing.Point(6, 31);
+ this.label8.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(72, 18);
+ this.label8.TabIndex = 0;
+ this.label8.Text = "STP";
+ this.label8.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.label1.Location = new System.Drawing.Point(6, 9);
+ this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(44, 13);
+ this.label1.TabIndex = 68;
+ this.label1.Text = "Queue";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.label2.Location = new System.Drawing.Point(65, 9);
+ this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(78, 13);
+ this.label2.TabIndex = 69;
+ this.label2.Text = "Concurrency";
+ //
+ // label9
+ //
+ this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.label9.Location = new System.Drawing.Point(147, 1);
+ this.label9.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label9.Name = "label9";
+ this.label9.Size = new System.Drawing.Size(79, 30);
+ this.label9.TabIndex = 70;
+ this.label9.Text = "Cancel Work Items";
+ this.label9.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label10
+ //
+ this.label10.AutoSize = true;
+ this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.label10.Location = new System.Drawing.Point(234, 10);
+ this.label10.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label10.Name = "label10";
+ this.label10.Size = new System.Drawing.Size(114, 13);
+ this.label10.TabIndex = 71;
+ this.label10.Text = "Work Items Priority";
+ //
+ // label14
+ //
+ this.label14.AutoSize = true;
+ this.label14.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.label14.Location = new System.Drawing.Point(6, 125);
+ this.label14.Name = "label14";
+ this.label14.Size = new System.Drawing.Size(92, 13);
+ this.label14.TabIndex = 4;
+ this.label14.Text = "Idle timeout (Sec):";
+ this.label14.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // label18
+ //
+ this.label18.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.label18.Location = new System.Drawing.Point(368, -5);
+ this.label18.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label18.Name = "label18";
+ this.label18.Size = new System.Drawing.Size(121, 36);
+ this.label18.TabIndex = 75;
+ this.label18.Text = "Work Items Produced / Second";
+ this.label18.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // label19
+ //
+ this.label19.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.label19.Location = new System.Drawing.Point(494, -1);
+ this.label19.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
+ this.label19.Name = "label19";
+ this.label19.Size = new System.Drawing.Size(126, 32);
+ this.label19.TabIndex = 77;
+ this.label19.Text = "Work Item Duration (milliseconds)";
+ this.label19.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // timer2
+ //
+ this.timer2.Enabled = true;
+ this.timer2.Interval = 1000;
+ this.timer2.Tick += new System.EventHandler(this.timer2_Tick);
+ //
+ // panelWIGsCtrls
+ //
+ this.tableLayoutPanel1.SetColumnSpan(this.panelWIGsCtrls, 3);
+ this.panelWIGsCtrls.Controls.Add(this.label5);
+ this.panelWIGsCtrls.Controls.Add(this.spinCon2);
+ this.panelWIGsCtrls.Controls.Add(this.spinDuration3);
+ this.panelWIGsCtrls.Controls.Add(this.spinCon3);
+ this.panelWIGsCtrls.Controls.Add(this.spinProduction3);
+ this.panelWIGsCtrls.Controls.Add(this.spinCon1);
+ this.panelWIGsCtrls.Controls.Add(this.spinDuration2);
+ this.panelWIGsCtrls.Controls.Add(this.label4);
+ this.panelWIGsCtrls.Controls.Add(this.spinProduction2);
+ this.panelWIGsCtrls.Controls.Add(this.label3);
+ this.panelWIGsCtrls.Controls.Add(this.spinDuration1);
+ this.panelWIGsCtrls.Controls.Add(this.btnCancel3);
+ this.panelWIGsCtrls.Controls.Add(this.spinProduction1);
+ this.panelWIGsCtrls.Controls.Add(this.btnCancel2);
+ this.panelWIGsCtrls.Controls.Add(this.comboWIPriority3);
+ this.panelWIGsCtrls.Controls.Add(this.btnCancel1);
+ this.panelWIGsCtrls.Controls.Add(this.comboWIPriority2);
+ this.panelWIGsCtrls.Controls.Add(this.comboWIPriority1);
+ this.panelWIGsCtrls.Location = new System.Drawing.Point(0, 452);
+ this.panelWIGsCtrls.Margin = new System.Windows.Forms.Padding(0);
+ this.panelWIGsCtrls.Name = "panelWIGsCtrls";
+ this.panelWIGsCtrls.Size = new System.Drawing.Size(612, 105);
+ this.panelWIGsCtrls.TabIndex = 1;
+ //
+ // pcActiveThreads
+ //
+ this.pcActiveThreads.CategoryName = "SmartThreadPool";
+ this.pcActiveThreads.CounterName = "Active threads";
+ this.pcActiveThreads.InstanceName = "SmartThreadPoolDemo";
+ //
+ // pcInUseThreads
+ //
+ this.pcInUseThreads.CategoryName = "SmartThreadPool";
+ this.pcInUseThreads.CounterName = "In use threads";
+ this.pcInUseThreads.InstanceName = "SmartThreadPoolDemo";
+ //
+ // pcQueuedWorkItems
+ //
+ this.pcQueuedWorkItems.CategoryName = "SmartThreadPool";
+ this.pcQueuedWorkItems.CounterName = "Work Items in queue";
+ this.pcQueuedWorkItems.InstanceName = "SmartThreadPoolDemo";
+ //
+ // pcCompletedWorkItems
+ //
+ this.pcCompletedWorkItems.CategoryName = "SmartThreadPool";
+ this.pcCompletedWorkItems.CounterName = "Work Items processed";
+ this.pcCompletedWorkItems.InstanceName = "SmartThreadPoolDemo";
+ //
+ // timerPoll
+ //
+ this.timerPoll.Interval = 500;
+ this.timerPoll.Tick += new System.EventHandler(this.timerPoll_Tick);
+ //
+ // panel1
+ //
+ this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)));
+ this.panel1.AutoSize = true;
+ this.panel1.Controls.Add(this.groupBox2);
+ this.panel1.Controls.Add(this.btnMode);
+ this.panel1.Controls.Add(this.groupBox3);
+ this.panel1.Controls.Add(this.btnStart);
+ this.panel1.Controls.Add(this.btnPause);
+ this.panel1.Controls.Add(this.spinIdleTimeout);
+ this.panel1.Controls.Add(this.label14);
+ this.panel1.Location = new System.Drawing.Point(0, 0);
+ this.panel1.Margin = new System.Windows.Forms.Padding(0);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(175, 397);
+ this.panel1.TabIndex = 0;
+ //
+ // panel2
+ //
+ this.tableLayoutPanel1.SetColumnSpan(this.panel2, 3);
+ this.panel2.Controls.Add(this.btnCancel6);
+ this.panel2.Controls.Add(this.spinDuration6);
+ this.panel2.Controls.Add(this.label9);
+ this.panel2.Controls.Add(this.label18);
+ this.panel2.Controls.Add(this.label19);
+ this.panel2.Controls.Add(this.spinProduction6);
+ this.panel2.Controls.Add(this.comboWIPriority6);
+ this.panel2.Controls.Add(this.label10);
+ this.panel2.Controls.Add(this.label2);
+ this.panel2.Controls.Add(this.label1);
+ this.panel2.Controls.Add(this.label8);
+ this.panel2.Controls.Add(this.spinCon6);
+ this.panel2.Location = new System.Drawing.Point(0, 397);
+ this.panel2.Margin = new System.Windows.Forms.Padding(0);
+ this.panel2.Name = "panel2";
+ this.panel2.Size = new System.Drawing.Size(612, 55);
+ this.panel2.TabIndex = 0;
+ //
+ // tableLayoutPanel1
+ //
+ this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.tableLayoutPanel1.ColumnCount = 3;
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tableLayoutPanel1.Controls.Add(this.panel2, 0, 1);
+ this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0);
+ this.tableLayoutPanel1.Controls.Add(this.panelWIGsCtrls, 0, 2);
+ this.tableLayoutPanel1.Controls.Add(this.groupBox7, 2, 0);
+ this.tableLayoutPanel1.Controls.Add(this.groupWIGQueues, 1, 0);
+ this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 146);
+ this.tableLayoutPanel1.MinimumSize = new System.Drawing.Size(615, 351);
+ this.tableLayoutPanel1.Name = "tableLayoutPanel1";
+ this.tableLayoutPanel1.RowCount = 3;
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 55F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 105F));
+ this.tableLayoutPanel1.Size = new System.Drawing.Size(615, 557);
+ this.tableLayoutPanel1.TabIndex = 95;
+ //
+ // queueUsageControl6
+ //
+ this.queueUsageControl6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)));
+ this.queueUsageControl6.BackColor = System.Drawing.Color.Transparent;
+ this.queueUsageControl6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.queueUsageControl6.Location = new System.Drawing.Point(39, 19);
+ this.queueUsageControl6.Margin = new System.Windows.Forms.Padding(6);
+ this.queueUsageControl6.Name = "queueUsageControl6";
+ this.queueUsageControl6.Size = new System.Drawing.Size(72, 346);
+ this.queueUsageControl6.TabIndex = 30;
+ this.queueUsageControl6.TabStop = false;
+ this.toolTip1.SetToolTip(this.queueUsageControl6, "Smart Thread Pool Queue. The head of the queue is at the bottom. Executed work i" +
+ "tems are shown blinking.");
+ //
+ // queueUsageControl2
+ //
+ this.queueUsageControl2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.queueUsageControl2.BackColor = System.Drawing.Color.Transparent;
+ this.queueUsageControl2.CausesValidation = false;
+ this.queueUsageControl2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.queueUsageControl2.Location = new System.Drawing.Point(102, 44);
+ this.queueUsageControl2.Margin = new System.Windows.Forms.Padding(6);
+ this.queueUsageControl2.Name = "queueUsageControl2";
+ this.queueUsageControl2.Size = new System.Drawing.Size(72, 321);
+ this.queueUsageControl2.TabIndex = 26;
+ this.toolTip1.SetToolTip(this.queueUsageControl2, "Work Items Group #2 Queue. Each Work Items Group has unique color for its work it" +
+ "ems.");
+ //
+ // queueUsageControl1
+ //
+ this.queueUsageControl1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.queueUsageControl1.BackColor = System.Drawing.Color.Transparent;
+ this.queueUsageControl1.CausesValidation = false;
+ this.queueUsageControl1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.queueUsageControl1.Location = new System.Drawing.Point(17, 44);
+ this.queueUsageControl1.Margin = new System.Windows.Forms.Padding(6);
+ this.queueUsageControl1.Name = "queueUsageControl1";
+ this.queueUsageControl1.Size = new System.Drawing.Size(72, 321);
+ this.queueUsageControl1.TabIndex = 25;
+ this.toolTip1.SetToolTip(this.queueUsageControl1, "Work Items Group #1 Queue. Each Work Items Group has unique color for its work it" +
+ "ems.");
+ //
+ // queueUsageControl3
+ //
+ this.queueUsageControl3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.queueUsageControl3.BackColor = System.Drawing.Color.Transparent;
+ this.queueUsageControl3.CausesValidation = false;
+ this.queueUsageControl3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.queueUsageControl3.Location = new System.Drawing.Point(186, 44);
+ this.queueUsageControl3.Margin = new System.Windows.Forms.Padding(6);
+ this.queueUsageControl3.Name = "queueUsageControl3";
+ this.queueUsageControl3.Size = new System.Drawing.Size(72, 321);
+ this.queueUsageControl3.TabIndex = 27;
+ this.toolTip1.SetToolTip(this.queueUsageControl3, "Work Items Group #3 Queue. Each Work Items Group has unique color for its work it" +
+ "ems.");
+ //
+ // usageThreadsInPool
+ //
+ this.usageThreadsInPool.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.usageThreadsInPool.BackColor = System.Drawing.Color.Black;
+ this.usageThreadsInPool.Location = new System.Drawing.Point(25, 16);
+ this.usageThreadsInPool.Maximum = 20;
+ this.usageThreadsInPool.Name = "usageThreadsInPool";
+ this.usageThreadsInPool.Size = new System.Drawing.Size(41, 104);
+ this.usageThreadsInPool.TabIndex = 0;
+ this.usageThreadsInPool.Value1 = 0;
+ this.usageThreadsInPool.Value2 = 0;
+ //
+ // usageHistorySTP
+ //
+ this.usageHistorySTP.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.usageHistorySTP.BackColor = System.Drawing.Color.Black;
+ this.usageHistorySTP.Location = new System.Drawing.Point(8, 16);
+ this.usageHistorySTP.Maximum = 100;
+ this.usageHistorySTP.Name = "usageHistorySTP";
+ this.usageHistorySTP.Size = new System.Drawing.Size(501, 104);
+ this.usageHistorySTP.TabIndex = 0;
+ this.toolTip1.SetToolTip(this.usageHistorySTP, "Smart Thread Pool usage. Green means used threads. Red means threads in pool.");
+ //
+ // Form1
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(628, 715);
+ this.Controls.Add(this.tableLayoutPanel1);
+ this.Controls.Add(this.groupBox1);
+ this.Controls.Add(this.groupBox4);
+ this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.MinimumSize = new System.Drawing.Size(636, 695);
+ this.Name = "Form1";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Work Items Group Demo";
+ this.Load += new System.EventHandler(this.Form1_Load);
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon6)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon3)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinCon2)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction6)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration6)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration2)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction2)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinDuration3)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinProduction3)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.spinIdleTimeout)).EndInit();
+ this.groupBox4.ResumeLayout(false);
+ this.groupBox1.ResumeLayout(false);
+ this.groupBox2.ResumeLayout(false);
+ this.groupBox3.ResumeLayout(false);
+ this.groupWIGQueues.ResumeLayout(false);
+ this.groupBox7.ResumeLayout(false);
+ this.panelWIGsCtrls.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.pcActiveThreads)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcInUseThreads)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcQueuedWorkItems)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.pcCompletedWorkItems)).EndInit();
+ this.panel1.ResumeLayout(false);
+ this.panel1.PerformLayout();
+ this.panel2.ResumeLayout(false);
+ this.panel2.PerformLayout();
+ this.tableLayoutPanel1.ResumeLayout(false);
+ this.tableLayoutPanel1.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private UsageControl.QueueUsageControl queueUsageControl1;
+ private System.Windows.Forms.Button btnStart;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.Button btnPause;
+ private UsageControl.QueueUsageControl queueUsageControl2;
+ private UsageControl.QueueUsageControl queueUsageControl3;
+ private UsageControl.QueueUsageControl queueUsageControl6;
+ private System.Windows.Forms.ToolTip toolTip1;
+ private System.Windows.Forms.Label lblStatus1;
+ private System.Windows.Forms.Label lblStatus2;
+ private System.Windows.Forms.Label lblStatus3;
+ private System.Windows.Forms.Label lblStatus6;
+ private System.Windows.Forms.GroupBox groupBox4;
+ private UsageControl.UsageHistoryControl usageHistorySTP;
+ private System.Windows.Forms.GroupBox groupBox1;
+ private UsageControl.UsageControl usageThreadsInPool;
+ private System.Windows.Forms.GroupBox groupBox2;
+ private System.Windows.Forms.Label lblWaitingCallbacks;
+ private System.Windows.Forms.Label label6;
+ private System.Windows.Forms.Label label7;
+ private System.Windows.Forms.Label label11;
+ private System.Windows.Forms.Label lblWorkItemsGenerated;
+ private System.Windows.Forms.Label lblWorkItemsCompleted;
+ private System.Windows.Forms.GroupBox groupBox3;
+ private System.Windows.Forms.Label lblThreadInUse;
+ private System.Windows.Forms.Label label12;
+ private System.Windows.Forms.Label lblThreadsInPool;
+ private System.Windows.Forms.Label label15;
+ private System.Windows.Forms.GroupBox groupWIGQueues;
+ private System.Windows.Forms.Label label13;
+ private System.Windows.Forms.Label label16;
+ private System.Windows.Forms.Label label17;
+ private System.Windows.Forms.GroupBox groupBox7;
+ private System.Windows.Forms.Button btnCancel1;
+ private System.Windows.Forms.Button btnCancel2;
+ private System.Windows.Forms.Button btnCancel3;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.NumericUpDown spinCon6;
+ private System.Windows.Forms.NumericUpDown spinCon1;
+ private System.Windows.Forms.NumericUpDown spinCon3;
+ private System.Windows.Forms.NumericUpDown spinCon2;
+ private System.Windows.Forms.Label label8;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Label label9;
+ private System.Windows.Forms.Label label10;
+ private System.Windows.Forms.ComboBox comboWIPriority6;
+ private System.Windows.Forms.Label label14;
+ private System.Windows.Forms.NumericUpDown spinProduction6;
+ private System.Windows.Forms.Label label18;
+ private System.Windows.Forms.NumericUpDown spinDuration6;
+ private System.Windows.Forms.Label label19;
+ private System.Windows.Forms.Button btnCancel6;
+ private System.Windows.Forms.ComboBox comboWIPriority1;
+ private System.Windows.Forms.ComboBox comboWIPriority2;
+ private System.Windows.Forms.ComboBox comboWIPriority3;
+ private System.Windows.Forms.NumericUpDown spinDuration1;
+ private System.Windows.Forms.NumericUpDown spinProduction1;
+ private System.Windows.Forms.NumericUpDown spinDuration2;
+ private System.Windows.Forms.NumericUpDown spinProduction2;
+ private System.Windows.Forms.NumericUpDown spinDuration3;
+ private System.Windows.Forms.NumericUpDown spinProduction3;
+ private System.Windows.Forms.NumericUpDown spinIdleTimeout;
+ private System.Windows.Forms.Timer timer2;
+ private System.Windows.Forms.Panel panelWIGsCtrls;
+ private System.Windows.Forms.Button btnMode;
+ private System.Diagnostics.PerformanceCounter pcActiveThreads;
+ private System.Diagnostics.PerformanceCounter pcInUseThreads;
+ private System.Diagnostics.PerformanceCounter pcQueuedWorkItems;
+ private System.Diagnostics.PerformanceCounter pcCompletedWorkItems;
+ private System.Windows.Forms.Timer timerPoll;
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.Panel panel2;
+ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
+ }
+}
+
diff --git a/WorkItemsGroupDemo/Form1.cs b/WorkItemsGroupDemo/Form1.cs
new file mode 100644
index 0000000..ca8f0a1
--- /dev/null
+++ b/WorkItemsGroupDemo/Form1.cs
@@ -0,0 +1,372 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using UsageControl;
+using Amib.Threading;
+using System.Threading;
+using System.Collections;
+
+namespace WorkItemsGroupDemo
+{
+ public partial class Form1 : Form
+ {
+ private bool _running = false;
+ private bool _paused = false;
+ private bool _advancedMode = false;
+ private int _workItemsGenerated;
+ Hashtable _workingStates = Hashtable.Synchronized(new Hashtable());
+ private SmartThreadPool _stp;
+ private IWorkItemsGroup _wig1;
+ private IWorkItemsGroup _wig2;
+ private IWorkItemsGroup _wig3;
+
+ private int[] _lastIndex = new int[4];
+
+ private static readonly Color _stpColor = Color.Gray;
+ private static readonly Color _wig1Color = Color.Red;
+ private static readonly Color _wig2Color = Color.Green;
+ private static readonly Color _wig3Color = Color.Blue;
+
+
+ private class WigEntry
+ {
+ public IWorkItemsGroup _wig;
+ public QueueUsageControl _queueUsageControl;
+ public Label _isIdle;
+
+ public WigEntry(
+ IWorkItemsGroup wig,
+ QueueUsageControl queueUsageControl,
+ Label isIdle)
+ {
+ _wig = wig;
+ _queueUsageControl = queueUsageControl;
+ _isIdle = isIdle;
+ }
+ }
+
+ private WigEntry[] _wigEntries = null;
+
+ public Form1()
+ {
+ InitializeComponent();
+
+ InitSTP();
+
+ UpdateControls(false);
+ UpdateModeControls();
+ }
+
+ private void InitSTP()
+ {
+ STPStartInfo stpStartInfo = new STPStartInfo();
+ stpStartInfo.StartSuspended = true;
+ stpStartInfo.MaxWorkerThreads = (int)spinCon6.Value;
+ stpStartInfo.IdleTimeout = 5000;
+ stpStartInfo.PerformanceCounterInstanceName = "SmartThreadPoolDemo";
+
+ _stp = new SmartThreadPool(stpStartInfo);
+ _wig1 = _stp.CreateWorkItemsGroup((int)spinCon1.Value);
+ _wig2 = _stp.CreateWorkItemsGroup((int)spinCon2.Value);
+ _wig3 = _stp.CreateWorkItemsGroup((int)spinCon3.Value);
+
+ spinCon1.Tag = _wig1;
+ spinCon2.Tag = _wig2;
+ spinCon3.Tag = _wig3;
+ spinCon6.Tag = _stp;
+
+ comboWIPriority1.SelectedIndex = 1;
+ comboWIPriority2.SelectedIndex = 1;
+ comboWIPriority3.SelectedIndex = 1;
+ comboWIPriority6.SelectedIndex = 1;
+
+ _wigEntries = new WigEntry[]
+ {
+ new WigEntry(_wig1, queueUsageControl1, lblStatus1),
+ new WigEntry(_wig2, queueUsageControl2, lblStatus2),
+ new WigEntry(_wig3, queueUsageControl3, lblStatus3),
+ };
+ for (int i = 0; i < _lastIndex.Length; i++)
+ {
+ _lastIndex[i] = 1;
+ }
+ }
+
+ private void btnStart_Click(object sender, EventArgs e)
+ {
+ _running = !_running;
+ btnStart.Text = _running ? "Stop STP" : "Start STP";
+
+ if (_running)
+ {
+ Start();
+ }
+ else
+ {
+ Shutdown();
+ }
+ }
+
+ private void Start()
+ {
+ _workItemsGenerated = 0;
+ UpdateControls(true);
+ _stp.Start();
+ _wig1.Start();
+ _wig2.Start();
+ _wig3.Start();
+ }
+
+ private void Shutdown()
+ {
+ _stp.Shutdown(false, 2000);
+ InitSTP();
+ UpdateControls(false);
+ }
+
+ private void UpdateControls(bool start)
+ {
+ timerPoll.Enabled = start;
+
+ lblThreadInUse.Text = "0";
+ lblThreadsInPool.Text = "0";
+ lblWaitingCallbacks.Text = "0";
+ usageThreadsInPool.Value1 = 0;
+ usageThreadsInPool.Value2 = 0;
+ lblWorkItemsCompleted.Text = "0";
+ lblWorkItemsGenerated.Text = "0";
+ usageHistorySTP.Reset();
+ usageHistorySTP.Maximum = usageThreadsInPool.Maximum;
+
+ spinIdleTimeout.Enabled = !start;
+ }
+
+ private object DoNothing(object state)
+ {
+ WorkItemState workItemState = (WorkItemState)state;
+ _workingStates.Add(workItemState.QueueUsageEntry, workItemState.QueueUsageEntry);
+ do
+ {
+ if (SmartThreadPool.IsWorkItemCanceled)
+ {
+ break;
+ }
+ Thread.Sleep(workItemState.SleepDuration);
+ } while (_paused);
+ _workingStates.Remove(workItemState.QueueUsageEntry);
+
+ return null;
+ }
+
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ foreach (WigEntry wigEntry in _wigEntries)
+ {
+ UpdateQueueUsageControl(
+ wigEntry._wig,
+ wigEntry._queueUsageControl,
+ wigEntry._isIdle);
+
+ }
+ UpdateWorkingSet();
+ }
+
+ private void UpdateWorkingSet()
+ {
+ lblStatus6.Text = _stp.IsIdle ? "Idle" : "Working";
+
+ object [] statesWorking = null;
+ lock (_workingStates.SyncRoot)
+ {
+ statesWorking = new object[_workingStates.Count];
+ _workingStates.Keys.CopyTo(statesWorking, 0);
+ }
+
+ object[] statesSTP = _stp.GetStates();
+
+ List list = new List();
+
+ foreach (QueueUsageControl.QueueUsageEntry entry in statesWorking)
+ {
+ if (null != entry)
+ {
+ entry.IsExecuting = true;
+ list.Add(entry);
+ }
+ }
+
+ foreach (WorkItemState state in statesSTP)
+ {
+ if (null != state)
+ {
+ list.Add(state.QueueUsageEntry);
+ }
+ }
+
+ queueUsageControl6.SetQueue(list);
+ }
+
+ private void UpdateQueueUsageControl(
+ IWorkItemsGroup wig,
+ QueueUsageControl queueUsageControl,
+ Label label)
+ {
+ label.Text = wig.IsIdle ? "Idle" : "Working";
+ object[] states = wig.GetStates();
+
+ List list = new List();
+
+ foreach (WorkItemState state in states)
+ {
+ if (null != state)
+ {
+ list.Add(state.QueueUsageEntry);
+ }
+ }
+
+ queueUsageControl.SetQueue(list);
+ }
+
+ private void btnPause_Click(object sender, EventArgs e)
+ {
+ _paused = !_paused;
+ btnPause.Text = _paused ? "Resume STP" : "Pause STP";
+ }
+
+ #region Test functions
+ private void btnState1_Click(object sender, EventArgs e)
+ {
+ List list = new List();
+ list.Add(new QueueUsageControl.QueueUsageEntry("#A", Color.Yellow));
+ list.Add(new QueueUsageControl.QueueUsageEntry("#B", Color.Green));
+ list.Add(new QueueUsageControl.QueueUsageEntry("#C", Color.Black));
+ list.Add(new QueueUsageControl.QueueUsageEntry("#D", Color.HotPink));
+ queueUsageControl1.SetQueue(list);
+
+ }
+
+ private void btnState2_Click(object sender, EventArgs e)
+ {
+ List list = new List();
+ list.Add(new QueueUsageControl.QueueUsageEntry("#1", Color.Cyan));
+ list.Add(new QueueUsageControl.QueueUsageEntry("#2", Color.Magenta));
+ list.Add(new QueueUsageControl.QueueUsageEntry("#3", Color.Red));
+ queueUsageControl1.SetQueue(list);
+ }
+ #endregion
+
+ #region WorkItemState class
+
+ private class WorkItemState
+ {
+ public readonly QueueUsageControl.QueueUsageEntry QueueUsageEntry;
+ public readonly int SleepDuration;
+
+ public WorkItemState(
+ QueueUsageControl.QueueUsageEntry queueUsageEntry,
+ int sleepDuration)
+ {
+ QueueUsageEntry = queueUsageEntry;
+ SleepDuration = sleepDuration;
+ }
+ }
+
+ #endregion
+
+ private void EnqueueWorkItems(ref int startIndex, int count, string text, Color color, WorkItemPriority priority, IWorkItemsGroup wig, int sleepDuration)
+ {
+ for (int i = 0; i < count; ++i, ++startIndex)
+ {
+ wig.QueueWorkItem(
+ DoNothing,
+ new WorkItemState(new QueueUsageControl.QueueUsageEntry(string.Format("{0}{1} ({2})", text, startIndex, priority.ToString().Substring(0,2 )), color), sleepDuration),
+ priority);
+ }
+ _workItemsGenerated += count;
+ }
+
+ private void btnCancel1_Click(object sender, EventArgs e)
+ {
+ _wig1.Cancel();
+ }
+
+ private void btnCancel2_Click(object sender, EventArgs e)
+ {
+ _wig2.Cancel();
+ }
+
+ private void btnCancel3_Click(object sender, EventArgs e)
+ {
+ _wig3.Cancel();
+ }
+
+ private void btnCancel6_Click(object sender, EventArgs e)
+ {
+ _stp.Cancel();
+ }
+
+ private void spinCon_ValueChanged(object sender, EventArgs e)
+ {
+ NumericUpDown spin = sender as NumericUpDown;
+ IWorkItemsGroup wig = spin.Tag as IWorkItemsGroup;
+ wig.Concurrency = (int)spin.Value;
+ }
+
+ private void timer2_Tick(object sender, EventArgs e)
+ {
+ EnqueueWorkItems(ref _lastIndex[0], Convert.ToInt32(spinProduction1.Value), "#", _wig1Color, (WorkItemPriority)(2 * comboWIPriority1.SelectedIndex), _wig1, Convert.ToInt32(spinDuration1.Value));
+ EnqueueWorkItems(ref _lastIndex[1], Convert.ToInt32(spinProduction2.Value), "#", _wig2Color, (WorkItemPriority)(2 * comboWIPriority2.SelectedIndex), _wig2, Convert.ToInt32(spinDuration2.Value));
+ EnqueueWorkItems(ref _lastIndex[2], Convert.ToInt32(spinProduction3.Value), "#", _wig3Color, (WorkItemPriority)(2 * comboWIPriority3.SelectedIndex), _wig3, Convert.ToInt32(spinDuration3.Value));
+ EnqueueWorkItems(ref _lastIndex[3], Convert.ToInt32(spinProduction6.Value), "#", _stpColor, (WorkItemPriority)(2 * comboWIPriority6.SelectedIndex), _stp, Convert.ToInt32(spinDuration6.Value));
+ }
+
+ private void btnMode_Click(object sender, EventArgs e)
+ {
+ _advancedMode = !_advancedMode;
+ UpdateModeControls();
+ }
+
+ private void UpdateModeControls()
+ {
+ btnMode.Text = _advancedMode ? "Basic <<" : "Advanced >>";
+ panelWIGsCtrls.Visible = _advancedMode;
+ groupWIGQueues.Visible = _advancedMode;
+ }
+
+ private void timerPoll_Tick(object sender, EventArgs e)
+ {
+ SmartThreadPool stp = _stp;
+ if (null == stp)
+ {
+ return;
+ }
+
+ int threadsInUse = (int)pcInUseThreads.NextValue();
+ int threadsInPool = (int)pcActiveThreads.NextValue();
+
+ lblThreadInUse.Text = threadsInUse.ToString();
+ lblThreadsInPool.Text = threadsInPool.ToString();
+ lblWaitingCallbacks.Text = pcQueuedWorkItems.NextValue().ToString(); //stp.WaitingCallbacks.ToString();
+ usageThreadsInPool.Value1 = threadsInUse;
+ usageThreadsInPool.Value2 = threadsInPool;
+ lblWorkItemsCompleted.Text = pcCompletedWorkItems.NextValue().ToString();
+ lblWorkItemsGenerated.Text = _workItemsGenerated.ToString();
+ usageHistorySTP.AddValues(threadsInUse, threadsInPool);
+ }
+
+ private void Form1_Load(object sender, EventArgs e)
+ {
+ label5.Image = QueueUsageControl.GenerateItemImage("", _wig1Color, 72, label5.Height, label5.Font); ;
+ label4.Image = QueueUsageControl.GenerateItemImage("", _wig2Color, 72, label5.Height, label5.Font); ;
+ label3.Image = QueueUsageControl.GenerateItemImage("", _wig3Color, 72, label5.Height, label5.Font); ;
+ label8.Image = QueueUsageControl.GenerateItemImage("", _stpColor, 72, label5.Height, label5.Font); ;
+
+
+ }
+ }
+}
diff --git a/WorkItemsGroupDemo/Form1.resx b/WorkItemsGroupDemo/Form1.resx
new file mode 100644
index 0000000..d258e73
--- /dev/null
+++ b/WorkItemsGroupDemo/Form1.resx
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 94, 4
+
+
+ 17, 7
+
+
+ 94, 4
+
+
+ 186, 4
+
+
+ 17, 54
+
+
+ 150, 54
+
+
+ 282, 54
+
+
+ 438, 54
+
+
+ 609, 54
+
+
+ 25
+
+
\ No newline at end of file
diff --git a/WorkItemsGroupDemo/Program.cs b/WorkItemsGroupDemo/Program.cs
new file mode 100644
index 0000000..681ecaf
--- /dev/null
+++ b/WorkItemsGroupDemo/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace WorkItemsGroupDemo
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ public static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new Form1());
+ }
+ }
+}
\ No newline at end of file
diff --git a/WorkItemsGroupDemo/Properties/AssemblyInfo.cs b/WorkItemsGroupDemo/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..146bfce
--- /dev/null
+++ b/WorkItemsGroupDemo/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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("WindowsApplication1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("WindowsApplication1")]
+[assembly: AssemblyCopyright("Copyright © 2006")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("68872ba4-6524-406b-9c96-cf8ca3f4c729")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/WorkItemsGroupDemo/Properties/Resources.Designer.cs b/WorkItemsGroupDemo/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..fefcbf9
--- /dev/null
+++ b/WorkItemsGroupDemo/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.832
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace WorkItemsGroupDemo.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WorkItemsGroupDemo.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/WorkItemsGroupDemo/Properties/Resources.resx b/WorkItemsGroupDemo/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/WorkItemsGroupDemo/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/WorkItemsGroupDemo/Properties/Settings.Designer.cs b/WorkItemsGroupDemo/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..8c80db0
--- /dev/null
+++ b/WorkItemsGroupDemo/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.832
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace WorkItemsGroupDemo.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/WorkItemsGroupDemo/Properties/Settings.settings b/WorkItemsGroupDemo/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/WorkItemsGroupDemo/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/WorkItemsGroupDemo/WorkItemsGroupDemo.csproj b/WorkItemsGroupDemo/WorkItemsGroupDemo.csproj
new file mode 100644
index 0000000..48c1c62
--- /dev/null
+++ b/WorkItemsGroupDemo/WorkItemsGroupDemo.csproj
@@ -0,0 +1,99 @@
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {DC005A64-FAE9-4CFA-ADC8-F1D1FE7FE6CD}
+ WinExe
+ Properties
+ WorkItemsGroupDemo
+ WorkItemsGroupDemo
+ WorkItemsGroupDemo.Program
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ bin\ReleaseCE\
+ TRACE
+ true
+ pdbonly
+ AnyCPU
+ prompt
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ Form1.cs
+
+
+
+
+ Designer
+ Form1.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ {8684FC56-A679-4E2E-8F96-E172FB062EB6}
+ SmartThreadPool
+
+
+ {C11A4561-CCB5-4C96-8DF2-B804031D89D8}
+ UsageControl
+
+
+
+
+
\ No newline at end of file