CVE-2015-10067: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (CWE-362)
value: A vulnerability was found in oznetmaster SSharpSmartThreadPool. It has been classified as problematic. This affects an unknown part of the file SSharpSmartThreadPool/SmartThreadPool.cs. The manipulation leads to race condition within a thread. The complexity of an attack is rather high. The exploitability is told to be difficult. The patch is named 0e58073c831093aad75e077962e9fb55cad0dc5f. It is recommended to apply a patch to fix this issue. The associated identifier of this vulnerability is VDB-218463.
CWE description: The product contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
CVSS v3 base score: 8.1
Published: 2023-01-18T01:15Z
Repository: https://github.com/oznetmaster/SSharpSmartThreadPool
Language: C#
Files changed: .gitattributes, .gitignore, Docs/SmartThreadPool.html, SSharpSmartThreadPool.sln, SSharpSmartThreadPool/CallerThreadContext.cs, SSharpSmartThreadPool/CanceledWorkItemsGroup.cs, SSharpSmartThreadPool/EventWaitHandle.cs, SSharpSmartThreadPool/EventWaitHandleFactory.cs, SSharpSmartThreadPool/Exceptions.cs, SSharpSmartThreadPool/Interfaces.cs, SSharpSmartThreadPool/InternalInterfaces.cs, SSharpSmartThreadPool/PriorityQueue.cs, SSharpSmartThreadPool/Properties/AssemblyInfo.cs, SSharpSmartThreadPool/SLExt.cs, SSharpSmartThreadPool/SSharpSmartThreadPool.csproj, SSharpSmartThreadPool/STPEventWaitHandle.cs, SSharpSmartThreadPool/STPPerformanceCounter.cs, SSharpSmartThreadPool/STPStartInfo.cs, SSharpSmartThreadPool/SmartThreadPool.ThreadEntry.cs, SSharpSmartThreadPool/SmartThreadPool.cs, SSharpSmartThreadPool/Stopwatch.cs, SSharpSmartThreadPool/SynchronizedDictionary.cs, SSharpSmartThreadPool/ThreadEx.cs, SSharpSmartThreadPool/WIGStartInfo.cs, SSharpSmartThreadPool/WorkItem.WorkItemResult.cs, SSharpSmartThreadPool/WorkItem.cs, SSharpSmartThreadPool/WorkItemFactory.cs, SSharpSmartThreadPool/WorkItemInfo.cs, SSharpSmartThreadPool/WorkItemResultTWrapper.cs, SSharpSmartThreadPool/WorkItemsGroup.cs, SSharpSmartThreadPool/WorkItemsGroupBase.cs, SSharpSmartThreadPool/WorkItemsQueue.cs, SSharpSmartThreadPoolTests/PermutationGenerator.cs, SSharpSmartThreadPoolTests/Properties/AssemblyInfo.cs, SSharpSmartThreadPoolTests/QueueWorkItemHelper.cs, SSharpSmartThreadPoolTests/SSharpSmartThreadPoolTests.csproj, SSharpSmartThreadPoolTests/TestActionT.cs, SSharpSmartThreadPoolTests/TestCancel.cs, SSharpSmartThreadPoolTests/TestChainedDelegates.cs, SSharpSmartThreadPoolTests/TestConcurrencyChanges.cs, SSharpSmartThreadPoolTests/TestExceptions.cs, SSharpSmartThreadPoolTests/TestFalseFillStateWithParams.cs, SSharpSmartThreadPoolTests/TestFillStateWithParams.cs, SSharpSmartThreadPoolTests/TestFunc.cs, SSharpSmartThreadPoolTests/TestFuncT.cs, SSharpSmartThreadPoolTests/TestGetResult.cs, SSharpSmartThreadPoolTests/TestMultipleWorkItems.cs, SSharpSmartThreadPoolTests/TestParallelMethods.cs, SSharpSmartThreadPoolTests/TestPostExecute.cs, SSharpSmartThreadPoolTests/TestPriorityQueue.cs, SSharpSmartThreadPoolTests/TestQueueWorkItem.cs, SSharpSmartThreadPoolTests/TestStartSuspended.cs, SSharpSmartThreadPoolTests/TestStateDispose.cs, SSharpSmartThreadPoolTests/TestThreadApartmentState.cs, SSharpSmartThreadPoolTests/TestThreadIsBackground.cs, SSharpSmartThreadPoolTests/TestThreadPriority.cs, SSharpSmartThreadPoolTests/TestThreadsCreate.cs, SSharpSmartThreadPoolTests/TestWIGActionT.cs, SSharpSmartThreadPoolTests/TestWIGChainedDelegates.cs, SSharpSmartThreadPoolTests/TestWIGConcurrency.cs, SSharpSmartThreadPoolTests/TestWIGConcurrencyChanges.cs, SSharpSmartThreadPoolTests/TestWIGExceptions.cs, SSharpSmartThreadPoolTests/TestWIGFillStateWithParams.cs, SSharpSmartThreadPoolTests/TestWIGFillStateWithParams1.cs, SSharpSmartThreadPoolTests/TestWIGFuncT.cs, SSharpSmartThreadPoolTests/TestWIGGetResult.cs, SSharpSmartThreadPoolTests/TestWIGMultipleWorkItems.cs, SSharpSmartThreadPoolTests/TestWIGPostExecute.cs, SSharpSmartThreadPoolTests/TestWIGQueueWorkItem.cs, SSharpSmartThreadPoolTests/TestWIGStateDispose.cs, SSharpSmartThreadPoolTests/TestWIGWaitForIdle.cs, SSharpSmartThreadPoolTests/TestWaitForIdle.cs, SSharpSmartThreadPoolTests/TestWorkItemTimeout.cs, SSharpSmartThreadPoolTests/TestWorkItemsGroups.cs, SSharpSmartThreadPoolTests/TestWorkItemsQueue.cs, license.txt
Commit date: 2015-03-14 11:05:44 +0800
Commit message: Fixed critical race vulnerability in Cancel

Diff: diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..bdb0cab
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,17 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+
+# Standard to msysgit
+*.doc	 diff=astextplain
+*.DOC	 diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF	 diff=astextplain
+*.rtf	 diff=astextplain
+*.RTF	 diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7057deb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,236 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings 
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# If using the old MSBuild-Integrated Package Restore, uncomment this:
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# =========================
+# Operating System Files
+# =========================
+
+# OSX
+# =========================
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# Windows
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+
+# Crestron S# Files
+ControlSystem.cfg
+
diff --git a/Docs/Mono.jpg b/Docs/Mono.jpg
new file mode 100644
index 0000000..e59723c
Binary files /dev/null and b/Docs/Mono.jpg differ
diff --git a/Docs/Silverlight.jpg b/Docs/Silverlight.jpg
new file mode 100644
index 0000000..2a3c8a3
Binary files /dev/null and b/Docs/Silverlight.jpg differ
diff --git a/Docs/SmartThreadPool.JPG b/Docs/SmartThreadPool.JPG
new file mode 100644
index 0000000..d724617
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..a7d3a8b
--- /dev/null
+++ b/Docs/SmartThreadPool.html
@@ -0,0 +1,1939 @@
+
+<ul class="download">
+<li><a href="smartthreadpool_2.2.3.zip">Download SmartThreadPool 2.2.3 source, examples, tests, and demos - 651KB</a></li></ul>
+
+
+<p><img width="600" height="405" src="smartthreadpool.jpg" /></p>
+
+<p>See the <a href="#History">History</a> section at the bottom for changes.</p>
+
+<h2>Basic usage&nbsp;</h2>
+
+<p>This is a Thread Pool; if you got here, you probably 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 <a href="#SimpleExample">examples</a> for advanced usage.</p>
+
+<pre lang="cs">// Create an instance of the Smart Thread Pool
+SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+// Queue an action (Fire and forget)
+smartThreadPool.QueueWorkItem(System.IO.File.Copy, 
+  @&quot;C:\Temp\myfile.bin&quot;, @&quot;C:\Temp\myfile.bak&quot;);
+
+// The action (file copy) will be done in the background by the Thread Pool</pre>
+
+<h2><a name="Introduction">Introduction</a></h2>
+
+<p>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:</p>
+
+<ul>
+<li>The number of threads dynamically changes according to the workload on the threads in the pool.&nbsp;</li>
+
+<li>Work items can return a value.</li>
+
+<li>A work item can be cancelled if it hasn't been executed yet.</li>
+
+<li>The caller thread's context is used when the work item is executed (limited).</li>
+
+<li>Usage of minimum number of Win32 event handles, so the handle count of the application won't explode.</li>
+
+<li>The caller can wait for multiple or all the work items to complete.</li>
+
+<li>A work item can have a <code>PostExecute</code> callback, which is called as soon the work item is completed.</li>
+
+<li>The state object that accompanies the work item can be disposed automatically.</li>
+
+<li>Work item exceptions are sent back to the caller.</li>
+
+<li>Work items have priority.</li>
+
+<li>Work items group.</li>
+
+<li>The caller can suspend the start of a thread pool and work items group.</li>
+
+<li>Threads have priority.</li>
+
+<li>Threads have initialization and termination events.</li>
+
+<li>WinCE platform is supported (limited).</li>
+
+<li><code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code> generic methods are supported.</li>
+
+<li><t>Silverlight is supported.</t></li>
+
+<li>Mono is supported.</li>
+
+<li><t>Performance counters (Windows and internal).</t></li>
+
+<li>Work item timeout (passive).</li>
+<li>Threads ApartmentState</li>
+<li>Threads IsBakcground</li>
+<li>Threads name template</li>
+<li>Windows Phone is supported (limited)</li>
+<li>Threads MaxStackSize</li>
+
+</ul>
+
+<h2>Why do you need a thread pool?</h2>
+
+<blockquote>
+<p>&quot;Many applications create threads that spend a great deal of time in the sleeping state, waiting for an event to occur. Other threads might enter a sleeping state only to be awakened periodically to poll for a change or update status information. Thread pooling enables you to use threads more efficiently by providing your application with a pool of worker threads that are managed by the system. One thread monitors the status of several wait operations queued to the thread pool. When a wait operation completes, a worker thread from the thread pool executes the corresponding callback function&quot;.</p>
+
+<p>MSDN, April 2004, ThreadPool Class [C#].</p>
+</blockquote>
+
+<h2>Smart Thread Pool features</h2>
+
+<p>When I wrote my application, I discovered that I needed a thread pool with the following features:</p>
+
+<ul>
+<li>The thread pool should implement the <code>QueueUserWorkItem()</code> method to comply with the .NET ThreadPool.</li>
+
+<li>The thread pool should be instantiated. (No static methods.) So, the threads in the pool are used only for one purpose.</li>
+
+<li>The number of threads in the pool should be dynamic with lower and upper limits.</li>
+</ul>
+
+<p>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:</p>
+
+<ol>
+<li><a href="#Feature1">The thread pool is instantiable</a>.</li>
+
+<li><a href="#Feature2">The number of threads dynamically changes</a>.</li>
+
+<li><a href="#Feature3">Work items return a value</a>.</li>
+
+<li><a href="#Feature4">The caller can wait for multiple work items to complete</a>.</li>
+
+<li><a href="#Feature5">A work item can be cancelled</a>.</li>
+
+<li><a href="#Feature6">The caller thread's context is used when the work item is executed (limited)</a>.</li>
+
+<li><a href="#Feature7">Usage of minimum number of Win32 event handles, so the handle count of the application won't explode</a>.</li>
+</ol>
+
+<p>Because of features 3 and 5, the thread pool no longer complies to the .NET ThreadPool, and so I could add more features.</p>
+
+<p>See the additional features section below for the new features added in this version.</p>
+
+<h2>Additional features added in December 2004</h2>
+
+<ol>
+<li value="8"><a href="#Feature8">Every 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>.</li>
+
+<li><a href="#Feature9">The user can choose to automatically dispose off the state object that accompanies the work item</a>.</li>
+
+<li><a href="#Feature10">The user can wait for the Smart Thread Pool to become idle</a>.</li>
+
+<li><a href="#Feature11">Exception handling has been changed, so if a work item throws an exception, it is re-thrown at <code>GetResult()</code>, rather than firing an <code>UnhandledException</code> event. Note that <code>PostExecute</code> exceptions are always ignored</a>.</li>
+</ol>
+
+<h2>New features added in January 2006</h2>
+
+<ol>
+<li value="12"><a href="#Feature12">Work items have priority</a>.</li>
+
+<li><a href="#Feature13">The caller thread's HTTP context can be used when the work item is executed (improves 6)</a>.</li>
+
+<li><a href="#Feature14">Work items group</a>.</li>
+
+<li><a href="#Feature15">The caller can create thread pools and work item groups in suspended state</a>.</li>
+
+<li><a href="#Feature16">Threads have priority</a>.</li>
+</ol>
+
+<h2>New features added in May 2008</h2>
+
+<ol>
+<li value="17"><a href="#Feature17">Enabled the change of <code>MaxThreads</code>/<code>MinThreads</code>/<code>Concurrency</code> at run time</a>.</li>
+
+<li><a href="#Feature5">Improved the Cancel behavior (see section 5)</a>.</li>
+
+<li><a href="#Feature19">Added callbacks for initialization and termination of threads</a>.</li>
+
+<li><a href="#Feature20">Added support for WinCE (limited)</a>.</li>
+
+<li><a href="#Feature21">Added the <code>IsIdle</code> flag to <code>SmartThreadPool</code> and to <code>IWorkItemsGroup</code></a>.</li>
+
+<li><a href="#Feature3">Added support for <code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code> (strong typed work items) (see section 3)</a>.</li>
+</ol>
+
+<h2>New features added in April 2009</h2>
+
+<ol>
+<li value="23"><a href="#Feature23">Added support for Silverlight</a>.</li>
+
+<li><a href="#Feature24">Added support for Mono</a>.</li>
+
+<li><a href="#Feature25">Added internal performance counters (for Windows CE, Silverlight, and Mono)</a>.</li>
+
+<li><a href="#Feature26">Added new methods: <code>Join</code>, <code>Choice</code>, and <code>Pipe</code></a>.</li>
+</ol>
+
+<h2>New features added in December 2009</h2>
+
+<ol>
+<li value="27"><a href="#Feature27">Added work item timeout (passive)</a>.</li>
+</ol>
+<h2>
+    New features added in August 2012</h2>
+<ol>
+    <li value="28"><a href="#Feature28">Enabled to set threads IsBackground.</a></li>
+    <li value="29"><a href="#Feature29">Enabled to set threads ApartmentState.
+    </a></li>
+<li value="30"><a href="#Feature30">Added support fore Windows Phone (limited).</a></li>
+<li value="31"><a href="#Feature30"></a><a href="#Feature31">Enabled to set threads MaxStackSize.</a></li>
+</ol>
+<p>
+
+
+</p><h2>What about the .NET ThreadPool?&nbsp;</h2>
+
+<p>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 the .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 &quot;Begin&quot; (e.g., <code>BeginInvoke</code>, <code>BeginSend</code>, <code>BeginReceive</code>, 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.</p>
+
+<p>This thread pool doesn't comply with the requirements 1, 5, 6, 8, 9, 10, 12-25.</p>
+
+<p>Note that the requirements 3 and 4 are implemented in .NET ThreadPool with delegates.</p>
+
+<h2>What about Stephen Toub's thread pool?</h2>
+
+<p>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 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.</p>
+
+<p>This thread pool doesn't comply with the requirements 1, 2, 3, 4, 5, 6, 8-25.</p>
+
+<h2>The Smart Thread Pool design and features</h2>
+
+<p>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.</p>
+
+<p>Features implementation:</p>
+
+<ol>
+<li><a name="Feature1"><strong>The thread pool is instantiable</strong></a>.</li>
+
+<p>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 a 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.</p>
+
+<p>To implement this feature, I just copied Toub's implementation and removed the <code lang="cs">static</code> keyword from the methods. That's the easy part of it.</p>
+
+<li><a name="Feature2"><strong>The number of threads dynamically changes</strong></a>.</li>
+
+<p>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.</p>
+
+<p>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?</p>
+
+<p>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:</p>
+
+<pre lang="text">_currentWorkItemsCount &gt; WorkerThreads</pre>
+
+<p>where <code>WorkerThreads</code> is the current number of threads in the pool, <code>InUseThreads</code> is the number of threads in the pool that are currently working on a work item, and <code>WaitingCallbacks</code> is the number of waiting work items. (Thanks to <strong>jrshute</strong> for the comment.)</p>
+
+<p>The <code>SmartThreadPool.Enqueue()</code> method looks like this:</p>
+
+<pre lang="cs">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 (_currentWorkItemsCount &gt; _workerThreads.Count) 
+    { 
+        StartThreads(1); 
+    } 
+}</pre>
+
+<p>When the number of threads reaches the upper limit, no more threads are created.</p>
+
+<p>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.</p>
+
+<p>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 (use 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.</p>
+
+<p>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?</p>
+
+<p>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.</p>
+
+<p>Have a look at the following scenario:</p>
+
+<p>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:</p>
+
+<table width="550" class="ArticleTable">
+<thead>
+<tr>
+<td>
+<p align="center">Work item arrival time (sec)</p>
+</td>
+
+<td>
+<p align="center">Work item work duration (sec)</p>
+</td>
+
+<td>
+<p align="center">Threads queue state</p>
+</td>
+
+<td>
+<p align="center">The thread that will execute the arrived work item</p>
+</td>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td align="center">
+<p align="center">00:00:00</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td width="113" align="center">
+<p align="center">A, B, C, D</p>
+</td>
+
+<td align="center">
+<p align="center">A</p>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<p align="center">00:00:01</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td width="113" align="center">
+<p align="center">B, C, D</p>
+</td>
+
+<td align="center">
+<p align="center">B</p>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<p align="center">00:00:02</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td width="113" align="center">
+<p align="center">C, D, A</p>
+</td>
+
+<td align="center">
+<p align="center">C</p>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<p align="center">00:00:03</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td width="113" align="center">
+<p align="center">D, A, B</p>
+</td>
+
+<td align="center">
+<p align="center">D</p>
+</td>
+</tr>
+</tbody>
+</table>
+
+<p>In this scenario, all the four threads are used, although two threads could handle all the work items.</p>
+
+<p>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.</p>
+
+<p>The previous scenario will look like this with the new implementation:</p>
+
+<table width="550" class="ArticleTable">
+<thead>
+<tr>
+<td>
+<p align="center">Work item arrival time (sec)</p>
+</td>
+
+<td>
+<p align="center">Work item work duration (sec)</p>
+</td>
+
+<td>
+<p align="center">Threads queue state</p>
+</td>
+
+<td>
+<p align="center">The thread that will execute the arrived work item</p>
+</td>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td align="center">
+<p align="center">00:00:00</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td align="center">
+<p align="center">A, B, C, D</p>
+</td>
+
+<td align="center">
+<p align="center">A</p>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<p align="center">00:00:01</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td align="center">
+<p align="center">B, C, D</p>
+</td>
+
+<td align="center">
+<p align="center">B</p>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<p align="center">00:00:02</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td align="center">
+<p align="center">A, C, D</p>
+</td>
+
+<td align="center">
+<p align="center">A</p>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<p align="center">00:00:03</p>
+</td>
+
+<td align="center">
+<p align="center">1.5</p>
+</td>
+
+<td align="center">
+<p align="center">B, C, D</p>
+</td>
+
+<td align="center">
+<p align="center">B</p>
+</td>
+</tr>
+</tbody>
+</table>
+
+<p>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 threads C and D will have to quit.</p>
+
+<p>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 the 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.</p>
+
+<p>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.</p>
+
+<p>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 quit. 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.</p>
+
+<li lang="cs"><a name="Feature3"><strong>Work items return a value</strong></a> (this feature is enhanced).</li>
+
+<p>This feature is very useful in cases where you want to know the result of a work item.</p>
+
+<p>The .NET ThreadPool supports this feature via delegates. Each time you create a delegate, you get the <code>BeginInvoke()</code> and <code>EndInvoke()</code> methods for free. <code>BeginInvoke()</code> queues the method and its parameters on the .NET ThreadPool, and <code>EndInvoke()</code> returns the result of the method. The delegate class is <code lang="cs">sealed</code>, so I couldn't override the <code>BeginInvoke()</code> and <code>EndInvoke()</code> methods. I took a different approach to implement this.</p>
+
+<p>First, the work item callback delegate can return a value:</p>
+
+<pre lang="cs">public delegate object WorkItemCallback(object state);</pre>
+
+<p>Or in its enhanced form, the callback can be any of the following forms:</p>
+
+<pre lang="cs">public delegate void Action(); 
+public delegate void Action&lt;T&gt;(T arg); 
+public delegate void Action&lt;T1, T2&gt;(T1 arg1, T2 arg2); 
+public delegate void Action&lt;T1, T2, T3&gt;(T1 arg1, 
+                     T2 arg2, T3 arg3); 
+public delegate void Action&lt;T1, T2, T3, T4&gt;(T1 arg1, 
+                     T2 arg2, T3 arg3, T4 arg4); 
+
+public delegate TResult Func(); 
+public delegate TResult Func&lt;T&gt;(T arg1); 
+public delegate TResult Func&lt;T1, T2&gt;(T1 arg1, T2 arg2); 
+public delegate TResult Func&lt;T1, T2, T3&gt;(T1 arg1, 
+                        T2 arg2, T3 arg3); 
+public delegate TResult Func&lt;T1, T2, T3, T4&gt;(T1 arg1, 
+                        T2 arg2, T3 arg3, T4 arg4);</pre>
+
+<p>(Note that the above delegates are defined in .NET 3.5. In .NET 2.0 &amp; 3.0, only <code lang="cs">public delegate void Action&lt;T&gt;(T arg)</code> is defined.) Second, the <code>SmartThreadPool.QueueWorkItem()</code> method returns a reference to an object that implements the <code>IWorkItemResult&lt;TResult&gt;</code> interface. The caller can use this object to get the result of the work item. The interface is similar to the <code>IAsyncResult</code> interface:</p>
+
+<pre lang="cs">public interface IWorkItemResult&lt;TResult&gt;
+{ 
+    /// 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<span class="code-SummaryComment">&lt;...&gt;
+</span>    /// or Func<span class="code-SummaryComment">&lt;...&gt; the State value
+</span>    /// 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.<span class="code-SummaryComment">&lt;/param&gt;
+</span>    /// 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; }
+}</pre>
+
+<p>If the work item callback is <code lang="cs">object WorkItemCallback(object state)</code>, then <code>IWorkItemResult</code> is returned, and <code>GetResult()</code> returns <code lang="cs">object</code>. Same as in previous versions.</p>
+
+<p>If the work item callback is one of the <code>Func&lt;...&gt;</code> methods I mentioned above, the result of <code>QueueWorkItem</code> is <code>IWorkItemResult&lt;TResult&gt;</code>. So, the result of the work item is strongly typed.</p>
+
+<p>If the work item callback is one of the <code>Action&lt;...&gt;</code> methods I mentioned above, the result of <code>QueueWorkItem</code> is <code>IWorkItemResult</code> and <code>GetResult()</code> always returns <code lang="cs">null</code>.</p>
+
+<p>If the work item callback is <code>Action&lt;...&gt;</code> or <code>Func&lt;...&gt;</code> and <code>WIGStartInfo.FillStateWithArgs</code> is set to <code lang="cs">true</code>, then the <code>State</code> of the <code>IWorkItemResult</code> is initialized with a <code lang="cs">object []</code> that contains the work item arguments. Otherwise, the <code>State</code> is <code lang="cs">null</code>.</p>
+
+<p>The <a href="#SimpleExample">code examples</a> in the section below show some snippets of how to use it.</p>
+
+<p>To get the result of the work item, use the <code>Result</code> property or the <code>GetResult()</code> 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 <code>GetResult()</code> 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:</p>
+
+<table width="550" class="ArticleTable">
+<thead>
+<tr>
+<td width="266">GetResult() return reason</td>
+
+<td width="344">GetResult() return value</td>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td>The work item has been executed and completed.</td>
+
+<td>The result of the work item.</td>
+</tr>
+
+<tr>
+<td height="39">The work item has been canceled.</td>
+
+<td height="39">Throws <code>WorkItemCancelException</code>.</td>
+</tr>
+
+<tr>
+<td height="39">The timeout expired.</td>
+
+<td height="39">Throws <code>WorkItemTimeoutException</code>.</td>
+</tr>
+
+<tr>
+<td>The <code>cancelWaitHandle</code> is signaled.</td>
+
+<td>Throws <code>WorkItemTimeoutException</code>.</td>
+</tr>
+
+<tr>
+<td>The work item threw an exception.</td>
+
+<td>Throws <code>WorkItemResultException</code> with the work item's exception as the inner exception.</td>
+</tr>
+</tbody>
+</table>
+
+<p>There are two ways to wait for a single work item to complete:</p>
+
+<ol>
+<li>The following function uses the <code>GetResult()</code> method which blocks the caller until the result is available:</li>
+
+<pre lang="cs">private void WaitForResult1(IWorkItemResult wir)
+{
+    wir.GetResult();
+}</pre>
+
+<li>The following function is not recommended, because it uses a busy wait loop. You can use it if you know what you are doing:</li>
+
+<pre lang="cs">private void WaitForResult2(IWorkItemResult wir)
+{
+    while(!wir.IsCompleted)
+    {
+        Thread.Sleep(100);
+    }
+}</pre>
+</ol>
+
+<li><a name="Feature4"><strong>The caller can wait for multiple work items to complete</strong></a>.</li>
+
+<p>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 <code>SmartThreadPool</code> class has two static methods for this: <code>WaitAny()</code> and <code>WaitAll()</code> (they have several overloads). Their signature is similar to the <code>WaitHandle</code> equivalent methods except that in the <code>SmartThreadPool</code> case, it gets an array of <code>IWaitableResult</code> (the <code>IWorkItemResult</code> interface inherits from <code>IWaitableResult</code>) objects instead of <code>WaitHandle</code> objects.</p>
+
+<p>The following snippets show how to wait for several work item results at once. Assume <code>wir1</code> and <code>wir2</code> are of type <code>IWorkItemResult</code>. You can wait for both work items to complete:</p>
+
+<pre lang="cs">// Wait for both work items complete 
+SmartThreadPool.WaitAll(new IWaitableResult[] { wir1, wir2});</pre>
+
+<p>Or, for any of the work items to complete:</p>
+
+<pre lang="cs">// Wait for at least one of the work items complete 
+SmartThreadPool.WaitAny(new IWaitableResult[] { wir1, wir2});</pre>
+
+<p>The <code>WaitAll()</code> and <code>WaitAny()</code> methods are overloaded, so you can specify the timeout, the exit context, and <code>cancelWaitHandle</code> (just like in the <code>GetResult()</code> method mentioned earlier).</p>
+
+<p><strong>Note</strong> that in order to use <code>WaitAny()</code> and <code>WaitAll()</code>, you need to work in MTA, because internally, I use <code>WaitHandle.WaitAny()</code> and <code>WaitHandle.WaitAll()</code> which require it. If you don't do that, the methods will throw an exception to remind you.</p>
+
+<p><strong>Also note</strong> that Windows supports <code>WaitAny()</code> of up to 64 handles. <code>WaitAll()</code> is more flexible, and I re-implemented it so it is not limited to 64 handles.</p>
+
+<p>See in the examples section below, the code snippets for <a href="#WaitForAllExample"><code>WaitAll</code></a> and <a href="#WaitForAnyExample"><code>WaitAny</code></a>.</p>
+
+<li><a name="Feature5"><strong>A work item can be cancelled</strong></a> (this feature is enhanced).</li>
+
+<p>This feature enables to cancel work items.</p>
+
+<p>There are several options to cancel work items. To cancel a single work item, call <code>IWorkItemResult.Cancel()</code>. To cancel more than one, call <code>IWorkItemsGroup.Cancel()</code> or <code>SmartThreadPool.Cancel()</code>. All cancels works in O(1).</p>
+
+<p>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 that the work item's state I mention here has nothing to do with the state object argument provided in the <code>QueueWorkItem</code>.)</p>
+
+<p>These are the possible states of a work item (defined in the <code>WorkItemState</code> enum):</p>
+
+<ul class="enum">
+<li><code>Queued</code>  The work item is waiting in a queue to be executed.</li>
+
+<li><code>InProgress</code>  A thread from the pool is executing the work item.</li>
+
+<li><code>Completed</code>  The work item execution has been completed.</li>
+
+<li><code>Cancelled</code>  The work item has been cancelled.</li>
+</ul>
+
+<p>The cancel behavior depends on the state of the work item.</p>
+
+<table class="ArticleTable">
+<thead>
+<tr>
+<td width="140" align="center">Initial State</td>
+
+<td width="99" align="center">Next State</td>
+
+<td width="332" align="center">Notes</td>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td><code>Queued</code></td>
+
+<td><code>Cancelled</code></td>
+
+<td>A queued work item becomes cancelled and is not executed at all.</td>
+</tr>
+
+<tr>
+<td><code>InProgress</code></td>
+
+<td><code>Cancelled</code></td>
+
+<td>An executing work item becomes cancelled even if it completes its execution!</td>
+</tr>
+
+<tr>
+<td><code>Completed</code></td>
+
+<td><code>Completed</code></td>
+
+<td>A completed work item stays completed.</td>
+</tr>
+
+<tr>
+<td><code>Cancelled</code></td>
+
+<td><code>Cancelled</code></td>
+
+<td>A cancelled work item stays cancelled.</td>
+</tr>
+</tbody>
+</table>
+
+<p>A cancelled work item throws a <code>WorkItemCancelException</code> when its <code>GetResult()</code> method is called.</p>
+
+<p>The behavior of <code>Cancel()</code> when the work item is in <code>Completed</code> or <code>Cancelled</code> states is straightforward, so I won't get into the details. A queued work item is marked as cancelled and is discarded once a thread from the pool dequeues it.</p>
+
+<p>If a work item is in the <code>InProgress</code> state, then the behavior depends on the value of <code>abortExecution</code> in the <code>Cancel</code> call. When <code>abortExecution</code> is <code lang="cs">true</code>, a <code>Thread.Abort()</code> will be called upon the executing thread. When <code>abortExecution</code> is <code lang="cs">false</code>, the work item method is responsible to sample the <code>SmartThreadPool.IsWorkItemCanceled</code> static method and quit. Note that, in both cases, the work item is cancelled, and throws a <code>WorkItemCancelException</code> on <code>GetResult()</code>.</p>
+
+<p>Here is an example of a cooperative work item:</p>
+
+<pre lang="cs">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
+    }
+}</pre>
+
+<li><a name="Feature6"><strong>The caller thread's context is used when the work item is executed (limited)</strong></a>.</li>
+
+<p>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 <code>CompressedStack</code> 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:</p>
+
+<ul>
+<li><code>CurrentCulture</code> - The culture of the thread.</li>
+
+<li><code>CurrentUICulture</code> - The culture used by the resource manager to look up culture-specific resources at run time.</li>
+
+<li><code>CurrentPrincipal</code> - The current principal (for role-based security).</li>
+
+<li><code>CurrentContext</code> - The current context in which the thread is executing (used in remoting).</li>
+</ul>
+
+<p>The first three belong to the <code>System.Threading.Thread</code> 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.</p>
+
+<p>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 <code>CallerThreadContext</code>, and it is used internally. When Microsoft unblocks the protection on the <code>CompressedStack</code>, I will add it there.</p>
+
+<p>The caller thread's context is stored when the work item is created, within the <code>EnqueueWorkItem()</code> method. Each time a thread from the pool executes a work item, the thread's context changes in the following order:</p>
+
+<ol>
+<li>The current thread context is captured.</li>
+
+<li>The caller thread context is applied.</li>
+
+<li>The work item is executed.</li>
+
+<li>The current old thread context is restored.</li>
+</ol>
+
+<li><strong>Usage of <a name="Feature7">minimum number of Win32 event handles, so the handle count of the application won't explode</a></strong>.</li>
+
+<p>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 <code>Close()</code> method of the <code>ManualResetEvent</code> 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.</p>
+
+<p>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 <code>WaitHandle</code>, but use them internally and then close them.</p>
+
+<p>In order to create fewer handles, I created the <code>ManualResetEvent</code> objects only when the user asks for them (lazy creation). For example, if you don't use the <code>GetResult()</code> of the <code>IWorkItemResult</code> interface, then a handle is not created. Using <code>SmartThreadPool.WaitAll()</code> and <code>SmartThreadPool.WaitAny()</code> creates a handle.</p>
+
+<p>The work item queue creates a lot of handles since each new wait for a work item creates a new <code>ManualResetEvent</code>. 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 <code>ManualResetEvent</code> 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.</p>
+
+<li><a name="Feature8"><strong>Work item can have a PostExecute callback. This is a method that will be called right after the work item execution has been completed</strong></a>.</li>
+
+<p>A <code>PostExecute</code> 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 executes the work item. The user can choose the cases in which <code>PostExecute</code> is called. The options are represented in the <code>CallToPostExecute</code> flagged enumerator:</p>
+
+<pre lang="cs">[Flags]
+public enum CallToPostExecute
+{
+    Never                    = 0x00,
+    WhenWorkItemCanceled     = 0x01,
+    WhenWorkItemNotCanceled  = 0x02,
+    Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
+}</pre>
+
+<p>Explanation:</p>
+
+<ul class="enum">
+<li><code>Never</code>  Don't run <code>PostExecute</code>.</li>
+
+<li><code>WhenWorkItemCanceled</code> - Run <code>PostExecute</code> only when the work item has been canceled.</li>
+
+<li><code>WhenWorkItemNotCanceled</code> - Run <code>PostExecute</code> only when the work item has not been canceled.</li>
+
+<li><code>Always</code>  Always run <code>PostExecute</code>.</li>
+</ul>
+
+<p><code>SmartThreadPool</code> has a default <code>CallToPostExecute</code> value of <code>CallToPostExecute.Always</code>. This can be changed during the construction of <code>SmartThreadPool</code> in the <code>STPStartInfo</code> class argument. Another way to give the <code>CallToPostExecute</code> value is in one of the <code>SmartThreadPool.QueueWorkItem</code> overloads. Note that, as opposed to the <code>WorkItem</code> execution, if an exception has been thrown during <code>PostExecute</code>, then it is ignored. <code>PostExecute</code> is a delegate with the following signature:</p>
+
+<pre lang="cs">public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);</pre>
+
+<p>As you can see, <code>PostExecute</code> receives an argument of type <code>IWorkItemResult</code>. It can be used to get the result of the work item, or any other information made available by the <code>IWorkItemResult</code> interface.</p>
+
+<li><a name="Feature9"><strong>The user can choose to automatically dispose off the state object that accompanies the work item</strong></a>.</li>
+
+<p>When the user calls <code>QueueWorkItem</code>, he/she can provide a state object. The state object usually stores specific information, such as arguments that should be used within the <code>WorkItemCallback</code> delegate.</p>
+
+<p>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.</p>
+
+<p>For this reason, I added a boolean to <code>SmartThreadPool</code> that indicates to call <code>Dispose</code> on the state object when the work item has been completed. The boolean is initialized when the thread pool is constructed with <code>STPStartInfo</code>. <code>Dispose</code> is called only if the state object implements the <code>IDisposable</code> interface. <code>Dispose</code> is called after the <code>WorkItem</code> has been completed and its <code>PostExecute</code> has run (if a <code>PostExecute</code> exists). The state object is disposed even if the work item has been canceled or the thread pool has been shutdown.</p>
+
+<p><strong>Note</strong> that this feature only applies to the <code>state</code> argument that comes with <code>WorkItemCallback</code>, it doesn't apply to the arguments supplied in <code>Action&lt;...&gt;</code> and <code>Func&lt;...&gt;</code>!!!</p>
+
+<li><a name="Feature10"><strong>The user can wait for the Smart Thread Pool / Work Items Group to become idle. </strong></a>
+<p>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.</p>
+
+<p>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 <code>IWorkItemResult</code> objects in case you just want to wait for all of the work items to complete.</p>
+
+<p>The <code>SmartThreadPool</code> and WorkItemsGroup classes both implement the <code>IWorkItemsGroup</code> interface which defines the <code>WaitForIdle</code> methods.</p>
+
+<p>To take advantage of this feature, use the <code>IWorkItemsGroup.WaitForIdle()</code> method (both <code>SmartThreadPool</code> and WorkItemsGroup implement the <code>IWorkItemsGroup</code> interface which defines the <code>WaitForIdle</code> methods). It has several overloads which provide a timeout argument. The <code>WaitForIdle()</code> method is not <code lang="cs">static</code>, and should be used on a <code>SmartThreadPool</code> instance.</p>
+
+<p><code>SmartThreadPool</code> 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.</p>
+
+<p>The <code>WaitForIdle()</code> mechanism works with a private <code>ManualResetEvent</code>. When a work item is queued, the <code>ManualResetEvent</code> is reset (changed to non-signaled state). When the work items count becomes zero (initial state of the Smart Thread Pool), the <code>ManualResetEvent</code> is set (changed to signaled state). The <code>WaitForIdle()</code> method just waits for the <code>ManualResetEvent</code> to implement its functionality.</p>
+
+<p>See the <a href="#WaitForIdleExample">example</a> below.</p>
+</li>
+
+<li><a name="Feature11"><strong>Exception handling has been changed, so if a work item throws an exception, it is rethrown at <code>GetResult()</code>, rather than firing an <code>UnhandledException</code> event. Note that <code>PostExecute</code> exceptions are always ignored</strong></a>. 
+<p>After I did some reading about delegates and their implementation, I decided to change the way <code>SmartThreadPool</code> treats exceptions. In the previous versions, I used an event driven mechanism. Entities were registered to the <code>SmartThreadPool.UnhandledException</code> event, and when a work item threw an exception, this event was fired. This is the behavior of Toubs thread pool.</p>
+
+<p>.NET delegates behave differently. Instead of using an event driven mechanism, it re-throws the exception of the delegated method at <code>EndInvoke()</code>. Similarly, the <code>SmartThreadPool</code> exception mechanism is changed so that exceptions are no longer fired by the <code>UnhandledException</code> event, but rather re-thrown again when <code>IWorkItemResult.GetResult()</code> is called.</p>
+
+<p>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 <code>GetResult()</code> 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.</p>
+
+<p><code>GetResult()</code> can be called unlimited number of times, and it re-throws the same exception each time.</p>
+
+<p>Note that <code>PostExecute</code> is called as and when needed, even if the work item has thrown an exception. Of course, the <code>PostExecute</code> implementation should handle exceptions if it calls <code>GetResult()</code>.</p>
+
+<p>Also note that if <code>PostExecute</code> throws an exception, then its exception is ignored.</p>
+
+<p>See the <a href="#CatchExceptionExample">example</a> below.</p>
+</li>
+
+<li><a name="Feature12"><strong>Work items have priority</strong></a>.</li>
+
+<p>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:</p>
+
+<pre lang="cs">public enum WorkItemPriority 
+{ 
+    Lowest, 
+    BelowNormal, 
+    Normal, 
+    AboveNormal, 
+    Highest,
+}</pre>
+
+<p>The default priority is <code>Normal</code>.</p>
+
+<p>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.</p>
+
+<p>This is the easiest solution to sort the work items.</p>
+
+<li><a name="Feature13"><strong>The caller thread's HTTP context can be used when the work item is executed</strong></a>.</li>
+
+<p>This feature improves 6, and was implemented by Steven T. I just replaced my code with that implementation.</p>
+
+<p>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.</p>
+
+<li><a name="Feature14"><strong>Work items group</strong></a>.</li>
+
+<p>This feature enables the user to execute a group of work items specifying the maximum level of concurrency.</p>
+
+<p>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 latter solution is too expensive (many threads) if the resources are idle most of the time.</p>
+
+<p>The Smart Thread Pool solution is to create a WorkItemsGroup per resource, with a 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 the work items.</p>
+
+<p>Here is a code snippet to show how this works:</p>
+
+<pre lang="cs">...
+
+// 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) {...}
+
+...</pre>
+
+<p>As you can see from the snippet, a WorkItemsGroup 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.</p>
+
+<p>The WorkItemsGroup has a priority queue (the same as the <code>SmartThreadPool</code>). 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 <code>SmartThreadPool</code> with the same priority.</p>
+
+<p>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 <code>SmartThreadPool</code>. If this number is less than the maximum level of concurrency, it queues the work item into the <code>SmartThreadPool</code>. If this number is equal (it cannot be greater), then the WorkItemsGroup stores the work item in its own priority queue.</p>
+
+<p>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 <code>SmartThreadPool</code> up to the maximum level of concurrency.</p>
+
+<p>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 <code>SmartThreadPool</code> queue.</p>
+
+<p>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 <code>SmartThreadPool</code>. The event is the only way to accomplish the concurrency level. When I tried to do it with <code>PostExecute</code>, I got fluctuating <code>WaitForIdle</code>.</p>
+
+<p>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 (<code>InQueue</code>, <code>InProgress</code>, <code>Completed</code>, or <code>Canceled</code>). The final state considers this object's value to know if the work item was cancelled.</p>
+
+<p>The Work Items Group can also be used as a conjunction point. Say, you want to accomplish a task by splitting it to subtasks. Once the subtasks are completed, a new task is issued and split to subtasks too. This can be achieved by using the <code>OnIdle</code> event of the WorkItemsGroup. See the <a href="#WorkItemsGroupExample">examples</a> below.</p>
+
+<p>See the WorkItemsGroupDemo demo in the source code solution.</p>
+
+<p><img width="550" height="648" src="WorkItemsGroup.jpg" /></p>
+
+<li><a name="Feature15"><strong>The caller can create thread pools and work item groups in suspended state</strong></a>.</li>
+
+<p>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.</p>
+
+<p>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 <code>Start()</code> method. The same method exists in the Work Items Group for the same purpose.</p>
+
+<p>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.</p>
+
+<li><a name="Feature16"><strong>Threads have priority</strong></a>.</li>
+
+<p><code>STPStartInfo</code> contains a property that defines the priority in which the threads are started in the <code>SmartThreadPool</code>. Use it if you know what you are doing. Playing with threads priority may end up with dead locks, live lock, and days locked <img align="top" alt="Frown | :-(" src="http://www.codeproject.com/script/Forums/Images/smiley_frown.gif" /> .</p>
+
+<li><a name="Feature17"><strong><code>MaxThreads</code>/<code>MinThreads</code>/<code>Concurrency</code> can be changed at run time</strong></a>.</li>
+
+<p>This addition allows the user to control the concurrency of work items execution. It is useful to make the STP adaptable.</p>
+
+<p>To execute more work items in parallel, increment the concurrency. To limit the number of executed work items and/or lower the CPU usage, decrement the concurrency.</p>
+
+<p>This option is available in the <code>SmartThreadPool</code> and in the WorkItemsGroup with the <code>IWokItemsGroup</code> interface:</p>
+
+<pre lang="cs">public interface IWorkItemsGroup
+{
+    ...
+    int Concurrency { get; set; }
+    ...
+}</pre>
+
+<p>The value of <code>Concurrency</code> must be positive.</p>
+
+<p>Although <code>Concurrency</code> has the same meaning and the same behavior as in the <code>SmartThreadPool</code> and in the WorkItemsGroup, its implemented differently.</p>
+
+<p><code>SmartThreadPool</code>s <code>Concurrency</code> is equivalent to the <code>MaxThreads</code> property. When <code>Concurrency</code> is incremented, the <code>SmartThreadPool</code> can create more threads to handle its work items up to the <code>Concurrency</code> limit. The creation of thread, in this case, is immediate. The threads are still created as explained in <a href="#Feature2">section 2</a>.</p>
+
+<p>When <code>Concurrency</code> is decremented, the <code>SmartThreadPool</code> doesnt create new threads and let existing threads to be terminated in order to decrement the number of threads in the thread pool. Note that the lowering of <code>Concurrency</code> may take a while to take effect, since we need to wait for work items to complete. <code>SmartThreadPool</code> doesnt abort a thread actively, but waits passively until it quits.</p>
+
+<p>WorkItemsGroups <code>Concurrency</code> is responsible for how many work items may be handled in parallel in the <code>SmartThreadPool</code>, as explained in <a href="#Feature14">section 14</a>. When <code>Concurrency</code> is incremented, more work items are queued to the <code>SmartThreadPool</code>. When <code>Concurrency</code> is decremented, the WorkItemsGroup stops to queue work items until the number of work items in the <code>SmartThreadPool</code> of this WorkItemsGroup is lower than the WorkItemsGroups <code>Concurrency</code>.</p>
+
+<p>In addition, <code>SmartThreadPool</code> also lets the <code>MinThreads</code> property to be changed after its creation. When <code>MinThreads</code> is created, the number of threads in the pool is raised so it will be at least <code>MinThreads</code>.</p>
+
+<p>The number of <code>MaxThreads</code> must be greater or equal to <code>MinThreads</code>. If <code>MaxThreads</code> is set to a number lower than <code>MinThreads</code>, then <code>MinThreads</code> is also set to the new value of <code>MaxThreads</code>. And, vice versa for <code>MinThreads</code>.</p>
+
+<li><a name="Feature18"><strong>Improved Cancel behavior</strong></a>.</li>
+
+<p><a href="#Feature5">(See section 5)</a>.</p>
+
+<li><a name="Feature19"><strong>Threads initialization and termination generate events</strong></a>.</li>
+
+<p>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 <code>SmartThreadPool</code> class:</p>
+
+<pre lang="cs">// 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;
+{...}</pre>
+
+<p>The <code>OnThreadInitialization</code> 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 code to initialize resources that are used by the thread, and should be initialized once per thread instead of once per work item.</p>
+
+<p>The <code>OnThreadTermination</code> 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 <code>OnThreadInitialization</code> event.</p>
+
+<li><a name="Feature20"><strong>Supports Windows CE (limited)</strong>.</a></li>
+
+<p>The SmartThreadPool project has a similar project named SmartThreadPoolCE. This version of the SmartThreadPool can be run on Windows CE.</p>
+
+<p>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.</p>
+
+<p><img width="346" height="586" src="WindowsCE.jpg" /></p>
+
+<li><a name="Feature21"><strong>SmartThreadPool and IWorkItemsGroup has the IsIdle flag</strong></a>.</li>
+
+<p>This flag enables the user to poll the Smart Thread Pool / Work Items Group if they are idle.</p>
+
+<li><a name="Feature22"><strong>Supports Action&lt;T&gt; and Func&lt;T&gt; (strongly typed work items)</strong></a>.</li>
+
+<p><a href="#Feature3">(See section 3)</a>.</p>
+
+<li><a name="Feature23"><strong>Supports Silverlight</strong></a>.</li>
+
+<p>Add a reference to <em>SmartThreadPoolSL.dll</em> in your code and use it.</p>
+
+<p><img width="550" height="641" src="Silverlight.jpg" /></p>
+
+<li><a name="Feature24"><strong>Supports Mono</strong></a>.</li>
+
+<p>Add a reference to <em>SmartThreadPoolMono.dll</em> in your code and use it.</p>
+
+<p>Note that the binaries of Mono were compiled on Windows with Visual Studio 2008.</p>
+
+<p><img width="550" height="344" src="Mono.jpg" /></p>
+
+<li><a name="Feature25"><strong>Internal performance counters</strong></a>.</li>
+
+<p>The internal performance counter should be used on platforms that don't support Performance Counters, such as Windows CE, Silverlight, and Mono.</p>
+
+<p>The internal performance counters are variables inside STP that collect the data. To enable them, set <code>STPStartInfo.EnableLocalPerformanceCounters</code> to <code lang="cs">true</code>. I use this feature in the new demos (WindowsCE, Silverlight, and Mono).</p>
+
+<li><a name="Feature26"><strong>Join, Choice, and Pipe</strong></a>.</li>
+
+<p>The new methods are added to the <code>SmartThreadPool</code> class and implemented using WorkItemsGroup. Their purpose is to simplify the initiation of a parallel task.</p>
+
+<ul>
+<li><code>Join</code> - Executes several work items and waits for all of them to complete <a href="#JoinExample">(Join example)</a>.</li>
+
+<li><code>Choice</code> - Executes several work items and returns when the first one completes <a href="#ChoiceExample">(Choice example)</a>.</li>
+
+<li><code>Pipe</code> - Executes several work items sequentially and waits until the last work item completes <a href="#PipeExample">(Pipe example)</a>.&nbsp;&nbsp;&nbsp;&nbsp;</li></ul><li><a name="Feature27"><strong>Work item timeout (passive)</strong></a>.&nbsp;</li>
+
+<p>This feature lets the user specify a timeout for the work item to complete, in milliseconds. When the timeout expires, the work item is cancelled automatically if it didn't complete. The cancel works the same as a call to <code>Cancel</code> with the <code>abortExecution</code> argument set to <code lang="cs">false</code> (this is why the timeout is passive).</p>
+
+<p>To sample the cancel, use <code>SmartThreadPool.IsWorkItemCanceled</code> which is a static property, or you can use <code>SmartThreadPool.AbortOnWorkItemCancel</code> which checks if the current work item is cancelled, and if so, abort the thread (<code>Thread.CurrentThread.Abort()</code>).</p>
+
+<p>See the timeout <a href="#TimeoutExample">example</a> below.</p>
+<li><a name="Feature28"><strong>Enabled to set threads IsBackground</strong></a></li>
+<p>This featue enables to set the <code>IsBackground</code> of the STP threads. The default is <code>true</code>.</p>
+<p>To use it:<br />
+	        </p><pre lang="cs">STPStartInfo stpStartInfo = new STPStartInfo();
+stpStartInfo.AreThreadsBackground = false; 
+SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+</pre>
+<p>
+
+</p><li><a name="Feature29"><strong>Enabled to set threads ApartmentState</strong></a></li>
+<p>This featue enables to set the <code>ApartmentState</code> of the STP threads. The default is not to set.</p>
+<p>To use it:<br />
+</p><pre lang="cs">STPStartInfo stpStartInfo = new STPStartInfo();
+stpStartInfo.ApartmentState = ApartmentState.MTA;
+SmartThreadPool stp = new SmartThreadPool(stpStartInfo);</pre><ol><p>
+</p>
+</ol>
+<li><a name="Feature30"><strong>Support Windows Phone (limited)</strong></a></li>
+<p>Add reference to SmartThreadPoolWP.dll in your code and use it.</p>
+<p><img width="404" height="749" src="WindowsPhone.png" /></p>
+
+<li><a name="Feature31"><strong>Enabled to set thread MaxStackSize</strong></a></li>
+<p>With this feature the user is able to set the MaxStackSize of the threads in the thread pool</p>
+<p>To use it set the MaxStackSize member in the STPStartInfo.</p>
+<p>
+</p><pre lang="cs">STPStartInfo stpStartInfo = new STPStartInfo();
+stpStartInfo.MaxStackSize = 1024*1024;
+SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+</pre>
+<p>
+</p></ol>
+
+<h2>When to use?</h2>
+
+<p>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.</p>
+
+<h2>How to use?</h2>
+
+<p>When the Smart Thread Pool or Work Items Group is created, it requires a few parameters; when a value is not provided, a default value is used.</p>
+
+<table class="ArticleTable">
+<thead>
+<tr>
+<td>Value name</td>
+
+<td>Default value</td>
+
+<td>Smart Thread Pool</td>
+
+<td>Work Items Group</td>
+
+<td>Description</td>
+</tr>
+</thead>
+
+<tbody>
+<tr>
+<td><code>IdleTimeout</code></td>
+
+<td>60 seconds</td>
+
+<td>Used</td>
+
+<td>Not used</td>
+
+<td>Idle timeout</td>
+</tr>
+
+<tr>
+<td><code>MaxWorkerThreads</code></td>
+
+<td>25</td>
+
+<td>Used</td>
+
+<td>Not used</td>
+
+<td>Maximum number of threads</td>
+</tr>
+
+<tr>
+<td><code>MinWorkerThreads</code></td>
+
+<td>0</td>
+
+<td>Used</td>
+
+<td>Not used</td>
+
+<td>Minimum number of threads</td>
+</tr>
+
+<tr>
+<td><code>UseCallerCallContext</code></td>
+
+<td><code lang="cs">false</code></td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td>Use caller thread call context</td>
+</tr>
+
+<tr>
+<td><code>UseCallerHttpContext</code></td>
+
+<td><code lang="cs">false</code></td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td>Use caller thread HTTP context</td>
+</tr>
+
+<tr>
+<td><code>DisposeOfStateObjects</code></td>
+
+<td><code lang="cs">false</code></td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td>Dispose of the state objects (if the state implements <code lang="cs">IDisposable</code>)</td>
+</tr>
+
+<tr>
+<td><code>CallToPostExecute</code></td>
+
+<td><code class="SmallText">CallToPostExecute.Always</code></td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td>Call to <code>PostExecute</code></td>
+</tr>
+
+<tr>
+<td><code class="SmallText">PostExecuteWorkItemCallback</code></td>
+
+<td><code lang="cs">null</code> (Do nothing)</td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td><code>PostExecute</code> method</td>
+</tr>
+
+<tr>
+<td><code>StartSuspended</code></td>
+
+<td><code lang="cs">false</code></td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td>Start suspended</td>
+</tr>
+
+<tr>
+<td><code lang="cs">FillStateWithArgs</code></td>
+
+<td><code lang="cs">false</code></td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td>Fill state with args (<code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code>)</td>
+</tr>
+
+<tr>
+<td><code lang="cs">ThreadPriority</code></td>
+
+<td><code class="SmallText">ThreadPriority.Normal</code></td>
+
+<td>Used</td>
+
+<td>Not used</td>
+
+<td>Thread priority in thread pool (not supported in Mono)</td>
+</tr>
+
+<tr>
+<td><code lang="cs">WorkItemPriority</code></td>
+
+<td><code class="SmallText">WorkItemPriority.Normal</code></td>
+
+<td>Used</td>
+
+<td>Used</td>
+
+<td>Work item default priority</td>
+</tr>
+
+<tr>
+<td><code class="SmallText">PerformanceCounterInstanceName</code></td>
+
+<td><code lang="cs">null</code></td>
+
+<td>Used</td>
+
+<td>Not used</td>
+
+<td>Performance counter instance name</td>
+</tr>
+
+<tr>
+<td><code class="SmallText">EnableLocalPerformanceCounters</code></td>
+
+<td><code lang="cs">false</code></td>
+
+<td>Used</td>
+
+<td>Not used</td>
+
+<td>Enable local performance counters (for platforms which don't support performance counters)</td>
+</tr>
+</tbody>
+</table>
+
+<p>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.</p>
+
+<h2>Code examples</h2>
+
+<p>Creating a Smart Thread Pool instance:</p>
+
+<pre lang="cs">SmartThreadPool smartThreadPool = 
+ new SmartThreadPool(
+    10*1000,    // Idle timeout in milliseconds
+    25,         // Threads upper limit
+    5,          // Threads lower limit
+    true);      // Use caller thread context</pre>
+
+<p>Another way to create an instance:</p>
+
+<pre lang="cs">// 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);</pre>
+
+<h2>Using the Smart Thread Pool</h2>
+
+<p>The following snippet is a simple example. The user queues a work item and then gets the result. Note that the <code>Result</code> property blocks until a result is available or the work item is cancelled:<a name="SimpleExample"></a></p>
+
+<pre lang="cs">public class SimpleExample
+{
+    public void DoWork(int [] numbers) 
+    { 
+        SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+        // Queue the work item
+        IWorkItemResult&lt;double&gt; wir = smartThreadPool.QueueWorkItem(
+                    new Func&lt;int[], double&gt;(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;
+    }
+}</pre>
+
+<p>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:<a name="WaitForAllExample"></a></p>
+
+<pre lang="cs">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;
+    }
+}</pre>
+
+<p>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<a name="WaitForAnyExample"></a>:</p>
+
+<pre lang="cs">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;
+    }
+}</pre>
+
+<p>The following example shows the use of <code>WaitForIdle()</code>. 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:<a name="WaitForIdleExample"></a></p>
+
+<pre lang="cs">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;
+    }
+}</pre>
+
+<p>The following example shows how to handle exceptions. Pay attention to the <code>Result</code> property that throws the <code>WorkItemResultException</code> and not the real exception:<a name="CatchExceptionExample"></a></p>
+
+<pre lang="cs">public class CatchExceptionExample
+{
+    public void DoWork() 
+    { 
+        SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+        IWorkItemResult&lt;double&gt; wir = smartThreadPool.QueueWorkItem(
+                  new Func&lt;double, double, double&gt;(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;
+    }
+}</pre>
+
+<p>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:<a name="GetExceptionExample"></a></p>
+
+<pre lang="cs">public class GetExceptionExample
+{
+    public void DoWork() 
+    { 
+        SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+        IWorkItemResult&lt;double&gt; wir = smartThreadPool.QueueWorkItem(
+                   new Func&lt;double, double, double&gt;(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;
+    }
+}</pre>
+
+<p>The next example shows how to create a Work Items Group and use it:<a name="WorkItemsGroupExample"></a></p>
+
+<pre lang="cs">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;
+    }
+}</pre>
+
+<p>The next example shows how to create a suspended Smart Thread Pool:<a name="SuspendedSTPStartExample"></a></p>
+
+<pre lang="cs">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;
+    }
+}</pre>
+
+<p>The next example shows how to create a suspended Work Items Group:<a name="SuspendedWIGStartExample"></a></p>
+
+<pre lang="cs">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;
+    }
+}</pre>
+
+<p>This example shows how to get the Work Items Group's <code>OnIdle</code> event:<a name="OnWIGIdleEventExample"></a></p>
+
+<pre lang="cs">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(&quot;WIG is idle&quot;);
+    }
+}</pre>
+
+<p>This example shows how to use <code>Join</code>:<a name="JoinExample"></a></p>
+
+<pre lang="cs">public class JoinExample
+{
+    public void DoWork() 
+    { 
+        SmartThreadPool stp = new SmartThreadPool();
+
+        stp.Join(DoSomeWork1, DoSomeWork2);
+
+        smartThreadPool.Shutdown();
+    } 
+
+    private void DoSomeWork1()
+    { 
+        // ...
+    }
+
+    private void DoSomeWork2()
+    { 
+        // ...
+    }
+   
+}</pre>
+
+<p>This example shows how to use <code>Choice</code>:<a name="ChoiceExample"></a></p>
+
+<pre lang="cs">public class ChoiceExample
+{
+    public void DoWork() 
+    { 
+        SmartThreadPool stp = new SmartThreadPool();
+
+        int index = stp.Choice(GetDataFromA, GetDataFromB);
+
+        if (index == 0)
+        {
+            // Got data from A
+        }
+        else if (index == 1)
+        {
+            // Got data from B
+        }
+
+        smartThreadPool.Shutdown();
+    } 
+
+    private void GetDataFromA()
+    { 
+        // ...
+    }
+
+    private void GetDataFromB()
+    { 
+        // ...
+    }
+   
+}</pre>
+
+<p>This example shows how to use <code>Pipe</code>:<a name="PipeExample"></a></p>
+
+<pre lang="cs">public class PipeExample
+{
+    public void DoWork() 
+    { 
+        SmartThreadPool stp = new SmartThreadPool();
+        
+        int [] data = new int[2];
+
+        stp.Pipe(data, DoStep1, DoStep2);
+
+        smartThreadPool.Shutdown();
+    } 
+
+    private void DoStep1(int [] data)
+    { 
+        data[0] = ...
+    }
+
+    private void DoStep2(int [] data)
+    { 
+        data[1] = ...
+    }
+   
+}</pre>
+
+<p>This example shows how to use <code>Timeout</code>:<a name="TimeoutExample"></a></p>
+
+<pre lang="cs">public class TimeoutExample
+{
+    public void DoWork() 
+    { 
+        SmartThreadPool stp = new SmartThreadPool();
+
+        ...
+
+        // Queue a work item that will be cancelled within 5 seconds
+        IWorkItemResult wir = 
+            stp.QueueWorkItem(
+                new WorkItemInfo() { Timeout = 5*1000 }, 
+                DoSomething);
+        ...
+
+        smartThreadPool.Shutdown();
+    } 
+
+    private object DoSomething(object state)
+    { 
+        ...
+
+        for(...)
+        {
+            // If the work item was cancelled then abort the thread.
+            SmartThreadPool.AbortOnWorkItemCancel();
+        }
+        
+        ...
+
+        return result;
+    }
+}</pre>
+
+<h2>Disclaimer</h2>
+
+<p>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.</p>
+
+<h2><a name="History">History</a></h2>
+
+<ul>
+<li>7<sup>th</sup> Aug, 2004: Initial version.</li>
+
+<li>14<sup>th</sup> Sep, 2004: Bug fixes:</li>
+
+<ol>
+<li>Changed the start thread formula to '<code lang="cs">if ((InUseThreads + WaitingCallbacks) &gt; _workerThreads.Count)</code>'.</li>
+
+<li>Fixed handle leaks.</li>
+</ol>
+
+<li>16<sup>th</sup> Oct, 2004: Added a few features:</li>
+
+<ul>
+<li>Work items return result.</li>
+
+<li>Supports waiting synchronization for multiple work items.</li>
+
+<li>Work items can be cancelled.</li>
+
+<li>Passage of the caller thread's context to the thread in the pool.</li>
+
+<li>Minimal usage of Win32 handles.</li>
+
+<li>Minor bug fixes.</li>
+</ul>
+
+<li>26<sup>th</sup> Dec, 2004: Changes:</li>
+
+<ul>
+<li>Added <code>PostExecute</code>, with options on which cases to call it.</li>
+
+<li>Added a <code>WaitForIdle()</code> method that waits until the work items queue is empty.</li>
+
+<li>Added an option to dispose off the state objects.</li>
+
+<li>Updated <code>FireUnhandledException</code> so it will be more robust.</li>
+
+<li>Removed static constructors.</li>
+
+<li>Added finalizers.</li>
+
+<li>Changed exceptions so that they are serializable.</li>
+
+<li>Fixed the bug in one of the <code>SmartThreadPool</code> constructors.</li>
+
+<li>Changed <code>SmartThreadPool.WaitAll()</code> so that it will support any number of waiters. <code>SmartThreadPool.WaitAny()</code> is still limited by the .NET Framework.</li>
+
+<li>Changed exception handling so if a work item throws an exception, it is re-thrown at <code>GetResult()</code>, rather than firing an <code>UnhandledException</code> event. Note that <code>PostExecute</code> exceptions are always ignored.</li>
+</ul>
+
+<li>25<sup>th</sup> Mar, 2005: Changes:</li>
+
+<ul>
+<li>Fixed a bug where the work items got lost. It happens especially when the idle timeout is small.</li>
+</ul>
+
+<li>3<sup>rd</sup> Jul, 2005: Changes:</li>
+
+<ul>
+<li>Fixed a bug where <code>Enqueue()</code> throws an exception when <code>PopWaiter()</code> returns <code lang="cs">null</code>, hardly reconstructed.</li>
+</ul>
+
+<li>16<sup>th</sup> Aug 2005: Changes:</li>
+
+<ul>
+<li>Fixed a bug where <code>InUseThreads</code> becomes negative while canceling work items.</li>
+</ul>
+
+<li>31<sup>st</sup> Jan, 2006: Changes:</li>
+
+<ul>
+<li>Added work items priority.</li>
+
+<li>Removed support for chained delegates in callbacks and post executes (nobody really uses it).</li>
+
+<li>Added work item groups.</li>
+
+<li>Added work item group's idle event.</li>
+
+<li>Changed <code>SmartThreadPool.WaitAll()</code> behavior so that when it gets an empty array, it returns <code lang="cs">true</code> rather than throwing an exception.</li>
+
+<li>Added option to start the Smart Thread Pool and the Work Items Group as suspended.</li>
+
+<li>Exception behavior changed (again). The real exception is returned by an inner exception.</li>
+
+<li>Added option to keep the HTTP context of the caller thread (thanks to Steven T).</li>
+
+<li>Added performance counters.</li>
+
+<li>Added priority to the threads in the pool.</li>
+</ul>
+
+<li>13<sup>th</sup> Feb, 2006: Changes:</li>
+
+<ul>
+<li>Fixed the demo so that it won't crash with exception on start.</li>
+
+<li>Added a call to dispose off the performance counters so that there is no performance counter leak.</li>
+
+<li>Added exception catch in case the performance counters cannot be created.</li>
+</ul>
+
+<li>16<sup>th</sup> May 2008: Changes:</li>
+
+<ul>
+<li>Changed the dispose behavior and removed the finalizers.</li>
+
+<li>Enabled the change of <code>MaxThreads</code> and <code>MinThreads</code> at run time.</li>
+
+<li>Enabled the change of <code>Concurrency</code> of a <code>IWorkItemsGroup</code> at run time. If the <code>IWorkItemsGroup</code> is a <code>SmartThreadPool</code>, then <code>Concurrency</code> refers to <code>MaxThreads</code>.</li>
+
+<li>Improved the Cancel behavior.</li>
+
+<li>Added events for thread creation and termination.</li>
+
+<li>Fixed the HttpContext context capture.</li>
+
+<li>Changed internal collections so they use generic collections.</li>
+
+<li>Added the IsIdle flag to the <code>SmartThreadPool</code> and <code>IWorkItemsGroup</code>.&nbsp;</li>
+
+<li>Added support for WinCE.</li>
+
+<li>Added support for <code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code>.&nbsp;</li>
+</ul>
+
+<li>7<sup>th</sup> April 2009: Changes:</li>
+
+<ul>
+<li>Added support for Silverlight and Mono.</li>
+
+<li>Added <code>Join</code>, <code>Choice</code>, and <code>Pipe</code> to <code>SmartThreadPool</code>.</li>
+
+<li>Added local performance counters (for Mono, Silverlight, and Windows CE).</li>
+
+<li>Changed duration measures from <code>DateTime.Now</code> to <code>Stopwatch</code>.</li>
+
+<li>Queues changed from <code>System.Collections.Queue</code> to <code>System.Collections.Generic.LinkedList&lt;T&gt;</code>.</li>
+</ul>
+
+<li>21<sup>st</sup> December 2009: Changes:&nbsp;</li>
+
+<ul>
+<li>Added work item timeout (passive).&nbsp;</li>
+</ul>
+
+    <li>27<sup>th</sup> August 2012: Changes</li>
+        <ul>
+            <li><strong>Added set name to threads</strong>&nbsp;</li>
+            <li><strong>Added IsBackground option to threads</strong></li>
+            <li><strong>Added ApartmentState to threads</strong></li><li><strong>Added MaxStackSize to threads</strong>&nbsp;</li>
+            <li><strong>Fixed SmartThreadPool.Pipe</strong>&nbsp;</li>
+            <li><strong>Fixed thread creation when queuing many work items at the same time.</strong></li>
+            <li><strong>Fixed the WorkItemsQueue.Dequeue.</strong><br /><strong>
+            Replaced <code>while(!Monitor.TryEnter(this));</code> with <code>lock(this) { ... }</code>&nbsp;</strong>&nbsp;</li>
+        </ul>
+
+
+</ul>
diff --git a/Docs/WindowsCE.jpg b/Docs/WindowsCE.jpg
new file mode 100644
index 0000000..1caf7df
Binary files /dev/null and b/Docs/WindowsCE.jpg differ
diff --git a/Docs/WindowsPhone.png b/Docs/WindowsPhone.png
new file mode 100644
index 0000000..203c571
Binary files /dev/null and b/Docs/WindowsPhone.png differ
diff --git a/Docs/WorkItemsGroup.JPG b/Docs/WorkItemsGroup.JPG
new file mode 100644
index 0000000..7a44a4e
Binary files /dev/null and b/Docs/WorkItemsGroup.JPG differ
diff --git a/SSharpSmartThreadPool.sln b/SSharpSmartThreadPool.sln
new file mode 100644
index 0000000..7a8b15a
--- /dev/null
+++ b/SSharpSmartThreadPool.sln
@@ -0,0 +1,57 @@
+ï»¿
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpSmartThreadPool", "SSharpSmartThreadPool\SSharpSmartThreadPool.csproj", "{06299ED2-056A-427C-8C84-D716197080EB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpSmartThreadPoolTests", "SSharpSmartThreadPoolTests\SSharpSmartThreadPoolTests.csproj", "{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpCrestronExtensionsLibrary", "..\..\Simpl# Libraries\SSharpCrestronExtensionsLibrary\SSharpCrestronExtensionsLibrary\SSharpCrestronExtensionsLibrary.csproj", "{9384CC19-3161-498C-897C-2E972C2628A4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpNUnitLiteSSharpTestRunner", "..\SSharpNUnitLite3\SSharpNUnitLiteSSharpTestRunner\SSharpNUnitLiteSSharpTestRunner.csproj", "{7E041F2A-01CA-4770-B485-BC9AE41881E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpThreadingLibrary", "..\..\Simpl# Libraries\SSharpThreadingLibrary\SSharpThreadingLibrary\SSharpThreadingLibrary.csproj", "{779FD9FC-526E-47A0-9BB8-AA86F8D75032}"
+EndProject
+Global
+	GlobalSection(SourceCodeControl) = preSolution
+		SccNumberOfProjects = 2
+		SccProjectUniqueName0 = ..\\SSharpNUnitLite3\\SSharpNUnitLiteSSharpTestRunner\\SSharpNUnitLiteSSharpTestRunner.csproj
+		SccProjectName0 = $/S#\u0020Pro\u0020Libraries/SSharpNUnitLite3/SSharpNUnitLiteSSharpTestRunner
+		SccLocalPath0 = ..\\SSharpNUnitLite3\\SSharpNUnitLiteSSharpTestRunner
+		SccProvider0 = MSSCCI:SourceGear\u0020Vault\u0020Professional\u0020Classic\u0020Client
+		CanCheckoutShared = false
+		SccProjectUniqueName1 = ..\\..\\Simpl#\u0020Libraries\\SSharpThreadingLibrary\\SSharpThreadingLibrary\\SSharpThreadingLibrary.csproj
+		SccProjectName1 = $/S#\u0020Libraries/SSharpThreadingLibrary
+		SccLocalPath1 = ..\\..\\Simpl#\u0020Libraries\\SSharpThreadingLibrary\\SSharpThreadingLibrary
+		SccProvider1 = MSSCCI:SourceGear\u0020Vault\u0020Professional\u0020Classic\u0020Client
+		CanCheckoutShared = false
+	EndGlobalSection
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{06299ED2-056A-427C-8C84-D716197080EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{06299ED2-056A-427C-8C84-D716197080EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{06299ED2-056A-427C-8C84-D716197080EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{06299ED2-056A-427C-8C84-D716197080EB}.Release|Any CPU.Build.0 = Release|Any CPU
+		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9384CC19-3161-498C-897C-2E972C2628A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9384CC19-3161-498C-897C-2E972C2628A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9384CC19-3161-498C-897C-2E972C2628A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9384CC19-3161-498C-897C-2E972C2628A4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Release|Any CPU.Build.0 = Release|Any CPU
+		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/SSharpSmartThreadPool/CallerThreadContext.cs b/SSharpSmartThreadPool/CallerThreadContext.cs
new file mode 100644
index 0000000..13ed9d3
--- /dev/null
+++ b/SSharpSmartThreadPool/CallerThreadContext.cs
@@ -0,0 +1,138 @@
+
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Reflection;
+using System.Web;
+using System.Runtime.Remoting.Messaging;
+
+
+namespace Amib.Threading.Internal
+{
+#region CallerThreadContext class
+
+	/// <summary>
+	/// This class stores the caller call context in order to restore
+	/// it when the work item is executed in the thread pool environment. 
+	/// </summary>
+	internal class CallerThreadContext 
+	{
+#region Prepare reflection information
+
+		// Cached type information.
+		private static readonly MethodInfo getLogicalCallContextMethodInfo =
+			typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
+
+		private static readonly MethodInfo setLogicalCallContextMethodInfo =
+			typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);
+
+		private static string HttpContextSlotName = GetHttpContextSlotName();
+
+		private static string GetHttpContextSlotName()
+		{
+			FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);
+
+            if (fi != null)
+            {
+                return (string) fi.GetValue(null);
+            }
+
+		    return "HttpContext";
+		}
+
+        #endregion
+
+#region Private fields
+
+		private HttpContext _httpContext;
+		private LogicalCallContext _callContext;
+
+        #endregion
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		private CallerThreadContext()
+		{
+		}
+
+		public bool CapturedCallContext
+		{
+			get
+			{
+				return (null != _callContext);
+			}
+		}
+
+		public bool CapturedHttpContext
+		{
+			get
+			{
+				return (null != _httpContext);
+			}
+		}
+
+		/// <summary>
+		/// Captures the current thread context
+		/// </summary>
+		/// <returns></returns>
+		public static CallerThreadContext Capture(
+			bool captureCallContext, 
+			bool captureHttpContext)
+		{
+			//Debug.Assert(captureCallContext || captureHttpContext);
+
+			CallerThreadContext callerThreadContext = new CallerThreadContext();
+
+			// TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture()
+			// Capture Call Context
+			if(captureCallContext && (getLogicalCallContextMethodInfo != null))
+			{
+				callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null);
+				if (callerThreadContext._callContext != null)
+				{
+					callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone();
+				}
+			}
+
+			// Capture httpContext
+			if (captureHttpContext && (null != HttpContext.Current))
+			{
+				callerThreadContext._httpContext = HttpContext.Current;
+			}
+
+			return callerThreadContext;
+		}
+
+		/// <summary>
+		/// Applies the thread context stored earlier
+		/// </summary>
+		/// <param name="callerThreadContext"></param>
+		public static void Apply(CallerThreadContext callerThreadContext)
+		{
+			if (null == callerThreadContext) 
+			{
+				throw new ArgumentNullException("callerThreadContext");			
+			}
+
+			// Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run()
+			// Restore call context
+			if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null))
+			{
+				setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
+			}
+
+			// Restore HttpContext 
+			if (callerThreadContext._httpContext != null)
+			{
+                HttpContext.Current = callerThreadContext._httpContext;
+				//CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
+			}
+		}
+	}
+
+    #endregion
+}
+#endif
diff --git a/SSharpSmartThreadPool/CanceledWorkItemsGroup.cs b/SSharpSmartThreadPool/CanceledWorkItemsGroup.cs
new file mode 100644
index 0000000..5d313ff
--- /dev/null
+++ b/SSharpSmartThreadPool/CanceledWorkItemsGroup.cs
@@ -0,0 +1,14 @@
+namespace Amib.SSharpThreading.Internal
+{
+    internal class CanceledWorkItemsGroup
+    {
+        public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup();
+
+        public CanceledWorkItemsGroup()
+        {
+            IsCanceled = false;
+        }
+
+        public bool IsCanceled { get; set; }
+    }
+}
\ No newline at end of file
diff --git a/SSharpSmartThreadPool/EventWaitHandle.cs b/SSharpSmartThreadPool/EventWaitHandle.cs
new file mode 100644
index 0000000..66d6a6a
--- /dev/null
+++ b/SSharpSmartThreadPool/EventWaitHandle.cs
@@ -0,0 +1,106 @@
+#if (_WINDOWS_CE) && !SSHARP
+
+using System;
+using System.Runtime.InteropServices;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using WaitHandle = Crestron.SimplSharp.CEventHandle;
+
+namespace Amib.Threading.Internal
+{
+    /// <summary>
+    /// EventWaitHandle class
+    /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation.
+    /// So I wrote this class to implement these two methods with some of their overloads.
+    /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny.
+    /// Note that this class doesn't even inherit from WaitHandle!
+    /// </summary>
+    public class STPEventWaitHandle
+    {
+        #region Public Constants
+
+        public const int WaitTimeout = Timeout.Infinite;
+
+        #endregion
+
+        #region Private External Constants
+
+        private const Int32 WAIT_FAILED = -1;
+        private const Int32 WAIT_TIMEOUT = 0x102;
+        private const UInt32 INFINITE = 0xFFFFFFFF;
+
+        #endregion
+
+        #region WaitAll and WaitAny
+
+        internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
+        {
+            return waitHandle.WaitOne(millisecondsTimeout, exitContext);
+        }
+
+	    private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles)
+	    {
+	        IntPtr[] nativeHandles = new IntPtr[waitHandles.Length];
+	        for (int i = 0; i < waitHandles.Length; i++)
+	        {
+                nativeHandles[i] = waitHandles[i].Handle;
+	        }
+	        return nativeHandles;
+	    }
+
+	    public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+	    {
+            uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;
+
+            IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);
+
+	        int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout);
+
+            if (result == WAIT_TIMEOUT || result == WAIT_FAILED)
+            {
+                return false;
+            }
+
+	        return true;
+	    }
+
+
+	    public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+        {
+            uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;
+
+            IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);
+
+            int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout);
+
+            if (result >= 0 && result < waitHandles.Length)
+            {
+                return result;
+            }
+
+            return -1;
+        }
+
+        public static int WaitAny(WaitHandle[] waitHandles)
+        {
+            return WaitAny(waitHandles, Timeout.Infinite, false);
+        }
+
+        public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext)
+        {
+            int millisecondsTimeout = (int)timeout.TotalMilliseconds;
+
+            return WaitAny(waitHandles, millisecondsTimeout, false);
+        }
+ 
+        #endregion
+
+        #region External methods
+
+        [DllImport("coredll.dll", SetLastError = true)]
+        public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds);
+
+        #endregion
+    }
+}
+#endif
\ No newline at end of file
diff --git a/SSharpSmartThreadPool/EventWaitHandleFactory.cs b/SSharpSmartThreadPool/EventWaitHandleFactory.cs
new file mode 100644
index 0000000..4c229c6
--- /dev/null
+++ b/SSharpSmartThreadPool/EventWaitHandleFactory.cs
@@ -0,0 +1,83 @@
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+#if (_WINDOWS_CE)
+using System;
+using System.Runtime.InteropServices;
+#endif
+
+namespace Amib.SSharpThreading.Internal
+{
+    /// <summary>
+    /// EventWaitHandleFactory class.
+    /// This is a static class that creates AutoResetEvent and ManualResetEvent objects.
+    /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property 
+    /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API.
+    /// Consequently this class creates the needed XxxResetEvent and replaces the handle if
+    /// it's a WindowsCE OS.
+    /// </summary>
+    public static class EventWaitHandleFactory
+    {
+        /// <summary>
+        /// Create a new AutoResetEvent object
+        /// </summary>
+        /// <returns>Return a new AutoResetEvent object</returns>
+        public static AutoResetEvent CreateAutoResetEvent()
+        {
+            AutoResetEvent waitHandle = new AutoResetEvent(false);
+
+#if (_WINDOWS_CE) && !SSHARP
+            ReplaceEventHandle(waitHandle, false, false);
+#endif
+
+            return waitHandle;
+        }
+
+        /// <summary>
+        /// Create a new ManualResetEvent object
+        /// </summary>
+        /// <returns>Return a new ManualResetEvent object</returns>
+        public static ManualResetEvent CreateManualResetEvent(bool initialState)
+        {
+            ManualResetEvent waitHandle = new ManualResetEvent(initialState);
+
+#if (_WINDOWS_CE) && !SSHARP
+            ReplaceEventHandle(waitHandle, true, initialState);
+#endif
+
+            return waitHandle;
+        }
+
+#if (_WINDOWS_CE) && !SSHARP
+
+        /// <summary>
+        /// Replace the event handle
+        /// </summary>
+        /// <param name="waitHandle">The WaitHandle object which its handle needs to be replaced.</param>
+        /// <param name="manualReset">Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false)</param>
+        /// <param name="initialState">The initial state of the event</param>
+        private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState)
+        {
+            // Store the old handle 
+            IntPtr oldHandle = waitHandle.Handle;
+
+            // Create a new event
+            IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null);
+
+            // Replace the old event with the new event
+            waitHandle.Handle = newHandle;
+
+            // Close the old event
+            CloseHandle (oldHandle);        
+        }
+
+        [DllImport("coredll.dll", SetLastError = true)]
+        public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
+
+        //Handle
+        [DllImport("coredll.dll", SetLastError = true)]
+        public static extern bool CloseHandle(IntPtr hObject);
+#endif
+
+    }
+}
diff --git a/SSharpSmartThreadPool/Exceptions.cs b/SSharpSmartThreadPool/Exceptions.cs
new file mode 100644
index 0000000..ff0fb34
--- /dev/null
+++ b/SSharpSmartThreadPool/Exceptions.cs
@@ -0,0 +1,111 @@
+using System;
+#if !(_WINDOWS_CE)
+using System.Runtime.Serialization;
+#endif
+
+namespace Amib.SSharpThreading
+{
+    #region Exceptions
+
+    /// <summary>
+    /// Represents an exception in case IWorkItemResult.GetResult has been canceled
+    /// </summary>
+    public sealed partial class WorkItemCancelException : Exception
+    {
+        public WorkItemCancelException()
+        {
+        }
+
+        public WorkItemCancelException(string message)
+            : base(message)
+        {
+        }
+
+        public WorkItemCancelException(string message, Exception e)
+            : base(message, e)
+        {
+        }
+    }
+
+    /// <summary>
+    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+    /// </summary>
+    public sealed partial class WorkItemTimeoutException : Exception
+    {
+        public WorkItemTimeoutException()
+        {
+        }
+
+        public WorkItemTimeoutException(string message)
+            : base(message)
+        {
+        }
+
+        public WorkItemTimeoutException(string message, Exception e)
+            : base(message, e)
+        {
+        }
+    }
+
+    /// <summary>
+    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+    /// </summary>
+    public sealed partial class WorkItemResultException : Exception
+    {
+        public WorkItemResultException()
+        {
+        }
+
+        public WorkItemResultException(string message)
+            : base(message)
+        {
+        }
+
+        public WorkItemResultException(string message, Exception e)
+            : base(message, e)
+        {
+        }
+    }
+
+
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+    /// <summary>
+    /// Represents an exception in case IWorkItemResult.GetResult has been canceled
+    /// </summary>
+    [Serializable]
+    public sealed partial class WorkItemCancelException
+    {
+        public WorkItemCancelException(SerializationInfo si, StreamingContext sc)
+            : base(si, sc)
+        {
+        }
+    }
+
+    /// <summary>
+    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+    /// </summary>
+    [Serializable]
+    public sealed partial class WorkItemTimeoutException
+    {
+        public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc)
+            : base(si, sc)
+        {
+        }
+    }
+
+    /// <summary>
+    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
+    /// </summary>
+    [Serializable]
+    public sealed partial class WorkItemResultException
+    {
+        public WorkItemResultException(SerializationInfo si, StreamingContext sc)
+            : base(si, sc)
+        {
+        }
+    }
+
+#endif
+
+    #endregion
+}
diff --git a/SSharpSmartThreadPool/Interfaces.cs b/SSharpSmartThreadPool/Interfaces.cs
new file mode 100644
index 0000000..dc69cdc
--- /dev/null
+++ b/SSharpSmartThreadPool/Interfaces.cs
@@ -0,0 +1,611 @@
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+namespace Amib.SSharpThreading
+	{
+	#region Delegates
+
+	/// <summary>
+	/// A delegate that represents the method to run as the work item
+	/// </summary>
+	/// <param name="state">A state object for the method to run</param>
+	public delegate object WorkItemCallback (object state);
+
+	/// <summary>
+	/// A delegate to call after the WorkItemCallback completed
+	/// </summary>
+	/// <param name="wir">The work item result object</param>
+	public delegate void PostExecuteWorkItemCallback (IWorkItemResult wir);
+
+	/// <summary>
+	/// A delegate to call after the WorkItemCallback completed
+	/// </summary>
+	/// <param name="wir">The work item result object</param>
+	public delegate void PostExecuteWorkItemCallback<TResult> (IWorkItemResult<TResult> wir);
+
+	/// <summary>
+	/// A delegate to call when a WorkItemsGroup becomes idle
+	/// </summary>
+	/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
+	public delegate void WorkItemsGroupIdleHandler (IWorkItemsGroup workItemsGroup);
+
+	/// <summary>
+	/// A delegate to call after a thread is created, but before 
+	/// it's first use.
+	/// </summary>
+	public delegate void ThreadInitializationHandler ();
+
+	/// <summary>
+	/// A delegate to call when a thread is about to exit, after 
+	/// it is no longer belong to the pool.
+	/// </summary>
+	public delegate void ThreadTerminationHandler ();
+
+	#endregion
+
+	#region WorkItem Priority
+
+	/// <summary>
+	/// Defines the availeable priorities of a work item.
+	/// The higher the priority a work item has, the sooner
+	/// it will be executed.
+	/// </summary>
+	public enum WorkItemPriority
+		{
+		Lowest,
+		BelowNormal,
+		Normal,
+		AboveNormal,
+		Highest,
+		}
+
+	#endregion
+
+	#region IWorkItemsGroup interface
+
+	/// <summary>
+	/// IWorkItemsGroup interface
+	/// Created by SmartThreadPool.CreateWorkItemsGroup()
+	/// </summary>
+	public interface IWorkItemsGroup
+		{
+		/// <summary>
+		/// Get/Set the name of the WorkItemsGroup
+		/// </summary>
+		string Name { get; set; }
+
+		/// <summary>
+		/// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
+		/// </summary>
+		int Concurrency { get; set; }
+
+		/// <summary>
+		/// Get the number of work items waiting in the queue.
+		/// </summary>
+		int WaitingCallbacks { get; }
+
+		/// <summary>
+		/// Get an array with all the state objects of the currently running items.
+		/// The array represents a snap shot and impact performance.
+		/// </summary>
+		object[] GetStates ();
+
+		/// <summary>
+		/// Get the WorkItemsGroup start information
+		/// </summary>
+		WIGStartInfo WIGStartInfo { get; }
+
+		/// <summary>
+		/// Starts to execute work items
+		/// </summary>
+		void Start ();
+
+		/// <summary>
+		/// Cancel all the work items.
+		/// Same as Cancel(false)
+		/// </summary>
+		void Cancel ();
+
+		/// <summary>
+		/// Cancel all work items using thread abortion
+		/// </summary>
+		/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
+		void Cancel (bool abortExecution);
+
+		/// <summary>
+		/// Wait for all work item to complete.
+		/// </summary>
+		void WaitForIdle ();
+
+		/// <summary>
+		/// Wait for all work item to complete, until timeout expired
+		/// </summary>
+		/// <param name="timeout">How long to wait for the work items to complete</param>
+		/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
+		bool WaitForIdle (TimeSpan timeout);
+
+		/// <summary>
+		/// Wait for all work item to complete, until timeout expired
+		/// </summary>
+		/// <param name="millisecondsTimeout">How long to wait for the work items to complete in milliseconds</param>
+		/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
+		bool WaitForIdle (int millisecondsTimeout);
+
+		/// <summary>
+		/// IsIdle is true when there are no work items running or queued.
+		/// </summary>
+		bool IsIdle { get; }
+
+		/// <summary>
+		/// This event is fired when all work items are completed.
+		/// (When IsIdle changes to true)
+		/// This event only work on WorkItemsGroup. On SmartThreadPool
+		/// it throws the NotImplementedException.
+		/// </summary>
+		event WorkItemsGroupIdleHandler OnIdle;
+
+		#region QueueWorkItem
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <returns>Returns a work item result</returns>        
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="workItemPriority">The priority of the work item</param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback, WorkItemPriority workItemPriority);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, WorkItemPriority workItemPriority);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="workItemInfo">Work item info</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback);
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="workItemInfo">Work item information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <returns>Returns a work item result</returns>
+		IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback, object state);
+
+		#endregion
+
+		#region QueueWorkItem(Action<...>)
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
+		IWorkItemResult QueueWorkItem (Action action);
+		IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
+		IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg);
+		IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
+		IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2);
+		IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
+		IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3);
+		IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
+		IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+		IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);
+
+		#endregion
+
+		#region QueueWorkItem(Func<...>)
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
+		/// its GetResult() returns a TResult object</returns>
+		IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func);
+		IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
+		/// its GetResult() returns a TResult object</returns>
+		IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg);
+		IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
+		/// its GetResult() returns a TResult object</returns>
+		IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2);
+		IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
+		/// its GetResult() returns a TResult object</returns>
+		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3);
+		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);
+
+		/// <summary>
+		/// Queue a work item.
+		/// </summary>
+		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
+		/// its GetResult() returns a TResult object</returns>
+		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);
+
+		#endregion
+		}
+
+	#endregion
+
+	#region CallToPostExecute enumerator
+
+	[Flags]
+	public enum CallToPostExecute
+		{
+		/// <summary>
+		/// Never call to the PostExecute call back
+		/// </summary>
+		Never = 0x00,
+
+		/// <summary>
+		/// Call to the PostExecute only when the work item is cancelled
+		/// </summary>
+		WhenWorkItemCanceled = 0x01,
+
+		/// <summary>
+		/// Call to the PostExecute only when the work item is not cancelled
+		/// </summary>
+		WhenWorkItemNotCanceled = 0x02,
+
+		/// <summary>
+		/// Always call to the PostExecute
+		/// </summary>
+		Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
+		}
+
+	#endregion
+
+	#region IWorkItemResult interface
+
+	/// <summary>
+	/// The common interface of IWorkItemResult and IWorkItemResult&lt;T&gt;
+	/// </summary>
+	public interface IWaitableResult
+		{
+		/// <summary>
+		/// This method intent is for internal use.
+		/// </summary>
+		/// <returns></returns>
+		IWorkItemResult GetWorkItemResult ();
+
+		/// <summary>
+		/// This method intent is for internal use.
+		/// </summary>
+		/// <returns></returns>
+		IWorkItemResult<TResult> GetWorkItemResultT<TResult> ();
+		}
+
+	/// <summary>
+	/// IWorkItemResult interface.
+	/// Created when a WorkItemCallback work item is queued.
+	/// </summary>
+	public interface IWorkItemResult : IWorkItemResult<object>
+		{
+		}
+
+	/// <summary>
+	/// IWorkItemResult&lt;TResult&gt; interface.
+	/// Created when a Func&lt;TResult&gt; work item is queued.
+	/// </summary>
+	public interface IWorkItemResult<TResult> : IWaitableResult
+		{
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits.
+		/// </summary>
+		/// <returns>The result of the work item</returns>
+		TResult GetResult ();
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout.
+		/// </summary>
+		/// <returns>The result of the work item</returns>
+		/// On timeout throws WorkItemTimeoutException
+		TResult GetResult (
+			int millisecondsTimeout,
+			bool exitContext);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout.
+		/// </summary>
+		/// <returns>The result of the work item</returns>
+		/// On timeout throws WorkItemTimeoutException
+		TResult GetResult (
+			TimeSpan timeout,
+			bool exitContext);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
+		/// </summary>
+		/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
+		/// <returns>The result of the work item</returns>
+		/// On timeout throws WorkItemTimeoutException
+		/// On cancel throws WorkItemCancelException
+		TResult GetResult (
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
+		/// </summary>
+		/// <returns>The result of the work item</returns>
+		/// On timeout throws WorkItemTimeoutException
+		/// On cancel throws WorkItemCancelException
+		TResult GetResult (
+			TimeSpan timeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits.
+		/// </summary>
+		/// <param name="e">Filled with the exception if one was thrown</param>
+		/// <returns>The result of the work item</returns>
+		TResult GetResult (out Exception e);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout.
+		/// </summary>
+		/// <param name="millisecondsTimeout"></param>
+		/// <param name="exitContext"></param>
+		/// <param name="e">Filled with the exception if one was thrown</param>
+		/// <returns>The result of the work item</returns>
+		/// On timeout throws WorkItemTimeoutException
+		TResult GetResult (
+			int millisecondsTimeout,
+			bool exitContext,
+			out Exception e);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout.
+		/// </summary>
+		/// <param name="exitContext"></param>
+		/// <param name="e">Filled with the exception if one was thrown</param>
+		/// <param name="timeout"></param>
+		/// <returns>The result of the work item</returns>
+		/// On timeout throws WorkItemTimeoutException
+		TResult GetResult (
+			TimeSpan timeout,
+			bool exitContext,
+			out Exception e);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
+		/// </summary>
+		/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
+		/// <param name="e">Filled with the exception if one was thrown</param>
+		/// <returns>The result of the work item</returns>
+		/// On timeout throws WorkItemTimeoutException
+		/// On cancel throws WorkItemCancelException
+		TResult GetResult (
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle,
+			out Exception e);
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
+		/// </summary>
+		/// <returns>The result of the work item</returns>
+		/// <param name="cancelWaitHandle"></param>
+		/// <param name="e">Filled with the exception if one was thrown</param>
+		/// <param name="timeout"></param>
+		/// <param name="exitContext"></param>
+		/// On timeout throws WorkItemTimeoutException
+		/// On cancel throws WorkItemCancelException
+		TResult GetResult (
+			TimeSpan timeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle,
+			out Exception e);
+
+		/// <summary>
+		/// Gets an indication whether the asynchronous operation has completed.
+		/// </summary>
+		bool IsCompleted { get; }
+
+		/// <summary>
+		/// Gets an indication whether the asynchronous operation has been canceled.
+		/// </summary>
+		bool IsCanceled { get; }
+
+		/// <summary>
+		/// Gets the user-defined object that contains context data 
+		/// for the work item method.
+		/// </summary>
+		object State { get; }
+
+		/// <summary>
+		/// Same as Cancel(false).
+		/// </summary>
+		bool Cancel ();
+
+		/// <summary>
+		/// Cancel the work item execution.
+		/// If the work item is in the queue then it won't execute
+		/// If the work item is completed, it will remain completed
+		/// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled
+		///   property to check if the work item has been cancelled. If the abortExecution is set to true then
+		///   the Smart Thread Pool will send an AbortException to the running thread to stop the execution 
+		///   of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException.
+		/// If the work item is already cancelled it will remain cancelled
+		/// </summary>
+		/// <param name="abortExecution">When true send an AbortException to the executing thread.</param>
+		/// <returns>Returns true if the work item was not completed, otherwise false.</returns>
+		bool Cancel (bool abortExecution);
+
+		/// <summary>
+		/// Get the work item's priority
+		/// </summary>
+		WorkItemPriority WorkItemPriority { get; }
+
+		/// <summary>
+		/// Return the result, same as GetResult()
+		/// </summary>
+		TResult Result { get; }
+
+		/// <summary>
+		/// Returns the exception if occured otherwise returns null.
+		/// </summary>
+		object Exception { get; }
+		}
+
+	#endregion
+
+	#region .NET 3.5
+
+#if false
+	// All these delegate are built-in .NET 3.5
+	// Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity.
+
+	public delegate void Action ();
+	public delegate void Action<T1, T2> (T1 arg1, T2 arg2);
+	public delegate void Action<T1, T2, T3> (T1 arg1, T2 arg2, T3 arg3);
+	public delegate void Action<T1, T2, T3, T4> (T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+	public delegate TResult Func<TResult> ();
+	public delegate TResult Func<T, TResult> (T arg1);
+	public delegate TResult Func<T1, T2, TResult> (T1 arg1, T2 arg2);
+	public delegate TResult Func<T1, T2, T3, TResult> (T1 arg1, T2 arg2, T3 arg3);
+	public delegate TResult Func<T1, T2, T3, T4, TResult> (T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+#endif
+	#endregion
+	}
diff --git a/SSharpSmartThreadPool/InternalInterfaces.cs b/SSharpSmartThreadPool/InternalInterfaces.cs
new file mode 100644
index 0000000..0e80060
--- /dev/null
+++ b/SSharpSmartThreadPool/InternalInterfaces.cs
@@ -0,0 +1,27 @@
+ï»¿
+namespace Amib.SSharpThreading.Internal
+{
+    /// <summary>
+    /// An internal delegate to call when the WorkItem starts or completes
+    /// </summary>
+    internal delegate void WorkItemStateCallback(WorkItem workItem);
+
+    internal interface IInternalWorkItemResult
+    {
+        event WorkItemStateCallback OnWorkItemStarted;
+        event WorkItemStateCallback OnWorkItemCompleted;
+    }
+
+    internal interface IInternalWaitableResult
+    {
+        /// <summary>
+        /// This method is intent for internal use.
+        /// </summary>   
+        IWorkItemResult GetWorkItemResult();
+    }
+
+    public interface IHasWorkItemPriority
+    {
+        WorkItemPriority WorkItemPriority { get; }
+    }
+}
diff --git a/SSharpSmartThreadPool/PriorityQueue.cs b/SSharpSmartThreadPool/PriorityQueue.cs
new file mode 100644
index 0000000..ac9754a
--- /dev/null
+++ b/SSharpSmartThreadPool/PriorityQueue.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+//using SSMono.Diagnostics;
+
+namespace Amib.SSharpThreading.Internal
+{
+	#region PriorityQueue class
+
+	/// <summary>
+	/// PriorityQueue class
+	/// This class is not thread safe because we use external lock
+	/// </summary>
+	public sealed class PriorityQueue : IEnumerable
+	{
+		#region Private members
+
+		/// <summary>
+		/// The number of queues, there is one for each type of priority
+		/// </summary>
+		private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1;
+
+		/// <summary>
+		/// Work items queues. There is one for each type of priority
+		/// </summary>
+        private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];
+
+		/// <summary>
+		/// The total number of work items within the queues 
+		/// </summary>
+		private int _workItemsCount;
+
+		/// <summary>
+		/// Use with IEnumerable interface
+		/// </summary>
+		private int _version;
+
+		#endregion
+
+		#region Contructor
+
+		public PriorityQueue()
+		{
+			for(int i = 0; i < _queues.Length; ++i)
+			{
+                _queues[i] = new LinkedList<IHasWorkItemPriority>();
+			}
+		}
+
+		#endregion
+
+		#region Methods
+
+		/// <summary>
+		/// Enqueue a work item.
+		/// </summary>
+		/// <param name="workItem">A work item</param>
+		public void Enqueue(IHasWorkItemPriority workItem)
+		{
+			//Debug.Assert(null != workItem);
+
+			int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1;
+			//Debug.Assert(queueIndex >= 0);
+			//Debug.Assert(queueIndex < _queuesCount);
+
+			_queues[queueIndex].AddLast(workItem);
+			++_workItemsCount;
+			++_version;
+		}
+
+		/// <summary>
+		/// Dequeque a work item.
+		/// </summary>
+		/// <returns>Returns the next work item</returns>
+		public IHasWorkItemPriority Dequeue()
+		{
+			IHasWorkItemPriority workItem = null;
+
+			if(_workItemsCount > 0)
+			{
+				int queueIndex = GetNextNonEmptyQueue(-1);
+				//Debug.Assert(queueIndex >= 0);
+                workItem = _queues[queueIndex].First.Value;
+				_queues[queueIndex].RemoveFirst();
+				//Debug.Assert(null != workItem);
+				--_workItemsCount;
+				++_version;
+			}
+
+			return workItem;
+		}
+
+		/// <summary>
+		/// Find the next non empty queue starting at queue queueIndex+1
+		/// </summary>
+		/// <param name="queueIndex">The index-1 to start from</param>
+		/// <returns>
+		/// The index of the next non empty queue or -1 if all the queues are empty
+		/// </returns>
+		private int GetNextNonEmptyQueue(int queueIndex)
+		{
+			for(int i = queueIndex+1; i < _queuesCount; ++i)
+			{
+				if(_queues[i].Count > 0)
+				{
+					return i;
+				}
+			}
+			return -1;
+		}
+
+		/// <summary>
+		/// The number of work items 
+		/// </summary>
+		public int Count
+		{
+			get
+			{
+				return _workItemsCount;
+			}
+		}
+
+		/// <summary>
+		/// Clear all the work items 
+		/// </summary>
+		public void Clear()
+		{
+			if (_workItemsCount > 0)
+			{
+				foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
+				{
+					queue.Clear();
+				}
+				_workItemsCount = 0;
+				++_version;
+			}
+		}
+
+		#endregion
+
+		#region IEnumerable Members
+
+		/// <summary>
+		/// Returns an enumerator to iterate over the work items
+		/// </summary>
+		/// <returns>Returns an enumerator</returns>
+		public IEnumerator GetEnumerator()
+		{
+			return new PriorityQueueEnumerator(this);
+		}
+
+		#endregion
+
+		#region PriorityQueueEnumerator
+
+		/// <summary>
+		/// The class the implements the enumerator
+		/// </summary>
+		private class PriorityQueueEnumerator : IEnumerator
+		{
+			private readonly PriorityQueue _priorityQueue;
+			private int _version;
+			private int _queueIndex;
+			private IEnumerator _enumerator;
+
+			public PriorityQueueEnumerator(PriorityQueue priorityQueue)
+			{
+				_priorityQueue = priorityQueue;
+				_version = _priorityQueue._version;
+				_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
+				if (_queueIndex >= 0)
+				{
+					_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
+				}
+				else
+				{
+					_enumerator = null;
+				}
+			}
+
+			#region IEnumerator Members
+
+			public void Reset()
+			{
+				_version = _priorityQueue._version;
+				_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
+				if (_queueIndex >= 0)
+				{
+					_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
+				}
+				else
+				{
+					_enumerator = null;
+				}
+			}
+
+			public object Current
+			{
+				get
+				{
+					//Debug.Assert(null != _enumerator);
+					return _enumerator.Current;
+				}
+			}
+
+			public bool MoveNext()
+			{
+				if (null == _enumerator)
+				{
+					return false;
+				}
+
+				if(_version != _priorityQueue._version)
+				{
+					throw new InvalidOperationException("The collection has been modified");
+
+				}
+				if (!_enumerator.MoveNext())
+				{
+					_queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex);
+					if(-1 == _queueIndex)
+					{
+						return false;
+					}
+					_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
+					_enumerator.MoveNext();
+					return true;
+				}
+				return true;
+			}
+
+			#endregion
+		}
+
+		#endregion
+	}
+
+	#endregion
+}
diff --git a/SSharpSmartThreadPool/Properties/AssemblyInfo.cs b/SSharpSmartThreadPool/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..3826b43
--- /dev/null
+++ b/SSharpSmartThreadPool/Properties/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+ï»¿using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyTitle ("SSharpSmartThreadPool")]
+[assembly: AssemblyDescription ("S# Port of Amib Smart Thread Pool")]
+[assembly: AssemblyCompany ("Nivloc Enterprises Ltd")]
+[assembly: AssemblyProduct ("SSharpSmartThreadPool")]
+[assembly: AssemblyVersion ("2.2.3.0")]
+
+[assembly: InternalsVisibleTo ("SSharpSmartThreadPoolTests")]
diff --git a/SSharpSmartThreadPool/SLExt.cs b/SSharpSmartThreadPool/SLExt.cs
new file mode 100644
index 0000000..0b894c8
--- /dev/null
+++ b/SSharpSmartThreadPool/SLExt.cs
@@ -0,0 +1,16 @@
+ï»¿#if _SILVERLIGHT
+
+using System.Threading;
+
+namespace Amib.Threading
+{
+    public enum ThreadPriority
+    {
+        Lowest,
+        BelowNormal,
+        Normal,
+        AboveNormal,
+        Highest,
+    }
+}
+#endif
diff --git a/SSharpSmartThreadPool/SSharpSmartThreadPool.csproj b/SSharpSmartThreadPool/SSharpSmartThreadPool.csproj
new file mode 100644
index 0000000..f1e994d
--- /dev/null
+++ b/SSharpSmartThreadPool/SSharpSmartThreadPool.csproj
@@ -0,0 +1,127 @@
+ï»¿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{06299ED2-056A-427C-8C84-D716197080EB}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SSharpSmartThreadPool</RootNamespace>
+    <AssemblyName>SSharpSmartThreadPool</AssemblyName>
+    <ProjectTypeGuids>{0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <PlatformFamilyName>WindowsCE</PlatformFamilyName>
+    <PlatformID>E2BECB1F-8C8C-41ba-B736-9BE7D946A398</PlatformID>
+    <OSVersion>5.0</OSVersion>
+    <DeployDirSuffix>SmartDeviceProject1</DeployDirSuffix>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <NativePlatformName>Windows CE</NativePlatformName>
+    <FormFactorID>
+    </FormFactorID>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE;_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <FileAlignment>512</FileAlignment>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <FileAlignment>512</FileAlignment>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
+    <DefineConstants>_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="mscorlib" />
+    <Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="SimplSharpHelperInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="SimplSharpPro, Version=1.4.0.14, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="SSharpThreadingLibrary, Version=1.0.0.10633, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\Simpl# Libraries\SSharpThreadingLibrary\SSharpThreadingLibrary\bin\Release\SSharpThreadingLibrary.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Data" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="CallerThreadContext.cs" />
+    <Compile Include="CanceledWorkItemsGroup.cs" />
+    <Compile Include="EventWaitHandle.cs" />
+    <Compile Include="EventWaitHandleFactory.cs" />
+    <Compile Include="Exceptions.cs" />
+    <Compile Include="Interfaces.cs" />
+    <Compile Include="InternalInterfaces.cs" />
+    <Compile Include="PriorityQueue.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="SmartThreadPool.cs" />
+    <Compile Include="SmartThreadPool.ThreadEntry.cs" />
+    <Compile Include="STPEventWaitHandle.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="STPPerformanceCounter.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="STPStartInfo.cs" />
+    <Compile Include="SynchronizedDictionary.cs" />
+    <Compile Include="WIGStartInfo.cs" />
+    <Compile Include="WorkItem.cs" />
+    <Compile Include="WorkItem.WorkItemResult.cs" />
+    <Compile Include="WorkItemFactory.cs" />
+    <Compile Include="WorkItemInfo.cs" />
+    <Compile Include="WorkItemResultTWrapper.cs" />
+    <Compile Include="WorkItemsGroup.cs" />
+    <Compile Include="WorkItemsGroupBase.cs" />
+    <Compile Include="WorkItemsQueue.cs" />
+    <None Include="Properties\ControlSystem.cfg" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{0B4745B0-194B-4BB6-8E21-E9057CA92300}">
+        <ProgramIdTag>SSharpSmartThreadPoo</ProgramIdTag>
+        <SystemName>SSharpSmartThreadPool</SystemName>
+        <Programmer />
+        <ArchiveFilename>C:\Projects\Crestron\Simpl# Pro Libraries\SSharpSmartThreadPool\SSharpSmartThreadPool\bin\Debug\SSharpSmartThreadPool.cplz</ArchiveFilename>
+        <MinFirmwareVersion>1.009.0029</MinFirmwareVersion>
+        <CompiledOn>3/14/2015 11:03:51 AM</CompiledOn>
+        <AdditionalInfo />
+        <EmbedSourceArchive>False</EmbedSourceArchive>
+        <CopyTo />
+        <OriginalReferenceSources>
+          <Reference sourceFilename="../../../Simpl%23%20Libraries/SSharpThreadingLibrary/SSharpThreadingLibrary/bin/Release/SSharpThreadingLibrary.dll" referenceName="SSharpThreadingLibrary" />
+        </OriginalReferenceSources>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+  <PropertyGroup>
+    <PostBuildEvent>rem S# Pro preparation will execute after these operations</PostBuildEvent>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/SSharpSmartThreadPool/STPEventWaitHandle.cs b/SSharpSmartThreadPool/STPEventWaitHandle.cs
new file mode 100644
index 0000000..cda2621
--- /dev/null
+++ b/SSharpSmartThreadPool/STPEventWaitHandle.cs
@@ -0,0 +1,64 @@
+ï»¿#if !(_WINDOWS_CE) || SSHARP
+
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+namespace Amib.SSharpThreading.Internal
+	{
+#if _WINDOWS ||  WINDOWS_PHONE || SSHARP
+	internal static class STPEventWaitHandle
+		{
+		public const int WaitTimeout = Timeout.Infinite;
+
+		internal static bool WaitAll (WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+			{
+			return WaitHandle.WaitAll (waitHandles, millisecondsTimeout);
+			}
+
+		internal static int WaitAny (WaitHandle[] waitHandles)
+			{
+			return WaitHandle.WaitAny (waitHandles);
+			}
+
+		internal static int WaitAny (WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+			{
+			return WaitHandle.WaitAny (waitHandles, millisecondsTimeout);
+			}
+
+		internal static bool WaitOne (WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
+			{
+			return waitHandle.WaitOne (millisecondsTimeout);
+			}
+		}
+#else
+    internal static class STPEventWaitHandle
+    {
+        public const int WaitTimeout = Timeout.Infinite;
+
+        internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+        {
+            return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
+        }
+
+        internal static int WaitAny(WaitHandle[] waitHandles)
+        {
+            return WaitHandle.WaitAny(waitHandles);
+        }
+
+        internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+        {
+            return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
+        }
+
+        internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
+        {
+            return waitHandle.WaitOne(millisecondsTimeout, exitContext);
+        }
+    }
+#endif
+
+	}
+
+#endif
\ No newline at end of file
diff --git a/SSharpSmartThreadPool/STPPerformanceCounter.cs b/SSharpSmartThreadPool/STPPerformanceCounter.cs
new file mode 100644
index 0000000..f147153
--- /dev/null
+++ b/SSharpSmartThreadPool/STPPerformanceCounter.cs
@@ -0,0 +1,448 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+
+namespace Amib.SSharpThreading
+{
+    public interface ISTPPerformanceCountersReader
+    {
+        long InUseThreads { get; }
+        long ActiveThreads { get; }
+        long WorkItemsQueued { get; }
+        long WorkItemsProcessed { get; }
+    }
+}
+
+namespace Amib.SSharpThreading.Internal
+{
+    internal interface ISTPInstancePerformanceCounters : IDisposable
+    {
+        void Close();
+        void SampleThreads(long activeThreads, long inUseThreads);
+        void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
+        void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
+        void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
+    }
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+
+    internal enum STPPerformanceCounterType
+	{
+		// Fields
+		ActiveThreads				= 0,
+		InUseThreads				= 1,
+		OverheadThreads				= 2,
+		OverheadThreadsPercent		= 3,
+		OverheadThreadsPercentBase	= 4,
+
+		WorkItems					= 5,
+		WorkItemsInQueue			= 6,
+		WorkItemsProcessed			= 7,
+
+		WorkItemsQueuedPerSecond	= 8,
+		WorkItemsProcessedPerSecond	= 9,
+
+		AvgWorkItemWaitTime			= 10,
+		AvgWorkItemWaitTimeBase		= 11,
+
+		AvgWorkItemProcessTime		= 12,
+		AvgWorkItemProcessTimeBase	= 13,
+
+		WorkItemsGroups				= 14,
+
+		LastCounter					= 14,
+	}
+ 
+
+	/// <summary>
+	/// Summary description for STPPerformanceCounter.
+	/// </summary>
+	internal class STPPerformanceCounter
+	{
+		// Fields
+		private readonly PerformanceCounterType _pcType;
+		protected string _counterHelp;
+		protected string _counterName;
+
+		// Methods
+		public STPPerformanceCounter(
+			string counterName, 
+			string counterHelp, 
+			PerformanceCounterType pcType)
+		{
+			_counterName = counterName;
+			_counterHelp = counterHelp;
+			_pcType = pcType;
+		}
+
+		public void AddCounterToCollection(CounterCreationDataCollection counterData)
+		{
+			CounterCreationData counterCreationData = new CounterCreationData(
+				_counterName, 
+				_counterHelp, 
+				_pcType);
+
+			counterData.Add(counterCreationData);
+		}
+ 
+		// Properties
+		public string Name
+		{
+			get
+			{
+				return _counterName;
+			}
+		}
+	}
+
+	internal class STPPerformanceCounters
+	{
+		// Fields
+		internal STPPerformanceCounter[] _stpPerformanceCounters;
+		private static readonly STPPerformanceCounters _instance;
+		internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
+		internal const string _stpCategoryName = "SmartThreadPool";
+
+		// Methods
+		static STPPerformanceCounters()
+		{
+			_instance = new STPPerformanceCounters();
+		}
+ 
+		private STPPerformanceCounters()
+		{
+			STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] 
+				{ 
+					new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), 
+					new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), 
+					new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), 
+					new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), 
+					new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), 
+
+					new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), 
+					new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), 
+					new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), 
+
+					new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), 
+					new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), 
+
+					new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), 
+					new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), 
+
+					new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), 
+					new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), 
+
+					new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), 
+				};
+
+			_stpPerformanceCounters = stpPerformanceCounters;
+			SetupCategory();
+		}
+ 
+		private void SetupCategory()
+		{
+			if (!PerformanceCounterCategory.Exists(_stpCategoryName))
+			{
+				CounterCreationDataCollection counters = new CounterCreationDataCollection();
+
+				for (int i = 0; i < _stpPerformanceCounters.Length; i++)
+				{
+					_stpPerformanceCounters[i].AddCounterToCollection(counters);
+				}
+
+				PerformanceCounterCategory.Create(
+					_stpCategoryName, 
+					_stpCategoryHelp, 
+                    PerformanceCounterCategoryType.MultiInstance,
+					counters);
+					
+			}
+		}
+ 
+		// Properties
+		public static STPPerformanceCounters Instance
+		{
+			get
+			{
+				return _instance;
+			}
+		}
+ 	}
+
+	internal class STPInstancePerformanceCounter : IDisposable
+	{
+		// Fields
+        private bool _isDisposed;
+		private PerformanceCounter _pcs;
+
+		// Methods
+		protected STPInstancePerformanceCounter()
+		{
+            _isDisposed = false;
+		}
+
+		public STPInstancePerformanceCounter(
+			string instance, 
+			STPPerformanceCounterType spcType) : this()
+		{
+			STPPerformanceCounters counters = STPPerformanceCounters.Instance;
+			_pcs = new PerformanceCounter(
+				STPPerformanceCounters._stpCategoryName, 
+				counters._stpPerformanceCounters[(int) spcType].Name, 
+				instance, 
+				false);
+			_pcs.RawValue = _pcs.RawValue;
+		}
+
+
+		public void Close()
+		{
+			if (_pcs != null)
+			{
+				_pcs.RemoveInstance();
+				_pcs.Close();
+				_pcs = null;
+			}
+		}
+ 
+		public void Dispose()
+		{
+            Dispose(true);
+		}
+
+        public virtual void Dispose(bool disposing)
+        {
+            if (!_isDisposed)
+            {
+                if (disposing)
+                {
+                    Close();
+                }
+            }
+            _isDisposed = true;
+        }
+ 
+		public virtual void Increment()
+		{
+			_pcs.Increment();
+		}
+ 
+		public virtual void IncrementBy(long val)
+		{
+			_pcs.IncrementBy(val);
+		}
+
+		public virtual void Set(long val)
+		{
+			_pcs.RawValue = val;
+		}
+	}
+
+	internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
+	{
+		// Methods
+		public override void Increment() {}
+		public override void IncrementBy(long value) {}
+		public override void Set(long val) {}
+	}
+
+
+
+	internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters
+	{
+        private bool _isDisposed;
+		// Fields
+		private STPInstancePerformanceCounter[] _pcs;
+		private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;
+
+		// Methods
+		static STPInstancePerformanceCounters()
+		{
+			_stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter();
+		}
+ 
+		public STPInstancePerformanceCounters(string instance)
+		{
+            _isDisposed = false;
+			_pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];
+
+            // 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)
+				{
+					_pcs[i] = new STPInstancePerformanceCounter(
+						instance, 
+						(STPPerformanceCounterType) i);
+				}
+				else
+				{
+					_pcs[i] = _stpInstanceNullPerformanceCounter;
+				}
+			}
+		}
+ 
+
+		public void Close()
+		{
+			if (null != _pcs)
+			{
+				for (int i = 0; i < _pcs.Length; i++)
+				{
+                    if (null != _pcs[i])
+                    {
+                        _pcs[i].Dispose();
+                    }
+				}
+				_pcs = null;
+			}
+		}
+
+		public void Dispose()
+		{
+            Dispose(true);
+		}
+
+        public virtual void Dispose(bool disposing)
+        {
+            if (!_isDisposed)
+            {
+                if (disposing)
+                {
+                    Close();
+                }
+            }
+            _isDisposed = true;
+        }
+ 
+		private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
+		{
+			return _pcs[(int) spcType];
+		}
+
+		public void SampleThreads(long activeThreads, long inUseThreads)
+		{
+			GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads);
+			GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads);
+			GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads);
+
+			GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads);
+			GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads);
+		}
+
+		public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
+		{
+			GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed);
+			GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued);
+			GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed);
+
+			GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued);
+			GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed);
+		}
+
+		public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
+		{
+			GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds);
+			GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment();
+		}
+
+		public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
+		{
+			GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds);
+			GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
+		}
+    }
+#endif
+
+    internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
+	{
+		private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();
+
+		public static NullSTPInstancePerformanceCounters Instance
+		{
+			get { return _instance; }
+		}
+
+ 		public void Close() {}
+		public void Dispose() {}
+ 
+		public void SampleThreads(long activeThreads, long inUseThreads) {}
+		public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
+		public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
+		public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
+        public long InUseThreads
+        {
+            get { return 0; }
+        }
+
+        public long ActiveThreads
+        {
+            get { return 0; }
+        }
+
+        public long WorkItemsQueued
+        {
+            get { return 0; }
+        }
+
+        public long WorkItemsProcessed
+        {
+            get { return 0; }
+        }
+	}
+
+    internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
+    {
+        public void Close() { }
+        public void Dispose() { }
+
+        private long _activeThreads;
+        private long _inUseThreads;
+        private long _workItemsQueued;
+        private long _workItemsProcessed;
+
+        public long InUseThreads
+        {
+            get { return _inUseThreads; }
+        }
+
+        public long ActiveThreads
+        {
+            get { return _activeThreads; }
+        }
+
+        public long WorkItemsQueued
+        {
+            get { return _workItemsQueued; }
+        }
+
+        public long WorkItemsProcessed
+        {
+            get { return _workItemsProcessed; }
+        }
+
+        public void SampleThreads(long activeThreads, long inUseThreads)
+        {
+            _activeThreads = activeThreads;
+            _inUseThreads = inUseThreads;
+        }
+
+        public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
+        {
+            _workItemsQueued = workItemsQueued;
+            _workItemsProcessed = workItemsProcessed;
+        }
+
+        public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
+        {
+            // Not supported
+        }
+
+        public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
+        {
+            // Not supported
+        }
+    }
+}
diff --git a/SSharpSmartThreadPool/STPStartInfo.cs b/SSharpSmartThreadPool/STPStartInfo.cs
new file mode 100644
index 0000000..069ad5b
--- /dev/null
+++ b/SSharpSmartThreadPool/STPStartInfo.cs
@@ -0,0 +1,214 @@
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;
+using WaitHandle = Crestron.SimplSharp.CEventHandle;
+
+namespace Amib.SSharpThreading
+{
+	/// <summary>
+	/// Summary description for STPStartInfo.
+	/// </summary>
+    public class STPStartInfo : WIGStartInfo
+    {
+        private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
+        private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
+        private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
+#if !(WINDOWS_PHONE)
+        private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
+#endif
+        private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
+        private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground;
+        private bool _enableLocalPerformanceCounters;
+        private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName;
+        private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize;
+
+        public STPStartInfo()
+        {
+            _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
+#if !(WINDOWS_PHONE)
+            _threadPriority = SmartThreadPool.DefaultThreadPriority;
+#endif
+            _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
+            _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
+            _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
+        }
+
+	    public STPStartInfo(STPStartInfo stpStartInfo)
+            : base(stpStartInfo)
+        {
+            _idleTimeout = stpStartInfo.IdleTimeout;
+            _minWorkerThreads = stpStartInfo.MinWorkerThreads;
+            _maxWorkerThreads = stpStartInfo.MaxWorkerThreads;
+#if !(WINDOWS_PHONE)
+            _threadPriority = stpStartInfo.ThreadPriority;
+#endif
+            _performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName;
+            _enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters;
+            _threadPoolName = stpStartInfo._threadPoolName;
+            _areThreadsBackground = stpStartInfo.AreThreadsBackground;
+#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)
+            _apartmentState = stpStartInfo._apartmentState;
+#endif
+        }
+	  
+	    /// <summary>
+	    /// Get/Set the idle timeout in milliseconds.
+	    /// If a thread is idle (starved) longer than IdleTimeout then it may quit.
+	    /// </summary>
+	    public virtual int IdleTimeout
+	    {
+	        get { return _idleTimeout; }
+	        set 
+            {
+                ThrowIfReadOnly();
+                _idleTimeout = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set the lower limit of threads in the pool.
+	    /// </summary>
+	    public virtual int MinWorkerThreads
+	    {
+	        get { return _minWorkerThreads; }
+	        set 
+            {
+                ThrowIfReadOnly();
+                _minWorkerThreads = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set the upper limit of threads in the pool.
+	    /// </summary>
+	    public virtual int MaxWorkerThreads
+	    {
+	        get { return _maxWorkerThreads; }
+	        set 
+            {
+                ThrowIfReadOnly();
+                _maxWorkerThreads = value; 
+            }
+	    }
+
+#if !(WINDOWS_PHONE)
+	    /// <summary>
+	    /// Get/Set the scheduling priority of the threads in the pool.
+	    /// The Os handles the scheduling.
+	    /// </summary>
+	    public virtual ThreadPriority ThreadPriority
+	    {
+	        get { return _threadPriority; }
+	        set 
+            {
+                ThrowIfReadOnly();
+                _threadPriority = value; 
+            }
+	    }
+#endif
+        /// <summary>
+        /// Get/Set the thread pool name. Threads will get names depending on this.
+        /// </summary>
+        public virtual string ThreadPoolName {
+            get { return _threadPoolName; }
+            set
+            {
+                ThrowIfReadOnly ();
+                _threadPoolName = value;
+            }
+        }
+
+	    /// <summary>
+	    /// Get/Set the performance counter instance name of this SmartThreadPool
+	    /// The default is null which indicate not to use performance counters at all.
+	    /// </summary>
+	    public virtual string PerformanceCounterInstanceName
+	    {
+	        get { return _performanceCounterInstanceName; }
+	        set 
+            {
+                ThrowIfReadOnly();
+                _performanceCounterInstanceName = value; 
+            }
+	    }
+        /// <summary>
+        /// Enable/Disable the local performance counter.
+        /// This enables the user to get some performance information about the SmartThreadPool 
+        /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.)
+        /// The default is false.
+        /// </summary>
+        public virtual bool EnableLocalPerformanceCounters
+	    {
+	        get { return _enableLocalPerformanceCounters; }
+	        set 
+			{ 
+				ThrowIfReadOnly(); 
+				_enableLocalPerformanceCounters = value; 
+			}
+	    }
+
+        /// <summary>
+        /// Get/Set backgroundness of thread in thread pool.
+        /// </summary>
+	    public virtual bool AreThreadsBackground
+ 	    {
+ 	        get { return _areThreadsBackground; }
+ 	        set
+ 	        {
+ 	            ThrowIfReadOnly ();
+ 	            _areThreadsBackground = value;
+ 	        }
+ 	    }
+
+	    /// <summary>
+        /// Get a readonly version of this STPStartInfo.
+        /// </summary>
+        /// <returns>Returns a readonly reference to this STPStartInfo</returns>
+        public new STPStartInfo AsReadOnly()
+        {
+            return new STPStartInfo(this) { _readOnly = true };
+        }
+
+#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)
+
+        private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState;
+
+        /// <summary>
+        /// Get/Set the apartment state of threads in the thread pool
+        /// </summary>
+        public ApartmentState ApartmentState
+        {
+            get { return _apartmentState; }
+            set
+            {
+                ThrowIfReadOnly();
+                _apartmentState = value;
+            }
+        } 
+
+#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+        
+        /// <summary>
+        /// Get/Set the max stack size of threads in the thread pool
+        /// </summary>
+        public int? MaxStackSize
+        {
+            get { return _maxStackSize; }
+            set
+            {
+                ThrowIfReadOnly();
+                if (value.HasValue && value.Value < 0)
+                {
+                    throw new ArgumentOutOfRangeException("value", "Value must be greater than 0.");
+                }
+                _maxStackSize = value;
+            }
+        }
+#endif
+
+#endif
+    }
+}
diff --git a/SSharpSmartThreadPool/SmartThreadPool.ThreadEntry.cs b/SSharpSmartThreadPool/SmartThreadPool.ThreadEntry.cs
new file mode 100644
index 0000000..57399cf
--- /dev/null
+++ b/SSharpSmartThreadPool/SmartThreadPool.ThreadEntry.cs
@@ -0,0 +1,60 @@
+ï»¿
+using System;
+using Amib.SSharpThreading.Internal;
+
+namespace Amib.SSharpThreading
+{
+    public partial class SmartThreadPool
+    {
+        #region ThreadEntry class
+
+        internal class ThreadEntry
+        {
+            /// <summary>
+            /// The thread creation time
+            /// The value is stored as UTC value.
+            /// </summary>
+            private readonly DateTime _creationTime;
+
+            /// <summary>
+            /// The last time this thread has been running
+            /// It is updated by IAmAlive() method
+            /// The value is stored as UTC value.
+            /// </summary>
+            private DateTime _lastAliveTime;
+
+            /// <summary>
+            /// A reference from each thread in the thread pool to its SmartThreadPool
+            /// object container.
+            /// With this variable a thread can know whatever it belongs to a 
+            /// SmartThreadPool.
+            /// </summary>
+            private readonly SmartThreadPool _associatedSmartThreadPool;
+
+            /// <summary>
+            /// A reference to the current work item a thread from the thread pool 
+            /// is executing.
+            /// </summary>            
+            public WorkItem CurrentWorkItem { get; set; }
+
+            public ThreadEntry(SmartThreadPool stp)
+            {
+                _associatedSmartThreadPool = stp;
+                _creationTime = DateTime.UtcNow;
+                _lastAliveTime = DateTime.MinValue;
+            }
+
+            public SmartThreadPool AssociatedSmartThreadPool
+            {
+                get { return _associatedSmartThreadPool; }
+            }
+
+            public void IAmAlive()
+            {
+                _lastAliveTime = DateTime.UtcNow;
+            }
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/SSharpSmartThreadPool/SmartThreadPool.cs b/SSharpSmartThreadPool/SmartThreadPool.cs
new file mode 100644
index 0000000..f2a6bca
--- /dev/null
+++ b/SSharpSmartThreadPool/SmartThreadPool.cs
@@ -0,0 +1,1820 @@
+#region Release History
+
+// Smart Thread Pool
+// 7 Aug 2004 - Initial release
+//
+// 14 Sep 2004 - Bug fixes 
+//
+// 15 Oct 2004 - Added new features
+//		- Work items return result.
+//		- Support 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.
+//
+// 26 Dec 2004 - Changes:
+//		- Removed static constructors.
+//      - Added finalizers.
+//		- Changed Exceptions so they are serializable.
+//		- Fixed the bug in one of the SmartThreadPool constructors.
+//		- Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. 
+//        The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
+//		- Added PostExecute with options on which cases to call it.
+//      - Added option to dispose of the state objects.
+//      - Added a WaitForIdle() method that waits until the work items queue is empty.
+//      - Added an STPStartInfo class for the initialization of the thread pool.
+//      - Changed exception handling so if a work item throws an exception it 
+//        is rethrown at GetResult(), rather then firing an UnhandledException event.
+//        Note that PostExecute exception are always ignored.
+//
+// 25 Mar 2005 - Changes:
+//		- Fixed lost of work items bug
+//
+// 3 Jul 2005: Changes.
+//      - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. 
+//
+// 16 Aug 2005: Changes.
+//		- Fixed bug where the InUseThreads becomes negative when canceling work items. 
+//
+// 31 Jan 2006 - Changes:
+//		- Added work items priority
+//		- Removed support of chained delegates in callbacks and post executes (nobody really use this)
+//		- Added work items groups
+//		- Added work items groups idle event
+//		- Changed SmartThreadPool.WaitAll() behavior so when it gets empty array
+//		  it returns true rather then throwing an exception.
+//		- Added option to start the STP and the WIG as suspended
+//		- Exception behavior changed, 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
+//
+// 13 Feb 2006 - Changes:
+//		- Added a call to the dispose of the Performance Counter so
+//		  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<T> and Func<T>
+//
+// 07 April 2009 - Changes:
+//      - Added support for Silverlight and Mono
+//      - Added Join, Choice, and Pipe to SmartThreadPool.
+//      - Added local performance counters (for Mono, Silverlight, and WindowsCE)
+//      - Changed duration measures from DateTime.Now to Stopwatch.
+//      - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList<T>.
+//
+// 21 December 2009 - Changes:
+//      - Added work item timeout (passive)
+//
+// 20 August 2012 - Changes:
+//      - Added set name to threads
+//      - Fixed the WorkItemsQueue.Dequeue. 
+//        Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... }
+//      - Fixed SmartThreadPool.Pipe
+//      - Added IsBackground option to threads
+//      - Added ApartmentState to threads
+//      - Fixed thread creation when queuing many work items at the same time.
+//
+// 24 August 2012 - Changes:
+//      - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan
+//      - Added option to set MaxStackSize of threads 
+
+#endregion
+
+using System;
+using System.Security;
+using Amib.SSharpThreading.Internal;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;
+using SSharp.Threading;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+//using SSMono.Diagnostics;
+using ThreadAbortException = System.Threading.ThreadAbortException;
+using ThreadStateException = System.Threading.ThreadStateException;
+
+namespace Amib.SSharpThreading
+	{
+	#region SmartThreadPool class
+	/// <summary>
+	/// Smart thread pool class.
+	/// </summary>
+	public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable
+		{
+		#region Public Default Constants
+
+		/// <summary>
+		/// Default minimum number of threads the thread pool contains. (0)
+		/// </summary>
+		public const int DefaultMinWorkerThreads = 0;
+
+		/// <summary>
+		/// Default maximum number of threads the thread pool contains. (25)
+		/// </summary>
+		public const int DefaultMaxWorkerThreads = 25;
+
+		/// <summary>
+		/// Default idle timeout in milliseconds. (One minute)
+		/// </summary>
+		public const int DefaultIdleTimeout = 60 * 1000; // One minute
+
+		/// <summary>
+		/// Indicate to copy the security context of the caller and then use it in the call. (false)
+		/// </summary>
+		public const bool DefaultUseCallerCallContext = false;
+
+		/// <summary>
+		/// Indicate to copy the HTTP context of the caller and then use it in the call. (false)
+		/// </summary>
+		public const bool DefaultUseCallerHttpContext = false;
+
+		/// <summary>
+		/// Indicate to dispose of the state objects if they support the IDispose interface. (false)
+		/// </summary>
+		public const bool DefaultDisposeOfStateObjects = false;
+
+		/// <summary>
+		/// The default option to run the post execute (CallToPostExecute.Always)
+		/// </summary>
+		public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always;
+
+		/// <summary>
+		/// The default post execute method to run. (None)
+		/// When null it means not to call it.
+		/// </summary>
+		public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback;
+
+		/// <summary>
+		/// The default work item priority (WorkItemPriority.Normal)
+		/// </summary>
+		public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;
+
+		/// <summary>
+		/// The default is to work on work items as soon as they arrive
+		/// and not to wait for the start. (false)
+		/// </summary>
+		public const bool DefaultStartSuspended = false;
+
+		/// <summary>
+		/// The default name to use for the performance counters instance. (null)
+		/// </summary>
+		public static readonly string DefaultPerformanceCounterInstanceName;
+
+#if !(WINDOWS_PHONE)
+
+		/// <summary>
+		/// The default thread priority (ThreadPriority.Normal)
+		/// </summary>
+
+		public const ThreadPriority DefaultThreadPriority = ThreadPriority.MediumPriority;
+#endif
+		/// <summary>
+		/// The default thread pool name. (SmartThreadPool)
+		/// </summary>
+		public const string DefaultThreadPoolName = "SmartThreadPool";
+
+		/// <summary>
+		/// The default Max Stack Size. (SmartThreadPool)
+		/// </summary>
+		public static readonly int? DefaultMaxStackSize = null;
+
+		/// <summary>
+		/// The default fill state with params. (false)
+		/// It is relevant only to QueueWorkItem of Action&lt;...&gt;/Func&lt;...&gt;
+		/// </summary>
+		public const bool DefaultFillStateWithArgs = false;
+
+		/// <summary>
+		/// The default thread backgroundness. (true)
+		/// </summary>
+		public const bool DefaultAreThreadsBackground = true;
+
+#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)
+        /// <summary>
+        /// The default apartment state of a thread in the thread pool. 
+        /// The default is ApartmentState.Unknown which means the STP will not 
+        /// set the apartment of the thread. It will use the .NET default.
+        /// </summary>
+        public const ApartmentState DefaultApartmentState = ApartmentState.Unknown;
+#endif
+
+		#endregion
+
+		#region Member Variables
+
+		/// <summary>
+		/// Dictionary of all the threads in the thread pool.
+		/// </summary>
+		private readonly SynchronizedDictionary<Thread, ThreadEntry> _workerThreads = new SynchronizedDictionary<Thread, ThreadEntry> ();
+
+		/// <summary>
+		/// Queue of work items.
+		/// </summary>
+		private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue ();
+
+		/// <summary>
+		/// Count the work items handled.
+		/// Used by the performance counter.
+		/// </summary>
+		private int _workItemsProcessed;
+
+		/// <summary>
+		/// Number of threads that currently work (not idle).
+		/// </summary>
+		private int _inUseWorkerThreads;
+
+		/// <summary>
+		/// Stores a copy of the original STPStartInfo.
+		/// It is used to change the MinThread and MaxThreads
+		/// </summary>
+		private STPStartInfo _stpStartInfo;
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		private int _currentWorkItemsCount;
+
+		/// <summary>
+		/// Signaled when the thread pool is idle, i.e. no thread is busy
+		/// and the work items queue is empty
+		/// </summary>
+		//private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
+		private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent (true);
+
+		/// <summary>
+		/// An event to signal all the threads to quit immediately.
+		/// </summary>
+		//private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
+		private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent (false);
+
+		/// <summary>
+		/// A flag to indicate if the Smart Thread Pool is now suspended.
+		/// </summary>
+		private bool _isSuspended;
+
+		/// <summary>
+		/// A flag to indicate the threads to quit.
+		/// </summary>
+		private bool _shutdown;
+
+		/// <summary>
+		/// Counts the threads created in the pool.
+		/// It is used to name the threads.
+		/// </summary>
+		private int _threadCounter;
+
+		/// <summary>
+		/// Indicate that the SmartThreadPool has been disposed
+		/// </summary>
+		private bool _isDisposed;
+
+		/// <summary>
+		/// Holds all the WorkItemsGroup instaces that have at least one 
+		/// work item int the SmartThreadPool
+		/// This variable is used in case of Shutdown
+		/// </summary>
+		private readonly SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup> _workItemsGroups = new SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup> ();
+
+		/// <summary>
+		/// A common object for all the work items int the STP
+		/// so we can mark them to cancel in O(1)
+		/// </summary>
+		private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup ();
+
+		/// <summary>
+		/// Windows STP performance counters
+		/// </summary>
+		private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance;
+
+		/// <summary>
+		/// Local STP performance counters
+		/// </summary>
+		private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance;
+
+#if (WINDOWS_PHONE) 
+        private static readonly Dictionary<int, ThreadEntry> _threadEntries = new Dictionary<int, ThreadEntry>();
+#elif (_WINDOWS_CE)
+		private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot ();
+#else
+        [ThreadStatic]
+        private static ThreadEntry _threadEntry;
+
+#endif
+
+		/// <summary>
+		/// An event to call after a thread is created, but before 
+		/// it's first use.
+		/// </summary>
+		private event ThreadInitializationHandler _onThreadInitialization;
+
+		/// <summary>
+		/// An event to call when a thread is about to exit, after 
+		/// it is no longer belong to the pool.
+		/// </summary>
+		private event ThreadTerminationHandler _onThreadTermination;
+
+		#endregion
+
+		#region Per thread properties
+
+		/// <summary>
+		/// A reference to the current work item a thread from the thread pool 
+		/// is executing.
+		/// </summary>
+		internal static ThreadEntry CurrentThreadEntry
+			{
+#if (WINDOWS_PHONE)
+            get
+            {
+                lock(_threadEntries)
+                {
+                    ThreadEntry threadEntry;
+                    if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry))
+                    {
+                        return threadEntry;
+                    }
+                }
+                return null;
+            }
+            set
+            {
+                lock(_threadEntries)
+                {
+                    _threadEntries[Thread.CurrentThread.ManagedThreadId] = value;
+                }
+            }
+#elif (_WINDOWS_CE)
+			get
+				{
+				//Thread.CurrentThread.ManagedThreadId
+				return Thread.GetData (_threadEntrySlot) as ThreadEntry;
+				}
+			set
+				{
+				Thread.SetData (_threadEntrySlot, value);
+				}
+#else
+            get
+            {
+                return _threadEntry;
+            }
+            set
+            {
+                _threadEntry = value;
+            }
+#endif
+			}
+		#endregion
+
+		#region Construction and Finalization
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		public SmartThreadPool ()
+			{
+			_stpStartInfo = new STPStartInfo ();
+			Initialize ();
+			}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="idleTimeout">Idle timeout in milliseconds</param>
+		public SmartThreadPool (int idleTimeout)
+			{
+			_stpStartInfo = new STPStartInfo
+			{
+				IdleTimeout = idleTimeout,
+			};
+			Initialize ();
+			}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="idleTimeout">Idle timeout in milliseconds</param>
+		/// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
+		public SmartThreadPool (
+			int idleTimeout,
+			int maxWorkerThreads)
+			{
+			_stpStartInfo = new STPStartInfo
+			{
+				IdleTimeout = idleTimeout,
+				MaxWorkerThreads = maxWorkerThreads,
+			};
+			Initialize ();
+			}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="idleTimeout">Idle timeout in milliseconds</param>
+		/// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
+		/// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
+		public SmartThreadPool (
+			int idleTimeout,
+			int maxWorkerThreads,
+			int minWorkerThreads)
+			{
+			_stpStartInfo = new STPStartInfo
+			{
+				IdleTimeout = idleTimeout,
+				MaxWorkerThreads = maxWorkerThreads,
+				MinWorkerThreads = minWorkerThreads,
+			};
+			Initialize ();
+			}
+
+		/// <summary>
+		/// Constructor
+		/// </summary>
+		/// <param name="stpStartInfo">A SmartThreadPool configuration that overrides the default behavior</param>
+		public SmartThreadPool (STPStartInfo stpStartInfo)
+			{
+			_stpStartInfo = new STPStartInfo (stpStartInfo);
+			Initialize ();
+			}
+
+		private void Initialize ()
+			{
+			Name = _stpStartInfo.ThreadPoolName;
+			ValidateSTPStartInfo ();
+
+			// _stpStartInfoRW stores a read/write copy of the STPStartInfo.
+			// Actually only MaxWorkerThreads and MinWorkerThreads are overwritten
+
+			_isSuspended = _stpStartInfo.StartSuspended;
+
+#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE)
+			if (null != _stpStartInfo.PerformanceCounterInstanceName)
+				{
+				throw new NotSupportedException ("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters");
+				}
+#else
+            if (null != _stpStartInfo.PerformanceCounterInstanceName)
+            {
+                try
+                {
+                    _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
+                }
+                catch (Exception e)
+                {
+                    //Debug.WriteLine("Unable to create Performance Counters: " + e);
+                    _windowsPCs = NullSTPInstancePerformanceCounters.Instance;
+                }
+            }
+#endif
+
+			if (_stpStartInfo.EnableLocalPerformanceCounters)
+				{
+				_localPCs = new LocalSTPInstancePerformanceCounters ();
+				}
+
+			// 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);
+			threadsCount -= _workerThreads.Count;
+			if (threadsCount > 0)
+				{
+				StartThreads (threadsCount);
+				}
+			}
+
+		private void ValidateSTPStartInfo ()
+			{
+			if (_stpStartInfo.MinWorkerThreads < 0)
+				{
+				throw new ArgumentOutOfRangeException (
+					"MinWorkerThreads", "MinWorkerThreads cannot be negative");
+				}
+
+			if (_stpStartInfo.MaxWorkerThreads <= 0)
+				{
+				throw new ArgumentOutOfRangeException (
+					"MaxWorkerThreads", "MaxWorkerThreads must be greater than zero");
+				}
+
+			if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads)
+				{
+				throw new ArgumentOutOfRangeException (
+					"MinWorkerThreads, maxWorkerThreads",
+					"MaxWorkerThreads must be greater or equal to MinWorkerThreads");
+				}
+			}
+
+		private static void ValidateCallback (Delegate callback)
+			{
+			if (callback.GetInvocationList ().Length > 1)
+				{
+				throw new NotSupportedException ("SmartThreadPool doesn't support delegates chains");
+				}
+			}
+
+		#endregion
+
+		#region Thread Processing
+
+		/// <summary>
+		/// Waits on the queue for a work item, shutdown, or timeout.
+		/// </summary>
+		/// <returns>
+		/// Returns the WaitingCallback or null in case of timeout or shutdown.
+		/// </returns>
+		private WorkItem Dequeue ()
+			{
+			WorkItem workItem =
+				_workItemsQueue.DequeueWorkItem (_stpStartInfo.IdleTimeout, _shuttingDownEvent);
+
+			return workItem;
+			}
+
+		/// <summary>
+		/// Put a new work item in the queue
+		/// </summary>
+		/// <param name="workItem">A work item to queue</param>
+		internal override void Enqueue (WorkItem workItem)
+			{
+			// Make sure the workItem is not null
+			//Debug.Assert (null != workItem);
+
+			IncrementWorkItemsCount ();
+
+			workItem.CanceledSmartThreadPool = _canceledSmartThreadPool;
+			_workItemsQueue.EnqueueWorkItem (workItem);
+			workItem.WorkItemIsQueued ();
+
+			// If all the threads are busy then try to create a new one
+			if (_currentWorkItemsCount > _workerThreads.Count)
+				{
+				StartThreads (1);
+				}
+			}
+
+		private void IncrementWorkItemsCount ()
+			{
+			_windowsPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
+			_localPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
+			int count = Interlocked.Increment (ref _currentWorkItemsCount);
+			//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
+			if (count == 1)
+				{
+				IsIdle = false;
+				_isIdleWaitHandle.Reset ();
+				}
+			}
+
+		private void DecrementWorkItemsCount ()
+			{
+			int count = Interlocked.Decrement (ref _currentWorkItemsCount);
+			//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
+			if (count == 0)
+				{
+				IsIdle = true;
+				_isIdleWaitHandle.Set ();
+				}
+
+			Interlocked.Increment (ref _workItemsProcessed);
+
+			if (!_shutdown)
+				{
+				// The counter counts even if the work item was cancelled
+				_windowsPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
+				_localPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
+				}
+
+			}
+
+		internal void RegisterWorkItemsGroup (IWorkItemsGroup workItemsGroup)
+			{
+			_workItemsGroups[workItemsGroup] = workItemsGroup;
+			}
+
+		internal void UnregisterWorkItemsGroup (IWorkItemsGroup workItemsGroup)
+			{
+			if (_workItemsGroups.Contains (workItemsGroup))
+				{
+				_workItemsGroups.Remove (workItemsGroup);
+				}
+			}
+
+		/// <summary>
+		/// Inform that the current thread is about to quit or quiting.
+		/// The same thread may call this method more than once.
+		/// </summary>
+		private void InformCompleted ()
+			{
+			// There is no need to lock the two methods together 
+			// since only the current thread removes itself
+			// and the _workerThreads is a synchronized dictionary
+			if (_workerThreads.Contains (Thread.CurrentThread))
+				{
+				_workerThreads.Remove (Thread.CurrentThread);
+				_windowsPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
+				_localPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
+				}
+			}
+
+		/// <summary>
+		/// Starts new threads
+		/// </summary>
+		/// <param name="threadsCount">The number of threads to start</param>
+		private void StartThreads (int threadsCount)
+			{
+			if (_isSuspended)
+				{
+				return;
+				}
+
+			lock (_workerThreads.SyncRoot)
+				{
+				// Don't start threads on shut down
+				if (_shutdown)
+					{
+					return;
+					}
+
+				for (int i = 0; i < threadsCount; ++i)
+					{
+					// Don't create more threads then the upper limit
+					if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads)
+						{
+						return;
+						}
+
+					// Create a new thread
+
+#if (_SILVERLIGHT) || (WINDOWS_PHONE) || (_WINDOWS_CE)
+#if SSHARP
+					var te = new ThreadEntry (this);
+
+					Thread workerThread = new Thread (ProcessQueuedItems, te, Thread.eThreadStartOptions.CreateSuspended);
+#else
+					Thread workerThread = new Thread (ProcessQueuedItems, null);
+#endif
+#else
+                    Thread workerThread =
+                        _stpStartInfo.MaxStackSize.HasValue
+                        ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value)
+                        : new Thread(ProcessQueuedItems);
+#endif
+					// Configure the new thread and start it
+					workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
+#if !SSHARP
+					workerThread.IsBackground = _stpStartInfo.AreThreadsBackground;
+#endif
+
+#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE)
+                    if (_stpStartInfo.ApartmentState != ApartmentState.Unknown)
+                    {
+                        workerThread.SetApartmentState(_stpStartInfo.ApartmentState);
+                    }
+#endif
+
+#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+					workerThread.Priority = _stpStartInfo.ThreadPriority;
+#endif
+					workerThread.Start ();
+					++_threadCounter;
+
+#if SSHARP
+					_workerThreads.AddPendingEntry ();
+#else
+					// Add it to the dictionary and update its creation time.
+					_workerThreads[workerThread] = new ThreadEntry (this);
+#endif
+
+					_windowsPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
+					_localPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
+					}
+				}
+			}
+
+		/// <summary>
+		/// A worker thread method that processes work items from the work items queue.
+		/// </summary>
+		private object ProcessQueuedItems (object state)
+			{
+#if SSHARP
+			var te = (ThreadEntry)state;
+			_workerThreads[Thread.CurrentThread] = te;
+			_workerThreads.RemovePendingEntry ();
+#endif
+
+			// Keep the entry of the dictionary as thread's variable to avoid the synchronization locks
+			// of the dictionary.
+			CurrentThreadEntry = _workerThreads[Thread.CurrentThread];
+
+			FireOnThreadInitialization ();
+
+			try
+				{
+				bool bInUseWorkerThreadsWasIncremented = false;
+
+				// Process until shutdown.
+				while (!_shutdown)
+					{
+					// Update the last time this thread was seen alive.
+					// It's good for debugging.
+					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.
+					CurrentThreadEntry.IAmAlive ();
+
+					// On timeout or shut down.
+					if (null == workItem)
+						{
+						// Double lock for quit.
+						if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
+							{
+							lock (_workerThreads.SyncRoot)
+								{
+								if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
+									{
+									// 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;
+									}
+								}
+							}
+						}
+
+					// If we didn't quit then skip to the next iteration.
+					if (null == workItem)
+						{
+						continue;
+						}
+
+					try
+						{
+						// 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.
+						// The cancel mechanism doesn't delete items from the queue,  
+						// it marks the work item as canceled, and when the work item
+						// is dequeued, we just skip it.
+						// If the post execute of work item is set to always or to
+						// call when the work item is canceled then the StartingWorkItem()
+						// will return true, so the post execute can run.
+						if (!workItem.StartingWorkItem ())
+							{
+							continue;
+							}
+
+						// Execute the callback.  Make sure to accurately
+						// record how many callbacks are currently executing.
+						int inUseWorkerThreads = Interlocked.Increment (ref _inUseWorkerThreads);
+						_windowsPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);
+						_localPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);
+
+						// Mark that the _inUseWorkerThreads incremented, so in the finally{}
+						// statement we will decrement it correctly.
+						bInUseWorkerThreadsWasIncremented = true;
+
+						workItem.FireWorkItemStarted ();
+
+						ExecuteWorkItem (workItem);
+						}
+					catch (Exception ex)
+						{
+						ex.GetHashCode ();
+						// Do nothing
+						}
+					finally
+						{
+						workItem.DisposeOfState ();
+
+						// Set the CurrentWorkItem to null, since we 
+						// no longer run user's code.
+						CurrentThreadEntry.CurrentWorkItem = null;
+
+						// Decrement the _inUseWorkerThreads only if we had 
+						// incremented it. Note the cancelled work items don't
+						// increment _inUseWorkerThreads.
+						if (bInUseWorkerThreadsWasIncremented)
+							{
+							int inUseWorkerThreads = Interlocked.Decrement (ref _inUseWorkerThreads);
+							_windowsPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);
+							_localPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);
+							}
+
+						// Notify that the work item has been completed.
+						// WorkItemsGroup may enqueue their next work item.
+						workItem.FireWorkItemCompleted ();
+
+						// Decrement the number of work items here so the idle 
+						// ManualResetEvent won't fluctuate.
+						DecrementWorkItemsCount ();
+						}
+					}
+				}
+#if SSHARP
+			catch (Exception tae)
+#else
+			catch (ThreadAbortException tae)
+#endif
+				{
+#if SSHARP
+				Exception e = tae;
+
+				if (tae is ThreadAbortException)
+					{
+#endif
+				tae.GetHashCode ();
+				// Handle the abort exception gracfully.
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+				Thread.ResetAbort();
+#endif
+
+				}
+#if !SSHARP
+			catch (Exception e)
+				{
+#endif
+				//Debug.Assert (null != e);
+				}
+			finally
+				{
+				InformCompleted ();
+				FireOnThreadTermination ();
+				}
+
+			return null;
+			}
+
+		private void ExecuteWorkItem (WorkItem workItem)
+			{
+			_windowsPCs.SampleWorkItemsWaitTime (workItem.WaitingTime);
+			_localPCs.SampleWorkItemsWaitTime (workItem.WaitingTime);
+			try
+				{
+				workItem.Execute ();
+				}
+			finally
+				{
+				_windowsPCs.SampleWorkItemsProcessTime (workItem.ProcessTime);
+				_localPCs.SampleWorkItemsProcessTime (workItem.ProcessTime);
+				}
+			}
+
+
+		#endregion
+
+		#region Public Methods
+
+		private void ValidateWaitForIdle ()
+			{
+			if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this)
+				{
+				throw new NotSupportedException (
+					"WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
+				}
+			}
+
+		internal static void ValidateWorkItemsGroupWaitForIdle (IWorkItemsGroup 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 causes a deadlock");
+				}
+			}
+
+		[MethodImpl (MethodImplOptions.NoInlining)]
+		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 causes a deadlock");
+				}
+			}
+
+		/// <summary>
+		/// Force the SmartThreadPool to shutdown
+		/// </summary>
+		public void Shutdown ()
+			{
+			Shutdown (true, 0);
+			}
+
+		/// <summary>
+		/// Force the SmartThreadPool to shutdown with timeout
+		/// </summary>
+		public void Shutdown (bool forceAbort, TimeSpan timeout)
+			{
+			Shutdown (forceAbort, (int)timeout.TotalMilliseconds);
+			}
+
+		/// <summary>
+		/// Empties the queue of work items and abort the threads in the pool.
+		/// </summary>
+		public void Shutdown (bool forceAbort, int millisecondsTimeout)
+			{
+			ValidateNotDisposed ();
+
+			ISTPInstancePerformanceCounters pcs = _windowsPCs;
+
+			if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs)
+				{
+				// Set the _pcs to "null" to stop updating the performance
+				// counters
+				_windowsPCs = NullSTPInstancePerformanceCounters.Instance;
+
+				pcs.Dispose ();
+				}
+
+			Thread[] threads;
+			lock (_workerThreads.SyncRoot)
+				{
+				// Shutdown the work items queue
+				_workItemsQueue.Dispose ();
+
+				// Signal the threads to exit
+				_shutdown = true;
+				_shuttingDownEvent.Set ();
+
+				// Make a copy of the threads' references in the pool
+#if SSHARP
+				threads = new Thread[_workerThreads.RealCount];
+#else
+				threads = new Thread[_workerThreads.Count];
+#endif
+				_workerThreads.Keys.CopyTo (threads, 0);
+				}
+
+			int millisecondsLeft = millisecondsTimeout;
+			Stopwatch stopwatch = Stopwatch.StartNew ();
+			//DateTime start = DateTime.UtcNow;
+			bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
+			bool timeout = false;
+
+			// Each iteration we update the time left for the timeout.
+			foreach (Thread thread in threads)
+				{
+				// Join don't work with negative numbers
+				if (!waitInfinitely && (millisecondsLeft < 0))
+					{
+					timeout = true;
+					break;
+					}
+
+				// Wait for the thread to terminate
+#if SSHARP
+				bool success;
+				try
+					{
+					success = thread.Join (millisecondsLeft);
+					}
+				catch (NullReferenceException)
+					{
+					success = true;
+					}
+#else
+				bool success = thread.Join (millisecondsLeft);
+#endif
+				if (!success)
+					{
+					timeout = true;
+					break;
+					}
+
+				if (!waitInfinitely)
+					{
+					// Update the time left to wait
+					//TimeSpan ts = DateTime.UtcNow - start;
+					millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
+					}
+				}
+
+			if (timeout && forceAbort)
+				{
+				// Abort the threads in the pool
+				foreach (Thread thread in threads)
+					{
+
+#if SSHARP
+					bool running = false;
+					try
+						{
+						running = thread != null && !thread.Join (0);
+						}
+					catch (NullReferenceException)
+						{
+						
+						}
+
+					if (running)
+#else
+					if ((thread != null)
+#if (_WINDOWS_CE)
+						&& !thread.Join (0)
+#else
+                        && thread.IsAlive
+#endif
+)
+#endif
+						{
+						try
+							{
+							thread.Abort (); // Shutdown
+							}
+#if SSHARP
+						catch (Exception e)
+							{
+							if (e.GetType () != typeof (System.Threading.ThreadAbortException) && e.GetType () != typeof (SecurityException))
+								throw;
+							e.GetHashCode ();
+							}
+#else
+						catch (SecurityException e)
+							{
+							e.GetHashCode ();
+							}
+						catch (ThreadStateException ex)
+							{
+							ex.GetHashCode ();
+							// In case the thread has been terminated 
+							// after the check if it is alive.
+							}
+#endif
+						}
+					}
+				}
+			}
+
+		/// <summary>
+		/// Wait for all work items to complete
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <returns>
+		/// true when every work item in workItemResults has completed; otherwise false.
+		/// </returns>
+		public static bool WaitAll (
+			IWaitableResult[] waitableResults)
+			{
+			return WaitAll (waitableResults, Timeout.Infinite, true);
+			}
+
+		/// <summary>
+		/// Wait for all work items to complete
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <returns>
+		/// true when every work item in workItemResults has completed; otherwise false.
+		/// </returns>
+		public static bool WaitAll (
+			IWaitableResult[] waitableResults,
+			TimeSpan timeout,
+			bool exitContext)
+			{
+			return WaitAll (waitableResults, (int)timeout.TotalMilliseconds, exitContext);
+			}
+
+		/// <summary>
+		/// Wait for all work items to complete
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
+		/// <returns>
+		/// true when every work item in workItemResults has completed; otherwise false.
+		/// </returns>
+		public static bool WaitAll (
+			IWaitableResult[] waitableResults,
+			TimeSpan timeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle)
+			{
+			return WaitAll (waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
+			}
+
+		/// <summary>
+		/// Wait for all work items to complete
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <returns>
+		/// true when every work item in workItemResults has completed; otherwise false.
+		/// </returns>
+		public static bool WaitAll (
+			IWaitableResult[] waitableResults,
+			int millisecondsTimeout,
+			bool exitContext)
+			{
+			return WorkItem.WaitAll (waitableResults, millisecondsTimeout, exitContext, null);
+			}
+
+		/// <summary>
+		/// Wait for all work items to complete
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
+		/// <returns>
+		/// true when every work item in workItemResults has completed; otherwise false.
+		/// </returns>
+		public static bool WaitAll (
+			IWaitableResult[] waitableResults,
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle)
+			{
+			return WorkItem.WaitAll (waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
+			}
+
+
+		/// <summary>
+		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <returns>
+		/// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
+		/// </returns>
+		public static int WaitAny (
+			IWaitableResult[] waitableResults)
+			{
+			return WaitAny (waitableResults, Timeout.Infinite, true);
+			}
+
+		/// <summary>
+		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <returns>
+		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
+		/// </returns>
+		public static int WaitAny (
+			IWaitableResult[] waitableResults,
+			TimeSpan timeout,
+			bool exitContext)
+			{
+			return WaitAny (waitableResults, (int)timeout.TotalMilliseconds, exitContext);
+			}
+
+		/// <summary>
+		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
+		/// <returns>
+		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
+		/// </returns>
+		public static int WaitAny (
+			IWaitableResult[] waitableResults,
+			TimeSpan timeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle)
+			{
+			return WaitAny (waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
+			}
+
+		/// <summary>
+		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <returns>
+		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
+		/// </returns>
+		public static int WaitAny (
+			IWaitableResult[] waitableResults,
+			int millisecondsTimeout,
+			bool exitContext)
+			{
+			return WorkItem.WaitAny (waitableResults, millisecondsTimeout, exitContext, null);
+			}
+
+		/// <summary>
+		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
+		/// <returns>
+		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
+		/// </returns>
+		public static int WaitAny (
+			IWaitableResult[] waitableResults,
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle)
+			{
+			return WorkItem.WaitAny (waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
+			}
+
+		/// <summary>
+		/// Creates a new WorkItemsGroup.
+		/// </summary>
+		/// <param name="concurrency">The number of work items that can be run concurrently</param>
+		/// <returns>A reference to the WorkItemsGroup</returns>
+		public IWorkItemsGroup CreateWorkItemsGroup (int concurrency)
+			{
+			IWorkItemsGroup workItemsGroup = new WorkItemsGroup (this, concurrency, _stpStartInfo);
+			return workItemsGroup;
+			}
+
+		/// <summary>
+		/// Creates a new WorkItemsGroup.
+		/// </summary>
+		/// <param name="concurrency">The number of work items that can be run concurrently</param>
+		/// <param name="wigStartInfo">A WorkItemsGroup configuration that overrides the default behavior</param>
+		/// <returns>A reference to the WorkItemsGroup</returns>
+		public IWorkItemsGroup CreateWorkItemsGroup (int concurrency, WIGStartInfo wigStartInfo)
+			{
+			IWorkItemsGroup workItemsGroup = new WorkItemsGroup (this, concurrency, wigStartInfo);
+			return workItemsGroup;
+			}
+
+		#region Fire Thread's Events
+
+		private void FireOnThreadInitialization ()
+			{
+			if (null != _onThreadInitialization)
+				{
+				foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList ())
+					{
+					try
+						{
+						tih ();
+						}
+					catch (Exception e)
+						{
+						e.GetHashCode ();
+						//Debug.Assert (false);
+						throw;
+						}
+					}
+				}
+			}
+
+		private void FireOnThreadTermination ()
+			{
+			if (null != _onThreadTermination)
+				{
+				foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList ())
+					{
+					try
+						{
+						tth ();
+						}
+					catch (Exception e)
+						{
+						e.GetHashCode ();
+						//Debug.Assert (false);
+						throw;
+						}
+					}
+				}
+			}
+
+		#endregion
+
+		/// <summary>
+		/// This event is fired when a thread is created.
+		/// Use it to initialize a thread before the work items use it.
+		/// </summary>
+		public event ThreadInitializationHandler OnThreadInitialization
+			{
+			add { _onThreadInitialization += value; }
+			remove { _onThreadInitialization -= value; }
+			}
+
+		/// <summary>
+		/// This event is fired when a thread is terminating.
+		/// Use it for cleanup.
+		/// </summary>
+		public event ThreadTerminationHandler OnThreadTermination
+			{
+			add { _onThreadTermination += value; }
+			remove { _onThreadTermination -= value; }
+			}
+
+
+		internal void CancelAbortWorkItemsGroup (WorkItemsGroup wig)
+			{
+			ThreadEntry[] threadEntries;
+			lock (_workerThreads.SyncRoot)
+				{
+				threadEntries = new ThreadEntry[_workerThreads.RealCount];
+				_workerThreads.Values.CopyTo (threadEntries, 0);
+				}
+
+			foreach (ThreadEntry threadEntry in threadEntries)
+				{
+				WorkItem workItem = threadEntry.CurrentWorkItem;
+				if (null != workItem &&
+					workItem.WasQueuedBy (wig) &&
+					!workItem.IsCanceled)
+					{
+					threadEntry.CurrentWorkItem.GetWorkItemResult ().Cancel (true);
+					}
+				}
+			}
+
+		#endregion
+
+		#region Properties
+
+		/// <summary>
+		/// Get/Set the lower limit of threads in the pool.
+		/// </summary>
+		public int MinThreads
+			{
+			get
+				{
+				ValidateNotDisposed ();
+				return _stpStartInfo.MinWorkerThreads;
+				}
+			set
+				{
+				//Debug.Assert (value >= 0);
+				//Debug.Assert (value <= _stpStartInfo.MaxWorkerThreads);
+				if (_stpStartInfo.MaxWorkerThreads < value)
+					{
+					_stpStartInfo.MaxWorkerThreads = value;
+					}
+				_stpStartInfo.MinWorkerThreads = value;
+				StartOptimalNumberOfThreads ();
+				}
+			}
+
+		/// <summary>
+		/// Get/Set the upper limit of threads in the pool.
+		/// </summary>
+		public int MaxThreads
+			{
+			get
+				{
+				ValidateNotDisposed ();
+				return _stpStartInfo.MaxWorkerThreads;
+				}
+
+			set
+				{
+				//Debug.Assert (value > 0);
+				//Debug.Assert (value >= _stpStartInfo.MinWorkerThreads);
+				if (_stpStartInfo.MinWorkerThreads > value)
+					{
+					_stpStartInfo.MinWorkerThreads = value;
+					}
+				_stpStartInfo.MaxWorkerThreads = value;
+				StartOptimalNumberOfThreads ();
+				}
+			}
+		/// <summary>
+		/// Get the number of threads in the thread pool.
+		/// Should be between the lower and the upper limits.
+		/// </summary>
+		public int ActiveThreads
+			{
+			get
+				{
+				ValidateNotDisposed ();
+				return _workerThreads.Count;
+				}
+			}
+
+		/// <summary>
+		/// Get the number of busy (not idle) threads in the thread pool.
+		/// </summary>
+		public int InUseThreads
+			{
+			get
+				{
+				ValidateNotDisposed ();
+				return _inUseWorkerThreads;
+				}
+			}
+
+		/// <summary>
+		/// 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.
+		/// </summary>
+		public static bool IsWorkItemCanceled
+			{
+			get
+				{
+				return CurrentThreadEntry.CurrentWorkItem.IsCanceled;
+				}
+			}
+
+		/// <summary>
+		/// Checks if the work item has been cancelled, and if yes then abort the thread.
+		/// Can be used with Cancel and timeout
+		/// </summary>
+		public static void AbortOnWorkItemCancel ()
+			{
+			if (IsWorkItemCanceled)
+				{
+				Thread.CurrentThread.Abort ();
+				}
+			}
+
+		/// <summary>
+		/// Thread Pool start information (readonly)
+		/// </summary>
+		public STPStartInfo STPStartInfo
+			{
+			get
+				{
+				return _stpStartInfo.AsReadOnly ();
+				}
+			}
+
+		public bool IsShuttingdown
+			{
+			get { return _shutdown; }
+			}
+
+		/// <summary>
+		/// Return the local calculated performance counters
+		/// Available only if STPStartInfo.EnableLocalPerformanceCounters is true.
+		/// </summary>
+		public ISTPPerformanceCountersReader PerformanceCountersReader
+			{
+			get { return (ISTPPerformanceCountersReader)_localPCs; }
+			}
+		#endregion
+
+		#region IDisposable Members
+
+		public void Dispose ()
+			{
+			if (!_isDisposed)
+				{
+				if (!_shutdown)
+					{
+					Shutdown ();
+					}
+
+				if (null != _shuttingDownEvent)
+					{
+					_shuttingDownEvent.Close ();
+					_shuttingDownEvent = null;
+					}
+				_workerThreads.Clear ();
+
+				if (null != _isIdleWaitHandle)
+					{
+					_isIdleWaitHandle.Close ();
+					_isIdleWaitHandle = null;
+					}
+
+				_isDisposed = true;
+				}
+			}
+
+		private void ValidateNotDisposed ()
+			{
+			if (_isDisposed)
+				{
+				throw new ObjectDisposedException (GetType ().ToString (), "The SmartThreadPool has been shutdown");
+				}
+			}
+		#endregion
+
+		#region WorkItemsGroupBase Overrides
+
+		/// <summary>
+		/// Get/Set the maximum number of work items that execute cocurrency on the thread pool
+		/// </summary>
+		public override int Concurrency
+			{
+			get { return MaxThreads; }
+			set { MaxThreads = value; }
+			}
+
+		/// <summary>
+		/// Get the number of work items in the queue.
+		/// </summary>
+		public override int WaitingCallbacks
+			{
+			get
+				{
+				ValidateNotDisposed ();
+				return _workItemsQueue.Count;
+				}
+			}
+
+		/// <summary>
+		/// Get an array with all the state objects of the currently running items.
+		/// The array represents a snap shot and impact performance.
+		/// </summary>
+		public override object[] GetStates ()
+			{
+			object[] states = _workItemsQueue.GetStates ();
+			return states;
+			}
+
+		/// <summary>
+		/// WorkItemsGroup start information (readonly)
+		/// </summary>
+		public override WIGStartInfo WIGStartInfo
+			{
+			get { return _stpStartInfo.AsReadOnly (); }
+			}
+
+		/// <summary>
+		/// Start the thread pool if it was started suspended.
+		/// If it is already running, this method is ignored.
+		/// </summary>
+		public override void Start ()
+			{
+			if (!_isSuspended)
+				{
+				return;
+				}
+			_isSuspended = false;
+
+			ICollection workItemsGroups = _workItemsGroups.Values;
+			foreach (WorkItemsGroup workItemsGroup in workItemsGroups)
+				{
+				workItemsGroup.OnSTPIsStarting ();
+				}
+
+			StartOptimalNumberOfThreads ();
+			}
+
+		/// <summary>
+		/// Cancel all work items using thread abortion
+		/// </summary>
+		/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
+		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)
+				{
+				ThreadEntry[] threadEntries;
+				lock (_workerThreads.SyncRoot)
+					{
+					threadEntries = new ThreadEntry[_workerThreads.RealCount];
+					_workerThreads.Values.CopyTo (threadEntries, 0);
+					}
+
+				foreach (ThreadEntry threadEntry in threadEntries)
+					{
+					WorkItem workItem = threadEntry.CurrentWorkItem;
+					if (null != workItem &&
+						threadEntry.AssociatedSmartThreadPool == this &&
+						!workItem.IsCanceled)
+						{
+						threadEntry.CurrentWorkItem.GetWorkItemResult ().Cancel (true);
+						}
+					}
+				}
+			}
+
+		/// <summary>
+		/// Wait for the thread pool to be idle
+		/// </summary>
+		public override bool WaitForIdle (int millisecondsTimeout)
+			{
+			ValidateWaitForIdle ();
+			return STPEventWaitHandle.WaitOne (_isIdleWaitHandle, millisecondsTimeout, false);
+			}
+
+		/// <summary>
+		/// This event is fired when all work items are completed.
+		/// (When IsIdle changes to true)
+		/// This event only work on WorkItemsGroup. On SmartThreadPool
+		/// it throws the NotImplementedException.
+		/// </summary>
+		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
+
+		#region Join, Choice, Pipe, etc.
+
+		/// <summary>
+		/// Executes all actions in parallel.
+		/// Returns when they all finish.
+		/// </summary>
+		/// <param name="actions">Actions to execute</param>
+		public void Join (IEnumerable<Action> actions)
+			{
+			WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
+			IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup (int.MaxValue, wigStartInfo);
+			foreach (Action action in actions)
+				{
+				workItemsGroup.QueueWorkItem (action);
+				}
+			workItemsGroup.Start ();
+			workItemsGroup.WaitForIdle ();
+			}
+
+		/// <summary>
+		/// Executes all actions in parallel.
+		/// Returns when they all finish.
+		/// </summary>
+		/// <param name="actions">Actions to execute</param>
+		public void Join (params Action[] actions)
+			{
+			Join ((IEnumerable<Action>)actions);
+			}
+
+		private class ChoiceIndex
+			{
+			public int _index = -1;
+			}
+
+		/// <summary>
+		/// Executes all actions in parallel
+		/// Returns when the first one completes
+		/// </summary>
+		/// <param name="actions">Actions to execute</param>
+		public int Choice (IEnumerable<Action> actions)
+			{
+			WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
+			IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup (int.MaxValue, wigStartInfo);
+
+			ManualResetEvent anActionCompleted = new ManualResetEvent (false);
+
+			ChoiceIndex choiceIndex = new ChoiceIndex ();
+
+			int i = 0;
+			foreach (Action action in actions)
+				{
+				Action act = action;
+				int value = i;
+				workItemsGroup.QueueWorkItem (() => { act (); Interlocked.CompareExchange (ref choiceIndex._index, value, -1); anActionCompleted.Set (); });
+				++i;
+				}
+			workItemsGroup.Start ();
+			anActionCompleted.WaitOne ();
+
+			return choiceIndex._index;
+			}
+
+		/// <summary>
+		/// Executes all actions in parallel
+		/// Returns when the first one completes
+		/// </summary>
+		/// <param name="actions">Actions to execute</param>
+		public int Choice (params Action[] actions)
+			{
+			return Choice ((IEnumerable<Action>)actions);
+			}
+
+		/// <summary>
+		/// Executes actions in sequence asynchronously.
+		/// Returns immediately.
+		/// </summary>
+		/// <param name="pipeState">A state context that passes </param>
+		/// <param name="actions">Actions to execute in the order they should run</param>
+		public void Pipe<T> (T pipeState, IEnumerable<Action<T>> actions)
+			{
+			WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
+			IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup (1, wigStartInfo);
+			foreach (Action<T> action in actions)
+				{
+				Action<T> act = action;
+				workItemsGroup.QueueWorkItem (() => act (pipeState));
+				}
+			workItemsGroup.Start ();
+			workItemsGroup.WaitForIdle ();
+			}
+
+		/// <summary>
+		/// Executes actions in sequence asynchronously.
+		/// Returns immediately.
+		/// </summary>
+		/// <param name="pipeState"></param>
+		/// <param name="actions">Actions to execute in the order they should run</param>
+		public void Pipe<T> (T pipeState, params Action<T>[] actions)
+			{
+			Pipe (pipeState, (IEnumerable<Action<T>>)actions);
+			}
+		#endregion
+		}
+	#endregion
+	}
diff --git a/SSharpSmartThreadPool/Stopwatch.cs b/SSharpSmartThreadPool/Stopwatch.cs
new file mode 100644
index 0000000..c6f88d9
--- /dev/null
+++ b/SSharpSmartThreadPool/Stopwatch.cs
@@ -0,0 +1,108 @@
+ï»¿using System;
+
+namespace Amib.Threading.Internal
+{
+    /// <summary>
+    /// Stopwatch class
+    /// Used with WindowsCE and Silverlight which don't have Stopwatch
+    /// </summary>
+    internal class Stopwatch
+    {
+        private long _elapsed;
+        private bool _isRunning;
+        private long _startTimeStamp;
+
+        public Stopwatch()
+        {
+            Reset();
+        }
+
+        private long GetElapsedDateTimeTicks()
+        {
+            long rawElapsedTicks = GetRawElapsedTicks();
+            return rawElapsedTicks;
+        }
+
+        private long GetRawElapsedTicks()
+        {
+            long elapsed = _elapsed;
+            if (_isRunning)
+            {
+                long ticks = GetTimestamp() - _startTimeStamp;
+                elapsed += ticks;
+            }
+            return elapsed;
+        }
+
+        public static long GetTimestamp()
+        {
+            return DateTime.UtcNow.Ticks;
+        }
+
+        public void Reset()
+        {
+           _elapsed = 0L;
+           _isRunning = false;
+           _startTimeStamp = 0L;
+        }
+
+        public void Start()
+        {
+            if (!_isRunning)
+            {
+                _startTimeStamp = GetTimestamp();
+                _isRunning = true;
+            }
+        }
+
+        public static Stopwatch StartNew()
+        {
+            Stopwatch stopwatch = new Stopwatch();
+            stopwatch.Start();
+            return stopwatch;
+        }
+
+        public void Stop()
+        {
+            if (_isRunning)
+            {
+                long ticks = GetTimestamp() - _startTimeStamp;
+                _elapsed += ticks;
+                _isRunning = false;
+            }
+        }
+
+        // Properties
+        public TimeSpan Elapsed
+        {
+            get
+            {
+                return new TimeSpan(GetElapsedDateTimeTicks());
+            }
+        }
+
+        public long ElapsedMilliseconds
+        {
+            get
+            {
+                return (GetElapsedDateTimeTicks() / 0x2710L);
+            }
+        }
+
+        public long ElapsedTicks
+        {
+            get
+            {
+                return GetRawElapsedTicks();
+            }
+        }
+
+        public bool IsRunning
+        {
+            get
+            {
+                return _isRunning;
+            }
+        }
+    }
+}
diff --git a/SSharpSmartThreadPool/SynchronizedDictionary.cs b/SSharpSmartThreadPool/SynchronizedDictionary.cs
new file mode 100644
index 0000000..afcab43
--- /dev/null
+++ b/SSharpSmartThreadPool/SynchronizedDictionary.cs
@@ -0,0 +1,117 @@
+using System.Collections.Generic;
+using Crestron.SimplSharp;
+
+namespace Amib.SSharpThreading.Internal
+	{
+	internal class SynchronizedDictionary<TKey, TValue>
+		{
+		private readonly Dictionary<TKey, TValue> _dictionary;
+		private readonly object _lock;
+#if SSHARP
+		private int _pendingEntries;
+#endif
+
+		public SynchronizedDictionary ()
+			{
+			_lock = new object ();
+			_dictionary = new Dictionary<TKey, TValue> ();
+			}
+
+		public int Count
+			{
+#if SSHARP
+			get { return _dictionary.Count + _pendingEntries; }
+#else
+			get { return _dictionary.Count; }
+#endif
+			}
+
+#if SSHARP
+		public int RealCount
+			{
+			get { return _dictionary.Count; }
+			}
+#endif
+
+		public bool Contains (TKey key)
+			{
+			lock (_lock)
+				{
+				return _dictionary.ContainsKey (key);
+				}
+			}
+
+		public void Remove (TKey key)
+			{
+			lock (_lock)
+				{
+				_dictionary.Remove (key);
+				}
+			}
+
+		public object SyncRoot
+			{
+			get { return _lock; }
+			}
+
+		public TValue this[TKey key]
+			{
+			get
+				{
+				lock (_lock)
+					{
+					return _dictionary[key];
+					}
+				}
+			set
+				{
+				lock (_lock)
+					{
+					_dictionary[key] = value;
+					}
+				}
+			}
+
+		public Dictionary<TKey, TValue>.KeyCollection Keys
+			{
+			get
+				{
+				lock (_lock)
+					{
+					return _dictionary.Keys;
+					}
+				}
+			}
+
+		public Dictionary<TKey, TValue>.ValueCollection Values
+			{
+			get
+				{
+				lock (_lock)
+					{
+					return _dictionary.Values;
+					}
+				}
+			}
+		public void Clear ()
+			{
+			lock (_lock)
+				{
+				_dictionary.Clear ();
+				_pendingEntries = 0;
+				}
+			}
+
+#if SSHARP
+		public void AddPendingEntry ()
+			{
+			Interlocked.Increment (ref _pendingEntries);
+			}
+
+		public void RemovePendingEntry ()
+			{
+			Interlocked.Decrement (ref _pendingEntries);
+			}
+#endif
+		}
+	}
diff --git a/SSharpSmartThreadPool/ThreadEx.cs b/SSharpSmartThreadPool/ThreadEx.cs
new file mode 100644
index 0000000..16219eb
--- /dev/null
+++ b/SSharpSmartThreadPool/ThreadEx.cs
@@ -0,0 +1,82 @@
+ï»¿using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+
+namespace Crestron.SimplSharpPro.CrestronThread
+	{
+	public class ThreadEx : Thread
+		{
+		private static int _lastExIdHigh;
+		private static int _lastExId;
+
+		private readonly long _threadExId;
+
+		public ThreadEx (Crestron.SimplSharpPro.CrestronThread.ThreadCallbackFunction threadCallbackFunc, object userDefinedObject)
+			: base (threadCallbackFunc, userDefinedObject)
+			{
+			_threadExId = Interlocked.Increment (ref _lastExId) | (long)(_threadExId == 0 ? Interlocked.Increment (ref _lastExIdHigh) : _lastExIdHigh) << 32;
+			}
+
+		public ThreadEx (Crestron.SimplSharpPro.CrestronThread.ThreadCallbackFunction threadCallbackFunc, object userDefinedObject,
+		                 Crestron.SimplSharpPro.CrestronThread.Thread.eThreadStartOptions threadStartOption)
+			: base (threadCallbackFunc, userDefinedObject, threadStartOption)
+			{
+			_threadExId = Interlocked.Increment (ref _lastExId) | (long)(_threadExId == 0 ? Interlocked.Increment (ref _lastExIdHigh) : _lastExIdHigh) << 32;
+			}
+
+		public long ThreadExId
+			{
+			get
+				{
+				return _threadExId;
+				}
+			}
+
+		public override int GetHashCode ()
+			{
+			return (int)_threadExId ^ ((int)(_threadExId >> 32));
+			}
+
+		public override bool Equals (object obj)
+			{
+			return obj is long && _threadExId == (long)obj;
+			}
+
+		public new void Join ()
+			{
+			Join (Timeout.Infinite);
+			}
+
+		public new bool Join (int millisecondTimeout)
+			{
+			try
+				{
+				return base.Join (millisecondTimeout);
+				}
+			catch (NullReferenceException)
+				{
+				return true;
+				}
+			}
+
+		public bool Join (TimeSpan timeout)
+			{
+			long lmsec = (long)timeout.TotalMilliseconds;
+			if (lmsec < 0 || lmsec > Int32.MaxValue)
+				throw new ArgumentOutOfRangeException ("timeout", "timeout exceed maximum milliseconds");
+
+			return Join ((int)lmsec);
+			}
+
+		public static ThreadEx CurrentThread
+			{
+			get
+				{
+				
+				}
+			}
+		}
+	}
\ No newline at end of file
diff --git a/SSharpSmartThreadPool/WIGStartInfo.cs b/SSharpSmartThreadPool/WIGStartInfo.cs
new file mode 100644
index 0000000..2caff6c
--- /dev/null
+++ b/SSharpSmartThreadPool/WIGStartInfo.cs
@@ -0,0 +1,171 @@
+using System;
+
+namespace Amib.SSharpThreading
+{
+	/// <summary>
+	/// Summary description for WIGStartInfo.
+	/// </summary>
+	public class WIGStartInfo
+	{
+        private bool _useCallerCallContext;
+        private bool _useCallerHttpContext;
+        private bool _disposeOfStateObjects;
+        private CallToPostExecute _callToPostExecute;
+        private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
+        private bool _startSuspended;
+        private WorkItemPriority _workItemPriority;
+        private bool _fillStateWithArgs;
+
+        protected bool _readOnly;
+
+	    public WIGStartInfo()
+        {
+	        _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
+	        _workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
+            _startSuspended = SmartThreadPool.DefaultStartSuspended;
+            _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
+            _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
+            _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
+            _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
+            _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
+        }
+
+	    public WIGStartInfo(WIGStartInfo wigStartInfo)
+        {
+            _useCallerCallContext = wigStartInfo.UseCallerCallContext;
+            _useCallerHttpContext = wigStartInfo.UseCallerHttpContext;
+            _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
+            _callToPostExecute = wigStartInfo.CallToPostExecute;
+            _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
+            _workItemPriority = wigStartInfo.WorkItemPriority;
+            _startSuspended = wigStartInfo.StartSuspended;
+            _fillStateWithArgs = wigStartInfo.FillStateWithArgs;
+        }
+
+        protected void ThrowIfReadOnly()
+        {
+            if (_readOnly)
+            {
+                throw new NotSupportedException("This is a readonly instance and set is not supported");
+            }
+        }
+
+	    /// <summary>
+	    /// Get/Set if to use the caller's security context
+	    /// </summary>
+	    public virtual bool UseCallerCallContext
+	    {
+	        get { return _useCallerCallContext; }
+            set 
+            { 
+                ThrowIfReadOnly();  
+                _useCallerCallContext = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set if to use the caller's HTTP context
+	    /// </summary>
+	    public virtual bool UseCallerHttpContext
+	    {
+	        get { return _useCallerHttpContext; }
+            set 
+            { 
+                ThrowIfReadOnly();  
+                _useCallerHttpContext = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set if to dispose of the state object of a work item
+	    /// </summary>
+	    public virtual bool DisposeOfStateObjects
+	    {
+	        get { return _disposeOfStateObjects; }
+            set 
+            { 
+                ThrowIfReadOnly();  
+                _disposeOfStateObjects = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set the run the post execute options
+	    /// </summary>
+	    public virtual CallToPostExecute CallToPostExecute
+	    {
+	        get { return _callToPostExecute; }
+            set 
+            { 
+                ThrowIfReadOnly();  
+                _callToPostExecute = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set the default post execute callback
+	    /// </summary>
+	    public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback
+	    {
+	        get { return _postExecuteWorkItemCallback; }
+            set 
+            { 
+                ThrowIfReadOnly();  
+                _postExecuteWorkItemCallback = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set if the work items execution should be suspended until the Start()
+	    /// method is called.
+	    /// </summary>
+	    public virtual bool StartSuspended
+	    {
+	        get { return _startSuspended; }
+            set 
+            { 
+                ThrowIfReadOnly();  
+                _startSuspended = value; 
+            }
+	    }
+
+
+	    /// <summary>
+	    /// Get/Set the default priority that a work item gets when it is enqueued
+	    /// </summary>
+	    public virtual WorkItemPriority WorkItemPriority
+	    {
+	        get { return _workItemPriority; }
+	        set { _workItemPriority = value; }
+	    }
+
+	    /// <summary>
+        /// Get/Set the if QueueWorkItem of Action&lt;...&gt;/Func&lt;...&gt; fill the
+	    /// arguments as an object array into the state of the work item.
+	    /// The arguments can be access later by IWorkItemResult.State.
+	    /// </summary>
+	    public virtual bool FillStateWithArgs
+	    {
+	        get { return _fillStateWithArgs; }
+            set 
+            { 
+                ThrowIfReadOnly();  
+                _fillStateWithArgs = value; 
+            }
+	    }
+
+	    /// <summary>
+        /// Get a readonly version of this WIGStartInfo
+        /// </summary>
+        /// <returns>Returns a readonly reference to this WIGStartInfoRO</returns>
+        public WIGStartInfo AsReadOnly()
+	    {
+            return new WIGStartInfo(this) { _readOnly = true };
+	    }
+    }
+}
diff --git a/SSharpSmartThreadPool/WorkItem.WorkItemResult.cs b/SSharpSmartThreadPool/WorkItem.WorkItemResult.cs
new file mode 100644
index 0000000..e87f473
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItem.WorkItemResult.cs
@@ -0,0 +1,192 @@
+ï»¿using System;
+using System.Collections.Generic;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+namespace Amib.SSharpThreading.Internal
+{
+    public partial class WorkItem
+    {
+        #region WorkItemResult class
+
+        private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult
+        {
+            /// <summary>
+            /// A back reference to the work item
+            /// </summary>
+            private readonly WorkItem _workItem;
+
+            public WorkItemResult(WorkItem workItem)
+            {
+                _workItem = workItem;
+            }
+
+            internal WorkItem GetWorkItem()
+            {
+                return _workItem;
+            }
+
+            #region IWorkItemResult Members
+
+            public bool IsCompleted
+            {
+                get
+                {
+                    return _workItem.IsCompleted;
+                }
+            }
+
+            public bool IsCanceled
+            {
+                get
+                {
+                    return _workItem.IsCanceled;
+                }
+            }
+
+            public object GetResult()
+            {
+                return _workItem.GetResult(Timeout.Infinite, true, null);
+            }
+
+            public object GetResult(int millisecondsTimeout, bool exitContext)
+            {
+                return _workItem.GetResult(millisecondsTimeout, exitContext, null);
+            }
+
+            public object GetResult(TimeSpan timeout, bool exitContext)
+            {
+                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null);
+            }
+
+            public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
+            {
+                return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
+            }
+
+            public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
+            {
+                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
+            }
+
+            public object GetResult(out Exception e)
+            {
+                return _workItem.GetResult(Timeout.Infinite, true, null, out e);
+            }
+
+            public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
+            {
+                return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e);
+            }
+
+            public object GetResult(TimeSpan timeout, bool exitContext, out Exception e)
+            {
+                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e);
+            }
+
+            public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
+            {
+                return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
+            }
+
+            public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
+            {
+                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e);
+            }
+
+            public bool Cancel()
+            {
+                return Cancel(false);
+            }
+
+            public bool Cancel(bool abortExecution)
+            {
+                return _workItem.Cancel(abortExecution);
+            }
+
+            public object State
+            {
+                get
+                {
+                    return _workItem._state;
+                }
+            }
+
+            public WorkItemPriority WorkItemPriority
+            {
+                get
+                {
+                    return _workItem._workItemInfo.WorkItemPriority;
+                }
+            }
+
+            /// <summary>
+            /// Return the result, same as GetResult()
+            /// </summary>
+            public object Result
+            {
+                get { return GetResult(); }
+            }
+
+            /// <summary>
+            /// Returns the exception if occured otherwise returns null.
+            /// This value is valid only after the work item completed,
+            /// before that it is always null.
+            /// </summary>
+            public object Exception
+            {
+                get { return _workItem._exception; }
+            }
+
+            #endregion
+
+            #region IInternalWorkItemResult Members
+
+            public event WorkItemStateCallback OnWorkItemStarted
+            {
+                add
+                {
+                    _workItem.OnWorkItemStarted += value;
+                }
+                remove
+                {
+                    _workItem.OnWorkItemStarted -= value;
+                }
+            }
+
+
+            public event WorkItemStateCallback OnWorkItemCompleted
+            {
+                add
+                {
+                    _workItem.OnWorkItemCompleted += value;
+                }
+                remove
+                {
+                    _workItem.OnWorkItemCompleted -= value;
+                }
+            }
+
+            #endregion
+
+            #region IInternalWorkItemResult Members
+
+            public IWorkItemResult GetWorkItemResult()
+            {
+                return this;
+            }
+
+            public IWorkItemResult<TResult> GetWorkItemResultT<TResult>()
+            {
+                return new WorkItemResultTWrapper<TResult>(this);
+            }
+
+            #endregion
+        }
+
+        #endregion
+
+    }
+}
diff --git a/SSharpSmartThreadPool/WorkItem.cs b/SSharpSmartThreadPool/WorkItem.cs
new file mode 100644
index 0000000..a37358a
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItem.cs
@@ -0,0 +1,1002 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+//using SSMono.Diagnostics;
+using Crestron.SimplSharp;
+using SSharp.Threading;
+
+namespace Amib.SSharpThreading.Internal
+	{
+	/// <summary>
+	/// Holds a callback delegate and the state for that delegate.
+	/// </summary>
+	public partial class WorkItem : IHasWorkItemPriority
+		{
+		#region WorkItemState enum
+
+		/// <summary>
+		/// Indicates the state of the work item in the thread pool
+		/// </summary>
+		private enum WorkItemState
+			{
+			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 Fields
+
+		/// <summary>
+		/// Callback delegate for the callback.
+		/// </summary>
+		private readonly WorkItemCallback _callback;
+
+		/// <summary>
+		/// State with which to call the callback delegate.
+		/// </summary>
+		private object _state;
+
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+        /// <summary>
+        /// Stores the caller's context
+        /// </summary>
+        private readonly CallerThreadContext _callerContext;
+#endif
+		/// <summary>
+		/// Holds the result of the mehtod
+		/// </summary>
+		private object _result;
+
+		/// <summary>
+		/// Hold the exception if the method threw it
+		/// </summary>
+		private Exception _exception;
+
+		/// <summary>
+		/// Hold the state of the work item
+		/// </summary>
+		private WorkItemState _workItemState;
+
+		/// <summary>
+		/// A ManualResetEvent to indicate that the result is ready
+		/// </summary>
+		private ManualResetEvent _workItemCompleted;
+
+		/// <summary>
+		/// A reference count to the _workItemCompleted. 
+		/// When it reaches to zero _workItemCompleted is Closed
+		/// </summary>
+		private int _workItemCompletedRefCount;
+
+		/// <summary>
+		/// Represents the result state of the work item
+		/// </summary>
+		private readonly WorkItemResult _workItemResult;
+
+		/// <summary>
+		/// Work item info
+		/// </summary>
+		private readonly WorkItemInfo _workItemInfo;
+
+		/// <summary>
+		/// Called when the WorkItem starts
+		/// </summary>
+		private event WorkItemStateCallback _workItemStartedEvent;
+
+		/// <summary>
+		/// Called when the WorkItem completes
+		/// </summary>
+		private event WorkItemStateCallback _workItemCompletedEvent;
+
+		/// <summary>
+		/// A reference to an object that indicates whatever the 
+		/// WorkItemsGroup has been canceled
+		/// </summary>
+		private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
+
+		/// <summary>
+		/// A reference to an object that indicates whatever the 
+		/// SmartThreadPool has been canceled
+		/// </summary>
+		private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;
+
+		/// <summary>
+		/// The work item group this work item belong to.
+		/// </summary>
+		private readonly IWorkItemsGroup _workItemsGroup;
+
+		/// <summary>
+		/// The thread that executes this workitem.
+		/// This field is available for the period when the work item is executed, before and after it is null.
+		/// </summary>
+		private Thread _executingThread;
+
+		/// <summary>
+		/// The absulote time when the work item will be timeout
+		/// </summary>
+		private long _expirationTime;
+
+		#region Performance Counter fields
+
+
+
+
+		/// <summary>
+		/// Stores how long the work item waited on the stp queue
+		/// </summary>
+		private Stopwatch _waitingOnQueueStopwatch;
+
+		/// <summary>
+		/// Stores how much time it took the work item to execute after it went out of the queue
+		/// </summary>
+		private Stopwatch _processingStopwatch;
+
+		#endregion
+
+		#endregion
+
+		#region Properties
+
+		public TimeSpan WaitingTime
+			{
+			get
+				{
+				return _waitingOnQueueStopwatch.Elapsed;
+				}
+			}
+
+		public TimeSpan ProcessTime
+			{
+			get
+				{
+				return _processingStopwatch.Elapsed;
+				}
+			}
+
+		internal WorkItemInfo WorkItemInfo
+			{
+			get
+				{
+				return _workItemInfo;
+				}
+			}
+
+		#endregion
+
+		#region Construction
+
+		/// <summary>
+		/// Initialize the callback holding object.
+		/// </summary>
+		/// <param name="workItemsGroup">The workItemGroup of the workitem</param>
+		/// <param name="workItemInfo">The WorkItemInfo of te workitem</param>
+		/// <param name="callback">Callback delegate for the callback.</param>
+		/// <param name="state">State with which to call the callback delegate.</param>
+		/// 
+		/// We assume that the WorkItem object is created within the thread
+		/// that meant to run the callback
+		public WorkItem (
+			IWorkItemsGroup workItemsGroup,
+			WorkItemInfo workItemInfo,
+			WorkItemCallback callback,
+			object state)
+			{
+			_workItemsGroup = workItemsGroup;
+			_workItemInfo = workItemInfo;
+
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+            if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
+            {
+                _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
+            }
+#endif
+
+			_callback = callback;
+			_state = state;
+			_workItemResult = new WorkItemResult (this);
+			Initialize ();
+			}
+
+		internal void Initialize ()
+			{
+			// The _workItemState is changed directly instead of using the SetWorkItemState
+			// method since we don't want to go throught IsValidStateTransition.
+			_workItemState = WorkItemState.InQueue;
+
+			_workItemCompleted = null;
+			_workItemCompletedRefCount = 0;
+			_waitingOnQueueStopwatch = new Stopwatch ();
+			_processingStopwatch = new Stopwatch ();
+			_expirationTime =
+				_workItemInfo.Timeout > 0 ?
+				DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond :
+				long.MaxValue;
+			}
+
+		internal bool WasQueuedBy (IWorkItemsGroup workItemsGroup)
+			{
+			return (workItemsGroup == _workItemsGroup);
+			}
+
+
+		#endregion
+
+		#region Methods
+
+		internal CanceledWorkItemsGroup CanceledWorkItemsGroup
+			{
+			get { return _canceledWorkItemsGroup; }
+			set { _canceledWorkItemsGroup = value; }
+			}
+
+		internal CanceledWorkItemsGroup CanceledSmartThreadPool
+			{
+			get { return _canceledSmartThreadPool; }
+			set { _canceledSmartThreadPool = value; }
+			}
+
+		/// <summary>
+		/// Change the state of the work item to in progress if it wasn't canceled.
+		/// </summary>
+		/// <returns>
+		/// Return true on success or false in case the work item was canceled.
+		/// If the work item needs to run a post execute then the method will return true.
+		/// </returns>
+		public bool StartingWorkItem ()
+			{
+			_waitingOnQueueStopwatch.Stop ();
+			_processingStopwatch.Start ();
+
+			lock (this)
+				{
+				if (IsCanceled)
+					{
+					bool result = false;
+					if ((_workItemInfo.PostExecuteWorkItemCallback != null) &&
+						((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled))
+						{
+						result = true;
+						}
+
+					return result;
+					}
+
+				//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);
+				}
+
+			return true;
+			}
+
+		/// <summary>
+		/// Execute the work item and the post execute
+		/// </summary>
+		public void Execute ()
+			{
+			CallToPostExecute currentCallToPostExecute = 0;
+
+			// Execute the work item if we are in the correct state
+			switch (GetWorkItemState ())
+				{
+				case WorkItemState.InProgress:
+					currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled;
+					ExecuteWorkItem ();
+					break;
+				case WorkItemState.Canceled:
+					currentCallToPostExecute |= CallToPostExecute.WhenWorkItemCanceled;
+					break;
+				default:
+					//Debug.Assert (false);
+					throw new NotSupportedException ();
+				}
+
+			// Run the post execute as needed
+			if ((currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0)
+				{
+				PostExecute ();
+				}
+
+			_processingStopwatch.Stop ();
+			}
+
+		internal void FireWorkItemCompleted ()
+			{
+			try
+				{
+				if (null != _workItemCompletedEvent)
+					{
+					_workItemCompletedEvent (this);
+					}
+				}
+			catch // Suppress exceptions
+			{ }
+			}
+
+		internal void FireWorkItemStarted ()
+			{
+			try
+				{
+				if (null != _workItemStartedEvent)
+					{
+					_workItemStartedEvent (this);
+					}
+				}
+			catch // Suppress exceptions
+			{ }
+			}
+
+		/// <summary>
+		/// Execute the work item
+		/// </summary>
+		private void ExecuteWorkItem ()
+			{
+
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+            CallerThreadContext ctc = null;
+            if (null != _callerContext)
+            {
+                ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
+                CallerThreadContext.Apply(_callerContext);
+            }
+#endif
+
+			Exception exception = null;
+			object result = null;
+
+			try
+				{
+				try
+					{
+					result = _callback (_state);
+					}
+				catch (Exception e)
+					{
+					// Save the exception so we can rethrow it later
+					exception = e;
+					}
+
+				// Remove the value of the execution thread, so it will be impossible to cancel the work item,
+				// since it is already completed.
+				// Cancelling a work item that already completed may cause the abortion of the next work item!!!
+				Thread executionThread = Interlocked.CompareExchange (ref _executingThread, null, _executingThread);
+
+				if (null == executionThread)
+					{
+					// Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException
+					Thread.Sleep (60 * 1000);
+
+					// If after 1 minute this thread was not aborted then let it continue working.
+					}
+				}
+			// We must treat the ThreadAbortException or else it will be stored in the exception variable
+#if SSHARP
+			catch (Exception tae)
+#else
+			catch (ThreadAbortException tae)
+#endif
+				{
+#if SSHARP
+				if (!(tae is System.Threading.ThreadAbortException))
+					throw;
+#endif
+				tae.GetHashCode ();
+				// Check if the work item was cancelled
+				// If we got a ThreadAbortException and the STP is not shutting down, it means the 
+				// work items was cancelled.
+				if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown)
+					{
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+                    Thread.ResetAbort();
+#endif
+					}
+				}
+
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+            if (null != _callerContext)
+            {
+                CallerThreadContext.Apply(ctc);
+            }
+#endif
+
+			if (!SmartThreadPool.IsWorkItemCanceled)
+				{
+				SetResult (result, exception);
+				}
+			}
+
+		/// <summary>
+		/// Runs the post execute callback
+		/// </summary>
+		private void PostExecute ()
+			{
+			if (null != _workItemInfo.PostExecuteWorkItemCallback)
+				{
+				try
+					{
+					_workItemInfo.PostExecuteWorkItemCallback (_workItemResult);
+					}
+				catch (Exception e)
+					{
+					//Debug.Assert (null != e);
+					}
+				}
+			}
+
+		/// <summary>
+		/// Set the result of the work item to return
+		/// </summary>
+		/// <param name="result">The result of the work item</param>
+		/// <param name="exception">The exception that was throw while the workitem executed, null
+		/// if there was no exception.</param>
+		internal void SetResult (object result, Exception exception)
+			{
+			_result = result;
+			_exception = exception;
+			SignalComplete (false);
+			}
+
+		/// <summary>
+		/// Returns the work item result
+		/// </summary>
+		/// <returns>The work item result</returns>
+		internal IWorkItemResult GetWorkItemResult ()
+			{
+			return _workItemResult;
+			}
+
+		/// <summary>
+		/// Wait for all work items to complete
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
+		/// <returns>
+		/// true when every work item in waitableResults has completed; otherwise false.
+		/// </returns>
+		internal static bool WaitAll (
+			IWaitableResult[] waitableResults,
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle)
+			{
+			if (0 == waitableResults.Length)
+				{
+				return true;
+				}
+
+			bool success;
+			WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length];
+			GetWaitHandles (waitableResults, waitHandles);
+
+			if ((null == cancelWaitHandle) && (waitHandles.Length <= 64))
+				{
+				success = STPEventWaitHandle.WaitAll (waitHandles, millisecondsTimeout, exitContext);
+				}
+			else
+				{
+				success = true;
+				int millisecondsLeft = millisecondsTimeout;
+				Stopwatch stopwatch = Stopwatch.StartNew ();
+
+				WaitHandle[] whs;
+				if (null != cancelWaitHandle)
+					{
+					whs = new WaitHandle[] { null, cancelWaitHandle };
+					}
+				else
+					{
+					whs = new WaitHandle[] { null };
+					}
+
+				bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
+				// Iterate over the wait handles and wait for each one to complete.
+				// 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 < waitableResults.Length; ++i)
+					{
+					// WaitAny don't work with negative numbers
+					if (!waitInfinitely && (millisecondsLeft < 0))
+						{
+						success = false;
+						break;
+						}
+
+					whs[0] = waitHandles[i];
+					int result = STPEventWaitHandle.WaitAny (whs, millisecondsLeft, exitContext);
+					if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result))
+						{
+						success = false;
+						break;
+						}
+
+					if (!waitInfinitely)
+						{
+						// Update the time left to wait
+						millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
+						}
+					}
+				}
+			// Release the wait handles
+			ReleaseWaitHandles (waitableResults);
+
+			return success;
+			}
+
+		/// <summary>
+		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
+		/// </summary>
+		/// <param name="waitableResults">Array of work item result objects</param>
+		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
+		/// <param name="exitContext">
+		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
+		/// </param>
+		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
+		/// <returns>
+		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
+		/// </returns>
+		internal static int WaitAny (
+			IWaitableResult[] waitableResults,
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle)
+			{
+			WaitHandle[] waitHandles;
+
+			if (null != cancelWaitHandle)
+				{
+				waitHandles = new WaitHandle[waitableResults.Length + 1];
+				GetWaitHandles (waitableResults, waitHandles);
+				waitHandles[waitableResults.Length] = cancelWaitHandle;
+				}
+			else
+				{
+				waitHandles = new WaitHandle[waitableResults.Length];
+				GetWaitHandles (waitableResults, waitHandles);
+				}
+
+			int result = STPEventWaitHandle.WaitAny (waitHandles, millisecondsTimeout, exitContext);
+
+			// Treat cancel as timeout
+			if (null != cancelWaitHandle)
+				{
+				if (result == waitableResults.Length)
+					{
+					result = STPEventWaitHandle.WaitTimeout;
+					}
+				}
+
+			ReleaseWaitHandles (waitableResults);
+
+			return result;
+			}
+
+		/// <summary>
+		/// Fill an array of wait handles with the work items wait handles.
+		/// </summary>
+		/// <param name="waitableResults">An array of work item results</param>
+		/// <param name="waitHandles">An array of wait handles to fill</param>
+		private static void GetWaitHandles (
+			IWaitableResult[] waitableResults,
+			WaitHandle[] waitHandles)
+			{
+			for (int i = 0; i < waitableResults.Length; ++i)
+				{
+				WorkItemResult wir = waitableResults[i].GetWorkItemResult () as WorkItemResult;
+				//Debug.Assert (null != wir, "All waitableResults must be WorkItemResult objects");
+
+				waitHandles[i] = wir.GetWorkItem ().GetWaitHandle ();
+				}
+			}
+
+		/// <summary>
+		/// Release the work items' wait handles
+		/// </summary>
+		/// <param name="waitableResults">An array of work item results</param>
+		private static void ReleaseWaitHandles (IWaitableResult[] waitableResults)
+			{
+			for (int i = 0; i < waitableResults.Length; ++i)
+				{
+				WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult ();
+
+				wir.GetWorkItem ().ReleaseWaitHandle ();
+				}
+			}
+
+		#endregion
+
+		#region Private Members
+
+		private WorkItemState GetWorkItemState ()
+			{
+			lock (this)
+				{
+				if (WorkItemState.Completed == _workItemState)
+					{
+					return _workItemState;
+					}
+
+				long nowTicks = DateTime.UtcNow.Ticks;
+
+				if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime)
+					{
+					_workItemState = WorkItemState.Canceled;
+					}
+
+				if (WorkItemState.InProgress == _workItemState)
+					{
+					return _workItemState;
+					}
+
+				if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled)
+					{
+					return WorkItemState.Canceled;
+					}
+
+				return _workItemState;
+				}
+			}
+
+
+		/// <summary>
+		/// Sets the work item's state
+		/// </summary>
+		/// <param name="workItemState">The state to set the work item to</param>
+		private void SetWorkItemState (WorkItemState workItemState)
+			{
+			lock (this)
+				{
+				if (IsValidStatesTransition (_workItemState, workItemState))
+					{
+					_workItemState = workItemState;
+					}
+				}
+			}
+
+		/// <summary>
+		/// Signals that work item has been completed or canceled
+		/// </summary>
+		/// <param name="canceled">Indicates that the work item has been canceled</param>
+		private void SignalComplete (bool canceled)
+			{
+			SetWorkItemState (canceled ? WorkItemState.Canceled : WorkItemState.Completed);
+			lock (this)
+				{
+				// If someone is waiting then signal.
+				if (null != _workItemCompleted)
+					{
+					_workItemCompleted.Set ();
+					}
+				}
+			}
+
+		internal void WorkItemIsQueued ()
+			{
+			_waitingOnQueueStopwatch.Start ();
+			}
+
+		#endregion
+
+		#region Members exposed by WorkItemResult
+
+		/// <summary>
+		/// Cancel the work item if it didn't start running yet.
+		/// </summary>
+		/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
+		private bool Cancel (bool abortExecution)
+			{
+#if false && (_WINDOWS_CE)
+			if (abortExecution)
+				{
+				throw new ArgumentOutOfRangeException ("abortExecution", "WindowsCE doesn't support this feature");
+				}
+#endif
+			bool success = false;
+			bool signalComplete = false;
+
+			lock (this)
+				{
+				switch (GetWorkItemState ())
+					{
+					case WorkItemState.Canceled:
+						////Debug.WriteLine("Work item already canceled");
+						if (abortExecution)
+							{
+							Thread executionThread = Interlocked.CompareExchange (ref _executingThread, null, _executingThread);
+							if (null != executionThread)
+								{
+								executionThread.Abort (); // "Cancel"
+								// No need to signalComplete, because we already cancelled this work item
+								// so it already signaled its completion.
+								//signalComplete = true;
+								}
+							}
+						success = true;
+						break;
+					case WorkItemState.Completed:
+						////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;
+						////Debug.WriteLine("Work item canceled");
+						success = true;
+						break;
+					}
+
+				if (signalComplete)
+					{
+					SignalComplete (true);
+					}
+				}
+			return success;
+			}
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
+		/// In case of error the method throws and exception
+		/// </summary>
+		/// <returns>The result of the work item</returns>
+		private object GetResult (
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle)
+			{
+			Exception e;
+			object result = GetResult (millisecondsTimeout, exitContext, cancelWaitHandle, out e);
+			if (null != e)
+				{
+				throw new WorkItemResultException ("The work item caused an excpetion, see the inner exception for details", e);
+				}
+			return result;
+			}
+
+		/// <summary>
+		/// Get the result of the work item.
+		/// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
+		/// In case of error the e argument is filled with the exception
+		/// </summary>
+		/// <returns>The result of the work item</returns>
+		private object GetResult (
+			int millisecondsTimeout,
+			bool exitContext,
+			WaitHandle cancelWaitHandle,
+			out Exception e)
+			{
+			e = null;
+
+			// Check for cancel
+			if (WorkItemState.Canceled == GetWorkItemState ())
+				{
+				throw new WorkItemCancelException ("Work item canceled");
+				}
+
+			// Check for completion
+			if (IsCompleted)
+				{
+				e = _exception;
+				return _result;
+				}
+
+			// If no cancelWaitHandle is provided
+			if (null == cancelWaitHandle)
+				{
+				WaitHandle wh = GetWaitHandle ();
+
+				bool timeout = !STPEventWaitHandle.WaitOne (wh, millisecondsTimeout, exitContext);
+
+				ReleaseWaitHandle ();
+
+				if (timeout)
+					{
+					throw new WorkItemTimeoutException ("Work item timeout");
+					}
+				}
+			else
+				{
+				WaitHandle wh = GetWaitHandle ();
+				int result = STPEventWaitHandle.WaitAny (new WaitHandle[] { wh, cancelWaitHandle });
+				ReleaseWaitHandle ();
+
+				switch (result)
+					{
+					case 0:
+						// The work item signaled
+						// Note that the signal could be also as a result of canceling the 
+						// work item (not the get result)
+						break;
+					case 1:
+					case STPEventWaitHandle.WaitTimeout:
+						throw new WorkItemTimeoutException ("Work item timeout");
+					default:
+						//Debug.Assert (false);
+						break;
+
+					}
+				}
+
+			// Check for cancel
+			if (WorkItemState.Canceled == GetWorkItemState ())
+				{
+				throw new WorkItemCancelException ("Work item canceled");
+				}
+
+			//Debug.Assert (IsCompleted);
+
+			e = _exception;
+
+			// Return the result
+			return _result;
+			}
+
+		/// <summary>
+		/// A wait handle to wait for completion, cancel, or timeout 
+		/// </summary>
+		private WaitHandle GetWaitHandle ()
+			{
+			lock (this)
+				{
+				if (null == _workItemCompleted)
+					{
+					_workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent (IsCompleted);
+					}
+				++_workItemCompletedRefCount;
+				}
+			return _workItemCompleted;
+			}
+
+		private void ReleaseWaitHandle ()
+			{
+			lock (this)
+				{
+				if (null != _workItemCompleted)
+					{
+					--_workItemCompletedRefCount;
+					if (0 == _workItemCompletedRefCount)
+						{
+						_workItemCompleted.Close ();
+						_workItemCompleted = null;
+						}
+					}
+				}
+			}
+
+		/// <summary>
+		/// Returns true when the work item has completed or canceled
+		/// </summary>
+		private bool IsCompleted
+			{
+			get
+				{
+				lock (this)
+					{
+					WorkItemState workItemState = GetWorkItemState ();
+					return ((workItemState == WorkItemState.Completed) ||
+							(workItemState == WorkItemState.Canceled));
+					}
+				}
+			}
+
+		/// <summary>
+		/// Returns true when the work item has canceled
+		/// </summary>
+		public bool IsCanceled
+			{
+			get
+				{
+				lock (this)
+					{
+					return (GetWorkItemState () == WorkItemState.Canceled);
+					}
+				}
+			}
+
+		#endregion
+
+		#region IHasWorkItemPriority Members
+
+		/// <summary>
+		/// Returns the priority of the work item
+		/// </summary>
+		public WorkItemPriority WorkItemPriority
+			{
+			get
+				{
+				return _workItemInfo.WorkItemPriority;
+				}
+			}
+
+		#endregion
+
+		internal event WorkItemStateCallback OnWorkItemStarted
+			{
+			add
+				{
+				_workItemStartedEvent += value;
+				}
+			remove
+				{
+				_workItemStartedEvent -= value;
+				}
+			}
+
+		internal event WorkItemStateCallback OnWorkItemCompleted
+			{
+			add
+				{
+				_workItemCompletedEvent += value;
+				}
+			remove
+				{
+				_workItemCompletedEvent -= value;
+				}
+			}
+
+		public void DisposeOfState ()
+			{
+			if (_workItemInfo.DisposeOfStateObjects)
+				{
+				IDisposable disp = _state as IDisposable;
+				if (null != disp)
+					{
+					disp.Dispose ();
+					_state = null;
+					}
+				}
+			}
+		}
+	}
diff --git a/SSharpSmartThreadPool/WorkItemFactory.cs b/SSharpSmartThreadPool/WorkItemFactory.cs
new file mode 100644
index 0000000..9b60df2
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItemFactory.cs
@@ -0,0 +1,343 @@
+using System;
+
+namespace Amib.SSharpThreading.Internal
+{
+	#region WorkItemFactory class 
+
+	public class WorkItemFactory
+	{
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+		/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
+		/// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback)
+		{
+			return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null);
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
+        /// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="workItemPriority">The priority of the work item</param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback, 
+			WorkItemPriority workItemPriority)
+		{
+			return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority);
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
+        /// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="workItemInfo">Work item info</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemInfo workItemInfo, 
+			WorkItemCallback callback)
+		{
+			return CreateWorkItem(
+				workItemsGroup,
+				wigStartInfo,
+				workItemInfo, 
+				callback, 
+				null);
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
+        /// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback, 
+			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,
+				workItemInfo,
+				callback, 
+				state);
+			return workItem;
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+		/// <param name="workItemsGroup">The work items group</param>
+		/// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback, 
+			object state, 
+			WorkItemPriority workItemPriority)
+		{
+			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 = workItemPriority;
+
+			WorkItem workItem = new WorkItem(
+				workItemsGroup,
+				workItemInfo,
+				callback, 
+				state);
+
+			return workItem;
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The work items group</param>
+		/// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="workItemInfo">Work item information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <returns>Returns a work item</returns>
+        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);
+
+            return workItem;
+        }
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The work items group</param>
+		/// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback, 
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback)
+		{
+			ValidateCallback(callback);
+			ValidateCallback(postExecuteWorkItemCallback);
+
+			WorkItemInfo workItemInfo = new WorkItemInfo();
+			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
+			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
+			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+			workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
+			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
+            workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
+
+			WorkItem workItem = new WorkItem(
+				workItemsGroup,
+				workItemInfo,
+				callback, 
+				state);
+
+			return workItem;
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The work items group</param>
+		/// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback, 
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
+			WorkItemPriority workItemPriority)
+		{
+			ValidateCallback(callback);
+			ValidateCallback(postExecuteWorkItemCallback);
+
+			WorkItemInfo workItemInfo = new WorkItemInfo();
+			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
+			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
+			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+			workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
+			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
+			workItemInfo.WorkItemPriority = workItemPriority;
+
+			WorkItem workItem = new WorkItem(
+				workItemsGroup,
+				workItemInfo,
+				callback, 
+				state);
+
+			return workItem;
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The work items group</param>
+		/// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback, 
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
+			CallToPostExecute callToPostExecute)
+		{
+			ValidateCallback(callback);
+			ValidateCallback(postExecuteWorkItemCallback);
+
+			WorkItemInfo workItemInfo = new WorkItemInfo();
+			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
+			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
+			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+			workItemInfo.CallToPostExecute = callToPostExecute;
+			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
+            workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;
+
+			WorkItem workItem = new WorkItem(
+				workItemsGroup,
+				workItemInfo,
+				callback, 
+				state);
+
+			return workItem;
+		}
+
+		/// <summary>
+		/// Create a new work item
+		/// </summary>
+        /// <param name="workItemsGroup">The work items group</param>
+		/// <param name="wigStartInfo">Work item group start information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item</returns>
+		public static WorkItem CreateWorkItem(
+			IWorkItemsGroup workItemsGroup,
+			WIGStartInfo wigStartInfo,
+			WorkItemCallback callback, 
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
+			CallToPostExecute callToPostExecute,
+			WorkItemPriority workItemPriority)
+		{
+
+			ValidateCallback(callback);
+			ValidateCallback(postExecuteWorkItemCallback);
+
+			WorkItemInfo workItemInfo = new WorkItemInfo();
+			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
+			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
+			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
+			workItemInfo.CallToPostExecute = callToPostExecute;
+			workItemInfo.WorkItemPriority = workItemPriority;
+			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
+
+			WorkItem workItem = new WorkItem(
+				workItemsGroup,
+				workItemInfo,
+				callback, 
+				state);
+			
+			return workItem;
+		}
+
+		private static void ValidateCallback(Delegate callback)
+		{
+            if (callback != null && callback.GetInvocationList().Length > 1)
+			{
+				throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
+			}
+		}
+	}
+
+	#endregion
+}
diff --git a/SSharpSmartThreadPool/WorkItemInfo.cs b/SSharpSmartThreadPool/WorkItemInfo.cs
new file mode 100644
index 0000000..812880d
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItemInfo.cs
@@ -0,0 +1,69 @@
+namespace Amib.SSharpThreading
+{
+	#region WorkItemInfo class
+
+	/// <summary>
+	/// Summary description for WorkItemInfo.
+	/// </summary>
+	public class WorkItemInfo
+	{
+	    public WorkItemInfo()
+		{
+			UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
+			UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
+			DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
+			CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
+			PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
+			WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
+		}
+
+		public WorkItemInfo(WorkItemInfo workItemInfo)
+		{
+			UseCallerCallContext = workItemInfo.UseCallerCallContext;
+			UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
+			DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
+			CallToPostExecute = workItemInfo.CallToPostExecute;
+			PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
+			WorkItemPriority = workItemInfo.WorkItemPriority;
+            Timeout = workItemInfo.Timeout;
+		}
+
+	    /// <summary>
+	    /// Get/Set if to use the caller's security context
+	    /// </summary>
+	    public bool UseCallerCallContext { get; set; }
+
+	    /// <summary>
+	    /// Get/Set if to use the caller's HTTP context
+	    /// </summary>
+	    public bool UseCallerHttpContext { get; set; }
+
+	    /// <summary>
+	    /// Get/Set if to dispose of the state object of a work item
+	    /// </summary>
+	    public bool DisposeOfStateObjects { get; set; }
+
+	    /// <summary>
+	    /// Get/Set the run the post execute options
+	    /// </summary>
+        public CallToPostExecute CallToPostExecute { get; set; }
+
+	    /// <summary>
+	    /// Get/Set the post execute callback
+	    /// </summary>
+        public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }
+
+	    /// <summary>
+	    /// Get/Set the work item's priority
+	    /// </summary>
+	    public WorkItemPriority WorkItemPriority { get; set; }
+
+	    /// <summary>
+	    /// Get/Set the work item's timout in milliseconds.
+        /// This is a passive timout. When the timout expires the work item won't be actively aborted!
+	    /// </summary>
+	    public long Timeout { get; set; }
+	}
+
+	#endregion
+}
diff --git a/SSharpSmartThreadPool/WorkItemResultTWrapper.cs b/SSharpSmartThreadPool/WorkItemResultTWrapper.cs
new file mode 100644
index 0000000..bc16c27
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItemResultTWrapper.cs
@@ -0,0 +1,131 @@
+using System;
+
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+namespace Amib.SSharpThreading.Internal
+{
+    #region WorkItemResultTWrapper class
+
+    internal class WorkItemResultTWrapper<TResult> : IWorkItemResult<TResult>, IInternalWaitableResult
+    {
+        private readonly IWorkItemResult _workItemResult;
+
+        public WorkItemResultTWrapper(IWorkItemResult workItemResult)
+        {
+            _workItemResult = workItemResult;
+        }
+
+        #region IWorkItemResult<TResult> Members
+
+        public TResult GetResult()
+        {
+            return (TResult)_workItemResult.GetResult();
+        }
+
+        public TResult GetResult(int millisecondsTimeout, bool exitContext)
+        {
+            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext);
+        }
+
+        public TResult GetResult(TimeSpan timeout, bool exitContext)
+        {
+            return (TResult)_workItemResult.GetResult(timeout, exitContext);
+        }
+
+        public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
+        {
+            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
+        }
+
+        public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
+        {
+            return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle);
+        }
+
+        public TResult GetResult(out Exception e)
+        {
+            return (TResult)_workItemResult.GetResult(out e);
+        }
+
+        public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
+        {
+            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e);
+        }
+
+        public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e)
+        {
+            return (TResult)_workItemResult.GetResult(timeout, exitContext, out e);
+        }
+
+        public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
+        {
+            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
+        }
+
+        public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
+        {
+            return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e);
+        }
+
+        public bool IsCompleted
+        {
+            get { return _workItemResult.IsCompleted; }
+        }
+
+        public bool IsCanceled
+        {
+            get { return _workItemResult.IsCanceled; }
+        }
+
+        public object State
+        {
+            get { return _workItemResult.State; }
+        }
+
+        public bool Cancel()
+        {
+            return _workItemResult.Cancel();
+        }
+
+        public bool Cancel(bool abortExecution)
+        {
+            return _workItemResult.Cancel(abortExecution);
+        }
+
+        public WorkItemPriority WorkItemPriority
+        {
+            get { return _workItemResult.WorkItemPriority; }
+        }
+
+        public TResult Result
+        {
+            get { return (TResult)_workItemResult.Result; }
+        }
+
+        public object Exception
+        {
+            get { return (TResult)_workItemResult.Exception; }
+        }
+
+        #region IInternalWorkItemResult Members
+
+        public IWorkItemResult GetWorkItemResult()
+        {
+            return _workItemResult.GetWorkItemResult();
+        }
+
+        public IWorkItemResult<TRes> GetWorkItemResultT<TRes>()
+        {
+            return (IWorkItemResult<TRes>)this;
+        }
+
+        #endregion
+
+        #endregion
+    }
+
+    #endregion
+
+}
diff --git a/SSharpSmartThreadPool/WorkItemsGroup.cs b/SSharpSmartThreadPool/WorkItemsGroup.cs
new file mode 100644
index 0000000..35bc752
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItemsGroup.cs
@@ -0,0 +1,363 @@
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+using System.Runtime.CompilerServices;
+//using SSMono.Diagnostics;
+
+namespace Amib.SSharpThreading.Internal
+{
+
+	#region WorkItemsGroup class 
+
+	/// <summary>
+	/// Summary description for WorkItemsGroup.
+	/// </summary>
+	public class WorkItemsGroup : WorkItemsGroupBase
+	{
+		#region Private members
+
+		private readonly object _lock = new object();
+
+		/// <summary>
+		/// A reference to the SmartThreadPool instance that created this 
+		/// WorkItemsGroup.
+		/// </summary>
+		private readonly SmartThreadPool _stp;
+
+		/// <summary>
+		/// The OnIdle event
+		/// </summary>
+		private event WorkItemsGroupIdleHandler _onIdle;
+
+        /// <summary>
+        /// A flag to indicate if the Work Items Group is now suspended.
+        /// </summary>
+        private bool _isSuspended;
+
+		/// <summary>
+		/// Defines how many work items of this WorkItemsGroup can run at once.
+		/// </summary>
+		private int _concurrency;
+
+		/// <summary>
+		/// Priority queue to hold work items before they are passed 
+		/// to the SmartThreadPool.
+		/// </summary>
+		private readonly PriorityQueue _workItemsQueue;
+
+		/// <summary>
+		/// Indicate how many work items are waiting in the SmartThreadPool
+		/// queue.
+		/// This value is used to apply the concurrency.
+		/// </summary>
+		private int _workItemsInStpQueue;
+
+		/// <summary>
+		/// Indicate how many work items are currently running in the SmartThreadPool.
+		/// This value is used with the Cancel, to calculate if we can send new 
+		/// work items to the STP.
+		/// </summary>
+		private int _workItemsExecutingInStp = 0;
+
+		/// <summary>
+		/// WorkItemsGroup start information
+		/// </summary>
+		private readonly WIGStartInfo _workItemsGroupStartInfo;
+
+		/// <summary>
+		/// Signaled when all of the WorkItemsGroup's work item completed.
+		/// </summary>
+        //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
+        private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);
+
+		/// <summary>
+		/// A common object for all the work items that this work items group
+		/// generate so we can mark them to cancel in O(1)
+		/// </summary>
+		private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
+
+		#endregion 
+
+		#region Construction
+
+	    public WorkItemsGroup(
+			SmartThreadPool stp, 
+			int concurrency, 
+			WIGStartInfo wigStartInfo)
+		{
+			if (concurrency <= 0)
+			{
+				throw new ArgumentOutOfRangeException(
+                    "concurrency",
+#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
+                    concurrency,
+#endif
+ "concurrency must be greater than zero");
+			}
+			_stp = stp;
+			_concurrency = concurrency;
+			_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).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 WorkItemsGroupBase Overrides
+
+        public override int Concurrency
+        {
+            get { return _concurrency; }
+            set
+            {
+                //Debug.Assert(value > 0);
+
+                int diff = value - _concurrency;
+                _concurrency = value;
+                if (diff > 0)
+                {
+                    EnqueueToSTPNextNWorkItem(diff);
+                }
+            }
+        }
+
+        public override int WaitingCallbacks
+        {
+            get { return _workItemsQueue.Count; }
+        }
+
+        public override object[] GetStates()
+        {
+            lock (_lock)
+            {
+                object[] states = new object[_workItemsQueue.Count];
+                int i = 0;
+                foreach (WorkItem workItem in _workItemsQueue)
+                {
+                    states[i] = workItem.GetWorkItemResult().State;
+                    ++i;
+                }
+                return states;
+            }
+        }
+
+	    /// <summary>
+        /// WorkItemsGroup start information
+        /// </summary>
+        public override WIGStartInfo WIGStartInfo
+        {
+            get { return _workItemsGroupStartInfo; }
+        }
+
+	    /// <summary>
+	    /// Start the Work Items Group if it was started suspended
+	    /// </summary>
+	    public override void Start()
+	    {
+	        // If the Work Items Group already started then quit
+	        if (!_isSuspended)
+	        {
+	            return;
+	        }
+	        _isSuspended = false;
+            
+	        EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
+	    }
+
+	    public override void Cancel(bool abortExecution)
+	    {
+	        lock (_lock)
+	        {
+	            _canceledWorkItemsGroup.IsCanceled = true;
+	            _workItemsQueue.Clear();
+	            _workItemsInStpQueue = 0;
+	            _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
+	        }
+
+	        if (abortExecution)
+	        {
+	            _stp.CancelAbortWorkItemsGroup(this);
+	        }
+	    }
+
+	    /// <summary>
+        /// Wait for the thread pool to be idle
+        /// </summary>
+        public override bool WaitForIdle(int millisecondsTimeout)
+        {
+            SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
+            return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
+        }
+
+	    public override event WorkItemsGroupIdleHandler OnIdle
+		{
+			add { _onIdle += value; }
+			remove { _onIdle -= value; }
+		}
+
+	    #endregion 
+
+		#region Private methods
+
+	    private void RegisterToWorkItemCompletion(IWorkItemResult wir)
+		{
+			IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
+			iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
+			iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
+		}
+
+	    public void OnSTPIsStarting()
+		{
+            if (_isSuspended)
+            {
+                return;
+            }
+			
+            EnqueueToSTPNextNWorkItem(_concurrency);
+		}
+
+	    public void EnqueueToSTPNextNWorkItem(int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                EnqueueToSTPNextWorkItem(null, false);
+            }
+        }
+
+		private object FireOnIdle(object state)
+		{
+			FireOnIdleImpl(_onIdle);
+			return null;
+		}
+
+		[MethodImpl(MethodImplOptions.NoInlining)]
+		private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
+		{
+			if(null == onIdle)
+			{
+				return;
+			}
+
+			Delegate[] delegates = onIdle.GetInvocationList();
+			foreach(WorkItemsGroupIdleHandler eh in delegates)
+			{
+				try
+				{
+					eh(this);
+				}
+                catch { }  // Suppress exceptions
+			}
+		}
+
+		private void OnWorkItemStartedCallback(WorkItem workItem)
+		{
+			lock(_lock)
+			{
+				++_workItemsExecutingInStp;
+			}
+		}
+
+		private void OnWorkItemCompletedCallback(WorkItem workItem)
+		{
+			EnqueueToSTPNextWorkItem(null, true);
+		}
+
+        internal override void Enqueue(WorkItem workItem)
+        {
+            EnqueueToSTPNextWorkItem(workItem);
+        }
+
+	    private void EnqueueToSTPNextWorkItem(WorkItem workItem)
+		{
+			EnqueueToSTPNextWorkItem(workItem, false);
+		}
+
+		private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
+		{
+			lock(_lock)
+			{
+				// Got here from OnWorkItemCompletedCallback()
+				if (decrementWorkItemsInStpQueue)
+				{
+					--_workItemsInStpQueue;
+
+					if(_workItemsInStpQueue < 0)
+					{
+						_workItemsInStpQueue = 0;
+					}
+
+					--_workItemsExecutingInStp;
+
+					if(_workItemsExecutingInStp < 0)
+					{
+						_workItemsExecutingInStp = 0;
+					}
+				}
+
+				// If the work item is not null then enqueue it
+				if (null != workItem)
+				{
+					workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;
+
+					RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
+					_workItemsQueue.Enqueue(workItem);
+					//_stp.IncrementWorkItemsCount();
+
+					if ((1 == _workItemsQueue.Count) && 
+						(0 == _workItemsInStpQueue))
+					{
+						_stp.RegisterWorkItemsGroup(this);
+                        IsIdle = false;
+                        _isIdleWaitHandle.Reset();
+					}
+				}
+
+				// If the work items queue of the group is empty than quit
+				if (0 == _workItemsQueue.Count)
+				{
+					if (0 == _workItemsInStpQueue)
+					{
+						_stp.UnregisterWorkItemsGroup(this);
+                        IsIdle = true;
+                        _isIdleWaitHandle.Set();
+                        if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
+                        {
+                            _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
+                        }
+					}
+					return;
+				}
+
+                if (!_isSuspended)
+				{
+					if (_workItemsInStpQueue < _concurrency)
+					{
+						WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
+                        try
+                        {
+                            _stp.Enqueue(nextWorkItem);
+                        }
+                        catch (ObjectDisposedException e)
+                        {
+                            e.GetHashCode();
+                            // The STP has been shutdown
+                        }
+
+						++_workItemsInStpQueue;
+					}
+				}
+			}
+		}
+
+		#endregion
+    }
+
+	#endregion
+}
diff --git a/SSharpSmartThreadPool/WorkItemsGroupBase.cs b/SSharpSmartThreadPool/WorkItemsGroupBase.cs
new file mode 100644
index 0000000..32fb634
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItemsGroupBase.cs
@@ -0,0 +1,498 @@
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+
+namespace Amib.SSharpThreading.Internal
+	{
+	public abstract class WorkItemsGroupBase : IWorkItemsGroup
+		{
+		#region Private Fields
+
+		/// <summary>
+		/// Contains the name of this instance of SmartThreadPool.
+		/// Can be changed by the user.
+		/// </summary>
+		private string _name = "WorkItemsGroupBase";
+
+		public WorkItemsGroupBase ()
+			{
+			IsIdle = true;
+			}
+
+		#endregion
+
+		#region IWorkItemsGroup Members
+
+		#region Public Methods
+
+		/// <summary>
+		/// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance
+		/// </summary>
+		public string Name
+			{
+			get { return _name; }
+			set { _name = value; }
+			}
+
+		#endregion
+
+		#region Abstract Methods
+
+		public abstract int Concurrency { get; set; }
+		public abstract int WaitingCallbacks { get; }
+		public abstract object[] GetStates ();
+		public abstract WIGStartInfo WIGStartInfo { get; }
+		public abstract void Start ();
+		public abstract void Cancel (bool abortExecution);
+		public abstract bool WaitForIdle (int millisecondsTimeout);
+		public abstract event WorkItemsGroupIdleHandler OnIdle;
+
+		internal abstract void Enqueue (WorkItem workItem);
+		internal virtual void PreQueueWorkItem () { }
+
+		#endregion
+
+		#region Common Base Methods
+
+		/// <summary>
+		/// Cancel all the work items.
+		/// Same as Cancel(false)
+		/// </summary>
+		public virtual void Cancel ()
+			{
+			Cancel (false);
+			}
+
+		/// <summary>
+		/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
+		/// </summary>
+		public void WaitForIdle ()
+			{
+			WaitForIdle (Timeout.Infinite);
+			}
+
+		/// <summary>
+		/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
+		/// </summary>
+		public bool WaitForIdle (TimeSpan timeout)
+			{
+			return WaitForIdle ((int)timeout.TotalMilliseconds);
+			}
+
+		/// <summary>
+		/// IsIdle is true when there are no work items running or queued.
+		/// </summary>
+		public bool IsIdle { get; protected set; }
+
+		#endregion
+
+		#region QueueWorkItem
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (WorkItemCallback callback)
+			{
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="workItemPriority">The priority of the work item</param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (WorkItemCallback callback, WorkItemPriority workItemPriority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, workItemPriority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="workItemInfo">Work item info</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, workItemInfo, callback);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state)
+			{
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, workItemPriority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="workItemInfo">Work item information</param>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, workItemInfo, callback, state);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (
+			WorkItemCallback callback,
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (
+			WorkItemCallback callback,
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
+			WorkItemPriority workItemPriority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (
+			WorkItemCallback callback,
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
+			CallToPostExecute callToPostExecute)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		/// <summary>
+		/// Queue a work item
+		/// </summary>
+		/// <param name="callback">A callback to execute</param>
+		/// <param name="state">
+		/// The context object of the work item. Used for passing arguments to the work item. 
+		/// </param>
+		/// <param name="postExecuteWorkItemCallback">
+		/// A delegate to call after the callback completion
+		/// </param>
+		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
+		/// <param name="workItemPriority">The work item priority</param>
+		/// <returns>Returns a work item result</returns>
+		public IWorkItemResult QueueWorkItem (
+			WorkItemCallback callback,
+			object state,
+			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
+			CallToPostExecute callToPostExecute,
+			WorkItemPriority workItemPriority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		#endregion
+
+		#region QueueWorkItem(Action<...>)
+
+		public IWorkItemResult QueueWorkItem (Action action)
+			{
+			return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+				this,
+				WIGStartInfo,
+				delegate
+					{
+					action.Invoke ();
+					return null;
+					}, priority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		public IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg)
+			{
+			return QueueWorkItem (action, arg, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+				this,
+				WIGStartInfo,
+				state =>
+					{
+					action.Invoke (arg);
+					return null;
+					},
+				WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		public IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2)
+			{
+			return QueueWorkItem (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+				this,
+				WIGStartInfo,
+				state =>
+					{
+					action.Invoke (arg1, arg2);
+					return null;
+					},
+				WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		public IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
+			{
+			return QueueWorkItem (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+				this,
+				WIGStartInfo,
+				state =>
+					{
+					action.Invoke (arg1, arg2, arg3);
+					return null;
+					},
+				WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		public IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
+			{
+			return QueueWorkItem (action, arg1, arg2, arg3, arg4, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (
+			Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+						   this,
+						   WIGStartInfo,
+						   state =>
+							   {
+							   action.Invoke (arg1, arg2, arg3, arg4);
+							   return null;
+							   },
+						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority);
+			Enqueue (workItem);
+			return workItem.GetWorkItemResult ();
+			}
+
+		#endregion
+
+		#region QueueWorkItem(Func<...>)
+
+		public IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func)
+			{
+			return QueueWorkItem (func, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+							this,
+							WIGStartInfo,
+							state =>
+								{
+								return func.Invoke ();
+								}, priority);
+			Enqueue (workItem);
+			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg)
+			{
+			return QueueWorkItem (func, arg, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+				this,
+				WIGStartInfo,
+				state =>
+					{
+					return func.Invoke (arg);
+					},
+				WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null,
+				priority);
+			Enqueue (workItem);
+			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2)
+			{
+			return QueueWorkItem (func, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+							this,
+							WIGStartInfo,
+							state =>
+								{
+								return func.Invoke (arg1, arg2);
+								},
+						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null,
+						   priority);
+			Enqueue (workItem);
+			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3)
+			{
+			return QueueWorkItem (func, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (
+			Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+							this,
+							WIGStartInfo,
+							state =>
+								{
+								return func.Invoke (arg1, arg2, arg3);
+								},
+						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null,
+						   priority);
+			Enqueue (workItem);
+			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
+			{
+			return QueueWorkItem (func, arg1, arg2, arg3, arg4, SmartThreadPool.DefaultWorkItemPriority);
+			}
+
+		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (
+			Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority)
+			{
+			PreQueueWorkItem ();
+			WorkItem workItem = WorkItemFactory.CreateWorkItem (
+							this,
+							WIGStartInfo,
+							state =>
+								{
+								return func.Invoke (arg1, arg2, arg3, arg4);
+								},
+						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null,
+						   priority);
+			Enqueue (workItem);
+			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
+			}
+
+		#endregion
+
+		#endregion
+		}
+	}
\ No newline at end of file
diff --git a/SSharpSmartThreadPool/WorkItemsQueue.cs b/SSharpSmartThreadPool/WorkItemsQueue.cs
new file mode 100644
index 0000000..0384afe
--- /dev/null
+++ b/SSharpSmartThreadPool/WorkItemsQueue.cs
@@ -0,0 +1,647 @@
+using System;
+using System.Collections.Generic;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+namespace Amib.SSharpThreading.Internal
+{
+	#region WorkItemsQueue class
+
+	/// <summary>
+	/// WorkItemsQueue class.
+	/// </summary>
+	public class WorkItemsQueue : IDisposable
+	{
+		#region Member variables
+
+		/// <summary>
+		/// Waiters queue (implemented as stack).
+		/// </summary>
+		private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();
+
+		/// <summary>
+		/// Waiters count
+		/// </summary>
+		private int _waitersCount = 0;
+
+		/// <summary>
+		/// Work items queue
+		/// </summary>
+		private readonly PriorityQueue _workItems = new PriorityQueue();
+
+        /// <summary>
+        /// Indicate that work items are allowed to be queued
+        /// </summary>
+        private bool _isWorkItemsQueueActive = true;
+
+
+#if (WINDOWS_PHONE) 
+        private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>();
+#elif (_WINDOWS_CE)
+        private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot();
+#else
+
+        [ThreadStatic]
+        private static WaiterEntry _waiterEntry;
+#endif
+
+
+        /// <summary>
+        /// Each thread in the thread pool keeps its own waiter entry.
+        /// </summary>
+        private static WaiterEntry CurrentWaiterEntry
+        {
+#if (WINDOWS_PHONE) 
+            get
+            {
+                lock (_waiterEntries)
+                {
+                    WaiterEntry waiterEntry;
+                    if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry))
+                    {
+                        return waiterEntry;
+                    }
+                }
+                return null;
+            }
+            set
+            {
+                lock (_waiterEntries)
+                {
+                    _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value;
+                }
+            }
+#elif (_WINDOWS_CE)
+            get
+            {
+                return Thread.GetData(_waiterEntrySlot) as WaiterEntry;
+            }
+            set
+            {
+                Thread.SetData(_waiterEntrySlot, value);
+            }
+#else
+            get
+            {
+                return _waiterEntry;
+            }
+            set
+            {
+                _waiterEntry = value;
+            }
+#endif
+        }
+
+        /// <summary>
+		/// A flag that indicates if the WorkItemsQueue has been disposed.
+		/// </summary>
+        private bool _isDisposed = false;
+
+		#endregion
+
+		#region Public properties
+
+        /// <summary>
+        /// Returns the current number of work items in the queue
+        /// </summary>
+		public int Count
+		{
+			get
+			{
+                return _workItems.Count;
+			}
+		}
+
+		/// <summary>
+		/// Returns the current number of waiters
+		/// </summary>
+		public int WaitersCount
+		{
+			get
+			{
+				return _waitersCount;
+			}
+		}
+
+
+        #endregion
+
+        #region Public methods
+
+		/// <summary>
+		/// Enqueue a work item to the queue.
+		/// </summary>
+		public bool EnqueueWorkItem(WorkItem workItem)
+		{
+			// A work item cannot be null, since null is used in the
+			// WaitForWorkItem() method to indicate timeout or cancel
+			if (null == workItem)
+			{
+				throw new ArgumentNullException("workItem" , "workItem cannot be null");
+			}
+
+			bool enqueue = true;
+
+			// First check if there is a waiter waiting for work item. During 
+			// the check, timed out waiters are ignored. If there is no 
+			// waiter then the work item is queued.
+			lock(this)
+			{
+                ValidateNotDisposed();
+
+                if (!_isWorkItemsQueueActive)
+                {
+                    return false;
+                }
+
+				while(_waitersCount > 0)
+				{
+					// Dequeue a waiter.
+					WaiterEntry waiterEntry = PopWaiter();
+
+					// Signal the waiter. On success break the loop
+					if (waiterEntry.Signal(workItem))
+					{
+						enqueue = false;
+						break;
+					}
+				}
+
+				if (enqueue)
+				{
+					// Enqueue the work item
+					_workItems.Enqueue(workItem);
+				}
+			}
+            return true;
+		}
+
+
+		/// <summary>
+		/// Waits for a work item or exits on timeout or cancel
+		/// </summary>
+		/// <param name="millisecondsTimeout">Timeout in milliseconds</param>
+		/// <param name="cancelEvent">Cancel wait handle</param>
+		/// <returns>Returns true if the resource was granted</returns>
+		public WorkItem DequeueWorkItem(
+			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 it.
+			// 
+			// If there are no waiting work items then the caller 
+			// is queued between other waiters for a work item to arrive.
+			// 
+			// If a work item didn't come within millisecondsTimeout or 
+			// the user canceled the wait by signaling the cancelEvent 
+			// then the method returns null to indicate that the caller 
+			// didn't get a work item.
+
+			WaiterEntry waiterEntry;
+			WorkItem workItem = null;
+		    lock (this)
+		    {
+                ValidateNotDisposed();
+
+                // If there are waiting work items then take one and return.
+                if (_workItems.Count > 0)
+                {
+                    workItem = _workItems.Dequeue() as WorkItem;
+                    return workItem;
+                }
+
+                // No waiting work items ...
+
+                // Get the waiter entry for the waiters queue
+                waiterEntry = GetThreadWaiterEntry();
+
+                // Put the waiter with the other waiters
+                PushWaiter(waiterEntry);
+		    }
+
+		    // Prepare array of wait handle for the WaitHandle.WaitAny()
+            WaitHandle [] waitHandles = new WaitHandle[] { 
+																waiterEntry.WaitHandle, 
+																cancelEvent };
+
+			// Wait for an available resource, cancel event, or timeout.
+
+			// 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 two lock(this) 
+			// statments instead of one.
+
+            int index = STPEventWaitHandle.WaitAny(
+				waitHandles,
+				millisecondsTimeout, 
+				true);
+
+			lock(this)
+			{
+				// success is true if it got a work item.
+				bool success = (0 == index);
+
+				// The timeout variable is used only for readability.
+				// (We treat cancel as timeout)
+				bool timeout = !success;
+
+				// On timeout update the waiterEntry that it is timed out
+				if (timeout)
+				{
+					// The Timeout() fails if the waiter has already been signaled
+					timeout = waiterEntry.Timeout();
+
+					// On timeout remove the waiter from the queue.
+					// Note that the complexity is O(1).
+					if(timeout)
+					{
+						RemoveWaiter(waiterEntry, false);
+					}
+
+					// Again readability
+					success = !timeout;
+				}
+
+				// On success return the work item
+				if (success)
+				{
+					workItem = waiterEntry.WorkItem;
+
+					if (null == workItem)
+					{
+						workItem = _workItems.Dequeue() as WorkItem;
+					}
+				}
+			}
+			// On failure return null.
+			return workItem;
+		}
+
+        /// <summary>
+        /// Cleanup the work items queue, hence no more work 
+        /// items are allowed to be queue
+        /// </summary>
+        private void Cleanup()
+        {
+            lock(this)
+            {
+                // Deactivate only once
+                if (!_isWorkItemsQueueActive)
+                {
+                    return;
+                }
+
+                // Don't queue more work items
+                _isWorkItemsQueueActive = false;
+
+                foreach(WorkItem workItem in _workItems)
+                {
+                    workItem.DisposeOfState();
+                }
+
+                // Clear the work items that are already queued
+                _workItems.Clear();
+
+                // Note: 
+                // I don't iterate over the queue and dispose of work items's states, 
+                // since if a work item has a state object that is still in use in the 
+                // application then I must not dispose it.
+
+                // Tell the waiters that they were timed out.
+                // It won't signal them to exit, but to ignore their
+                // next work item.
+				while(_waitersCount > 0)
+				{
+					WaiterEntry waiterEntry = PopWaiter();
+					waiterEntry.Timeout();
+				}
+            }
+        }
+
+        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
+
+		/// <summary>
+		/// Returns the WaiterEntry of the current thread
+		/// </summary>
+		/// <returns></returns>
+		/// In order to avoid creation and destuction of WaiterEntry
+		/// objects each thread has its own WaiterEntry object.
+		private static WaiterEntry GetThreadWaiterEntry()
+		{
+			if (null == CurrentWaiterEntry)
+			{
+				CurrentWaiterEntry = new WaiterEntry();
+			}
+			CurrentWaiterEntry.Reset();
+			return CurrentWaiterEntry;
+		}
+
+		#region Waiters stack methods
+
+		/// <summary>
+		/// Push a new waiter into the waiter's stack
+		/// </summary>
+		/// <param name="newWaiterEntry">A waiter to put in the stack</param>
+		public void PushWaiter(WaiterEntry newWaiterEntry)
+		{
+			// Remove the waiter if it is already in the stack and 
+			// update waiter's count as needed
+			RemoveWaiter(newWaiterEntry, false);
+
+			// If the stack is empty then newWaiterEntry is the new head of the stack 
+			if (null == _headWaiterEntry._nextWaiterEntry)
+			{
+				_headWaiterEntry._nextWaiterEntry = newWaiterEntry;
+				newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
+
+			}
+			// If the stack is not empty then put newWaiterEntry as the new head 
+			// of the stack.
+			else
+			{
+				// Save the old first waiter entry
+				WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
+
+                // Update the links
+				_headWaiterEntry._nextWaiterEntry = newWaiterEntry;
+				newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry;
+				newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
+				oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry;
+			}
+
+			// Increment the number of waiters
+			++_waitersCount;
+		}
+
+		/// <summary>
+		/// Pop a waiter from the waiter's stack
+		/// </summary>
+		/// <returns>Returns the first waiter in the stack</returns>
+		private WaiterEntry PopWaiter()
+		{
+			// Store the current stack head
+			WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;
+
+			// Store the new stack head
+			WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry;
+
+			// Update the old stack head list links and decrement the number
+			// waiters.
+			RemoveWaiter(oldFirstWaiterEntry, true);
+
+			// Update the new stack head
+			_headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry;
+			if (null != newHeadWaiterEntry)
+			{
+				newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry;
+			}
+
+			// Return the old stack head
+			return oldFirstWaiterEntry;
+		}
+
+		/// <summary>
+		/// Remove a waiter from the stack
+		/// </summary>
+		/// <param name="waiterEntry">A waiter entry to remove</param>
+		/// <param name="popDecrement">If true the waiter count is always decremented</param>
+		private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement)
+		{
+			// Store the prev entry in the list
+			WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry;
+
+			// Store the next entry in the list
+			WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry;
+
+			// A flag to indicate if we need to decrement the waiters count.
+			// If we got here from PopWaiter then we must decrement.
+			// If we got here from PushWaiter then we decrement only if
+			// the waiter was already in the stack.
+			bool decrementCounter = popDecrement;
+
+			// Null the waiter's entry links
+			waiterEntry._prevWaiterEntry = null;
+			waiterEntry._nextWaiterEntry = null;
+
+			// If the waiter entry had a prev link then update it.
+			// It also means that the waiter is already in the list and we
+			// need to decrement the waiters count.
+			if (null != prevWaiterEntry)
+			{
+				prevWaiterEntry._nextWaiterEntry = nextWaiterEntry;
+				decrementCounter = true;
+			}
+
+			// If the waiter entry had a next link then update it.
+			// It also means that the waiter is already in the list and we
+			// need to decrement the waiters count.
+			if (null != nextWaiterEntry)
+			{
+				nextWaiterEntry._prevWaiterEntry = prevWaiterEntry;
+				decrementCounter = true;
+			}
+
+			// Decrement the waiters count if needed
+			if (decrementCounter)
+			{
+				--_waitersCount;
+			}
+		}
+
+		#endregion
+
+		#endregion
+
+		#region WaiterEntry class 
+
+		// A waiter entry in the _waiters queue.
+		public sealed class WaiterEntry : IDisposable
+		{
+			#region Member variables
+
+			/// <summary>
+			/// Event to signal the waiter that it got the work item.
+			/// </summary>
+			//private AutoResetEvent _waitHandle = new AutoResetEvent(false);
+            private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();
+
+			/// <summary>
+			/// Flag to know if this waiter already quited from the queue 
+			/// because of a timeout.
+			/// </summary>
+			private bool _isTimedout = false;
+
+			/// <summary>
+			/// Flag to know if the waiter was signaled and got a work item. 
+			/// </summary>
+			private bool _isSignaled = false;
+
+            /// <summary>
+            /// A work item that passed directly to the waiter withou going 
+            /// through the queue
+            /// </summary>
+			private WorkItem _workItem = null;
+
+            private bool _isDisposed = false;
+
+			// Linked list members
+			internal WaiterEntry _nextWaiterEntry = null;
+			internal WaiterEntry _prevWaiterEntry = null;
+
+			#endregion
+
+			#region Construction
+
+			public WaiterEntry()
+			{
+				Reset();
+			}
+						
+			#endregion
+
+			#region Public methods
+
+			public WaitHandle WaitHandle
+			{
+				get { return _waitHandle; }
+			}
+
+			public WorkItem WorkItem
+			{
+				get
+				{
+					return _workItem;
+				}
+			}
+
+			/// <summary>
+			/// Signal the waiter that it got a work item.
+			/// </summary>
+			/// <returns>Return true on success</returns>
+			/// The method fails if Timeout() preceded its call
+			public bool Signal(WorkItem workItem)
+			{
+				lock(this)
+				{
+					if (!_isTimedout)
+					{
+						_workItem = workItem;
+						_isSignaled = true;
+						_waitHandle.Set();
+						return true;
+					}
+				}
+				return false;
+			}
+
+			/// <summary>
+			/// Mark the wait entry that it has been timed out
+			/// </summary>
+			/// <returns>Return true on success</returns>
+			/// The method fails if Signal() preceded its call
+			public bool Timeout()
+			{
+				lock(this)
+				{
+					// Time out can happen only if the waiter wasn't marked as
+					// signaled
+					if (!_isSignaled)
+					{
+						// We don't remove the waiter from the queue, the DequeueWorkItem 
+                        // method skips _waiters that were timed out.
+						_isTimedout = true;
+						return true;
+					}
+				}
+				return false;
+			}
+
+			/// <summary>
+			/// Reset the wait entry so it can be used again
+			/// </summary>
+			public void Reset()
+			{
+				_workItem = null;
+				_isTimedout = false;
+				_isSignaled = false;
+				_waitHandle.Reset();
+			}
+
+			/// <summary>
+			/// Free resources
+			/// </summary>
+			public void Close()
+			{
+				if (null != _waitHandle)
+				{
+					_waitHandle.Close();
+					_waitHandle = null;
+				}
+			}
+
+			#endregion
+
+			#region IDisposable Members
+
+			public void Dispose()
+			{
+                lock (this)
+                {
+                    if (!_isDisposed)
+                    {
+                        Close();
+                    }
+                    _isDisposed = true;
+                }
+            }
+
+			#endregion
+		}
+
+		#endregion
+
+        #region IDisposable Members
+
+        public void Dispose()
+        {
+            if (!_isDisposed)
+            {
+                Cleanup();
+            }
+            _isDisposed = true;
+        }
+
+        private void ValidateNotDisposed()
+        {
+            if(_isDisposed)
+            {
+                throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
+            }
+        }
+
+        #endregion
+    }
+
+	#endregion
+}
+
diff --git a/SSharpSmartThreadPoolTests/PermutationGenerator.cs b/SSharpSmartThreadPoolTests/PermutationGenerator.cs
new file mode 100644
index 0000000..9161f69
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/PermutationGenerator.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections;
+
+/*
+ * The code below generates permutations.
+ * 
+ * The original code was written by Michael Gilleland,
+ * and can be found in the following site 
+ * http://www.merriampark.com/perm.htm
+ * 
+ * I translated it to C# from Java.
+ */
+namespace SmartThreadPoolTests
+{
+	//--------------------------------------
+	// Systematically generate permutations. 
+	//--------------------------------------
+
+	public class PermutationGenerator : IEnumerable
+	{
+		private object [] _objects;
+
+		public PermutationGenerator(object [] objects)
+		{
+			_objects = (object [])objects.Clone();
+		}
+
+		#region IEnumerable Members
+
+		public IEnumerator GetEnumerator()
+		{
+			return new PermutationGeneratorEnumerator(_objects);
+		}
+
+		#endregion
+
+		private class PermutationGeneratorEnumerator : IEnumerator
+		{
+			private object [] _objects;
+			private object [] _currentPermutation;
+			private PermutationGeneratorHelper _permutationGeneratorHelper;
+
+			public PermutationGeneratorEnumerator(object [] objects)
+			{
+				_objects = objects;
+				Reset();
+			}
+
+			#region IEnumerator Members
+
+			public void Reset()
+			{
+				_permutationGeneratorHelper = new PermutationGeneratorHelper(_objects.Length);
+			}
+
+			public object Current
+			{
+				get
+				{
+					return _currentPermutation;
+				}
+			}
+
+			public bool MoveNext()
+			{
+				if (_permutationGeneratorHelper.hasMore())
+				{
+					_currentPermutation = new object[_objects.Length];
+					int [] indices = _permutationGeneratorHelper.getNext();
+					for (int i = 0; i < indices.Length; i++) 
+					{
+						_currentPermutation[i] = _objects[indices[i]];
+					}
+					return true;
+				}
+				_currentPermutation = null;
+				return false;
+			}
+
+			#endregion
+		}
+
+
+		private class PermutationGeneratorHelper
+		{
+
+			private int[] a;
+			private long numLeft;
+			private long total;
+
+			//-----------------------------------------------------------
+			// Constructor. WARNING: Don't make n too large.
+			// Recall that the number of permutations is n!
+			// which can be very large, even when n is as small as 20 --
+			// 20! = 2,432,902,008,176,640,000 and
+			// 21! is too big to fit into a Java long, which is
+			// why we use long instead.
+			//----------------------------------------------------------
+
+			public PermutationGeneratorHelper (int n) 
+			{
+				if (n < 1) 
+				{
+					throw new ArgumentOutOfRangeException("n", "Min 1");
+				}
+				a = new int[n];
+				total = getFactorial (n);
+				reset();
+			}
+
+			//------
+			// Reset
+			//------
+
+			public void reset () 
+			{
+				for (int i = 0; i < a.Length; i++) 
+				{
+					a[i] = i;
+				}
+				numLeft = total;
+			}
+
+			//------------------------------------------------
+			// Return number of permutations not yet generated
+			//------------------------------------------------
+
+			public long getNumLeft () 
+			{
+				return numLeft;
+			}
+
+			//------------------------------------
+			// Return total number of permutations
+			//------------------------------------
+
+			public long getTotal () 
+			{
+				return total;
+			}
+
+			//-----------------------------
+			// Are there more permutations?
+			//-----------------------------
+
+			public bool hasMore () 
+			{
+				return (numLeft > 0);
+			}
+
+			//------------------
+			// Compute factorial
+			//------------------
+
+			private static long getFactorial (int n) 
+			{
+				long fact = 1;
+				for (int i = n; i > 1; i--) 
+				{
+					fact = fact * i;
+				}
+				return fact;
+			}
+
+			//--------------------------------------------------------
+			// Generate next permutation (algorithm from Rosen p. 284)
+			//--------------------------------------------------------
+
+			public int[] getNext () 
+			{
+
+				if (numLeft == total) 
+				{
+					--numLeft;
+					return a;
+				}
+
+				int temp;
+
+				// Find largest index j with a[j] < a[j+1]
+
+				int j = a.Length - 2;
+				while (a[j] > a[j+1]) 
+				{
+					j--;
+				}
+
+				// Find index k such that a[k] is smallest integer
+				// greater than a[j] to the right of a[j]
+
+				int k = a.Length - 1;
+				while (a[j] > a[k]) 
+				{
+					k--;
+				}
+
+				// Interchange a[j] and a[k]
+
+				temp = a[k];
+				a[k] = a[j];
+				a[j] = temp;
+
+				// Put tail end of permutation after jth position in increasing order
+
+				int r = a.Length - 1;
+				int s = j + 1;
+
+				while (r > s) 
+				{
+					temp = a[s];
+					a[s] = a[r];
+					a[r] = temp;
+					r--;
+					s++;
+				}
+
+				--numLeft;
+				return a;
+			}
+		}
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/Properties/AssemblyInfo.cs b/SSharpSmartThreadPoolTests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..11193fa
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/Properties/AssemblyInfo.cs
@@ -0,0 +1,8 @@
+ï»¿using System.Reflection;
+
+[assembly: AssemblyTitle ("SSharpSmartThreadPoolTests")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("SSharpSmartThreadPoolTests")]
+[assembly: AssemblyCopyright ("Copyright Â©  2015")]
+[assembly: AssemblyVersion ("1.0.0.*")]
+
diff --git a/SSharpSmartThreadPoolTests/Properties/ControlSystem.cfg b/SSharpSmartThreadPoolTests/Properties/ControlSystem.cfg
new file mode 100644
index 0000000..e69de29
diff --git a/SSharpSmartThreadPoolTests/QueueWorkItemHelper.cs b/SSharpSmartThreadPoolTests/QueueWorkItemHelper.cs
new file mode 100644
index 0000000..55ac390
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/QueueWorkItemHelper.cs
@@ -0,0 +1,681 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using Amib.SSharpThreading.Internal;
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+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((WorkItemCallback) 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 class WorkItemInfoComparer
+        {
+            private WorkItemInfo _neededWorkItemInfo;
+            private object _state;
+
+            public int SleepTime { get; set; }
+
+            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 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemInfo;
+                    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/SSharpSmartThreadPoolTests/SSharpSmartThreadPoolTests.csproj b/SSharpSmartThreadPoolTests/SSharpSmartThreadPoolTests.csproj
new file mode 100644
index 0000000..78b66f9
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/SSharpSmartThreadPoolTests.csproj
@@ -0,0 +1,158 @@
+ï»¿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SSharpSmartThreadPoolTests</RootNamespace>
+    <AssemblyName>SSharpSmartThreadPoolTests</AssemblyName>
+    <ProjectTypeGuids>{0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <PlatformFamilyName>WindowsCE</PlatformFamilyName>
+    <PlatformID>E2BECB1F-8C8C-41ba-B736-9BE7D946A398</PlatformID>
+    <OSVersion>5.0</OSVersion>
+    <DeployDirSuffix>SmartDeviceProject1</DeployDirSuffix>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <NativePlatformName>Windows CE</NativePlatformName>
+    <FormFactorID>
+    </FormFactorID>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE;_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <FileAlignment>512</FileAlignment>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
+    <DebugType>none</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <FileAlignment>512</FileAlignment>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
+    <DefineConstants>_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="mscorlib" />
+    <Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="SimplSharpHelperInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="SimplSharpPro, Version=1.4.0.14, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="SSharpCrestronExtensionsLibrary, Version=1.0.0.13603, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\Simpl# Libraries\SSharpCrestronExtensionsLibrary\SSharpCrestronExtensionsLibrary\bin\Release\SSharpCrestronExtensionsLibrary.dll</HintPath>
+    </Reference>
+    <Reference Include="SSharpNunitLite, Version=3.0.5543.22955, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\SSharpNUnitLite3\SSharpNunitLite\bin\Release\SSharpNunitLite.dll</HintPath>
+    </Reference>
+    <Reference Include="SSharpThreadingLibrary, Version=1.0.0.15966, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\Simpl# Libraries\SSharpThreadingLibrary\SSharpThreadingLibrary\bin\Release\SSharpThreadingLibrary.dll</HintPath>
+    </Reference>
+    <Reference Include="SSMonoDiagnosticsLibrary, Version=1.0.0.23683, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\Simpl# Libraries\SSMonoDiagnosticsLibrary\SSMonoDiagnosticsLibrary\bin\Release\SSMonoDiagnosticsLibrary.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Data" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PermutationGenerator.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="QueueWorkItemHelper.cs" />
+    <Compile Include="TestActionT.cs" />
+    <Compile Include="TestCancel.cs" />
+    <Compile Include="TestChainedDelegates.cs" />
+    <Compile Include="TestConcurrencyChanges.cs" />
+    <Compile Include="TestExceptions.cs" />
+    <Compile Include="TestFalseFillStateWithParams.cs" />
+    <Compile Include="TestFillStateWithParams.cs" />
+    <Compile Include="TestFunc.cs" />
+    <Compile Include="TestFuncT.cs" />
+    <Compile Include="TestGetResult.cs" />
+    <Compile Include="TestMultipleWorkItems.cs" />
+    <Compile Include="TestParallelMethods.cs" />
+    <Compile Include="TestPostExecute.cs" />
+    <Compile Include="TestPriorityQueue.cs" />
+    <Compile Include="TestQueueWorkItem.cs" />
+    <Compile Include="TestStartSuspended.cs" />
+    <Compile Include="TestStateDispose.cs" />
+    <Compile Include="TestThreadPriority.cs" />
+    <Compile Include="TestThreadsCreate.cs" />
+    <Compile Include="TestWaitForIdle.cs" />
+    <Compile Include="TestWIGActionT.cs" />
+    <Compile Include="TestWIGChainedDelegates.cs" />
+    <Compile Include="TestWIGConcurrency.cs" />
+    <Compile Include="TestWIGConcurrencyChanges.cs" />
+    <Compile Include="TestWIGExceptions.cs" />
+    <Compile Include="TestWIGFillStateWithParams.cs" />
+    <Compile Include="TestWIGFuncT.cs" />
+    <Compile Include="TestWIGGetResult.cs" />
+    <Compile Include="TestWIGMultipleWorkItems.cs" />
+    <Compile Include="TestWIGPostExecute.cs" />
+    <Compile Include="TestWIGQueueWorkItem.cs" />
+    <Compile Include="TestWIGStateDispose.cs" />
+    <Compile Include="TestWIGWaitForIdle.cs" />
+    <Compile Include="TestWorkItemsGroups.cs" />
+    <Compile Include="TestWorkItemsQueue.cs" />
+    <Compile Include="TestWorkItemTimeout.cs" />
+    <None Include="Properties\ControlSystem.cfg" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\SSharpSmartThreadPool\SSharpSmartThreadPool.csproj">
+      <Project>{06299ED2-056A-427C-8C84-D716197080EB}</Project>
+      <Name>SSharpSmartThreadPool</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{0B4745B0-194B-4BB6-8E21-E9057CA92300}">
+        <ProgramIdTag>SSharpSmartThreadPoolTests</ProgramIdTag>
+        <SystemName>SSharpSmartThreadPoolTests</SystemName>
+        <Programmer />
+        <ArchiveFilename>C:\Projects\Crestron\Simpl# Pro Libraries\SSharpSmartThreadPool\SSharpSmartThreadPoolTests\bin\Debug\SSharpSmartThreadPoolTests.cplz</ArchiveFilename>
+        <MinFirmwareVersion>1.009.0029</MinFirmwareVersion>
+        <CompiledOn>3/14/2015 11:00:21 AM</CompiledOn>
+        <AdditionalInfo />
+        <EmbedSourceArchive>False</EmbedSourceArchive>
+        <CopyTo />
+        <OriginalReferenceSources>
+          <Reference sourceFilename="../../SSharpNUnitLite3/SSharpNunitLite/bin/Release/SSharpNunitLite.dll" referenceName="SSharpNunitLite" />
+          <Reference sourceFilename="../../../Simpl%23%20Libraries/SSharpCrestronExtensionsLibrary/SSharpCrestronExtensionsLibrary/bin/Release/SSharpCrestronExtensionsLibrary.dll" referenceName="SSharpCrestronExtensionsLibrary" />
+        </OriginalReferenceSources>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+  <PropertyGroup>
+    <PostBuildEvent>rem S# Pro preparation will execute after these operations</PostBuildEvent>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/SSharpSmartThreadPoolTests/TestActionT.cs b/SSharpSmartThreadPoolTests/TestActionT.cs
new file mode 100644
index 0000000..7a93b8c
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestActionT.cs
@@ -0,0 +1,124 @@
+using Amib.SSharpThreading;
+using NUnit.Framework;
+
+namespace SmartThreadPoolTests
+{
+    /// <summary>
+    /// Summary description for TestCancel.
+    /// </summary>
+    [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/SSharpSmartThreadPoolTests/TestCancel.cs b/SSharpSmartThreadPoolTests/TestCancel.cs
new file mode 100644
index 0000000..b0f3fd1
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestCancel.cs
@@ -0,0 +1,500 @@
+using Crestron.SimplSharpPro.CrestronThread;
+using Crestron.SimplSharp;
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+	{
+	/// <summary>
+	/// Summary description for TestCancel.
+	/// </summary>
+	[TestFixture]
+	[Category ("TestCancel")]
+	public class TestCancel
+		{
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[Test]
+		public void CancelInQueueWorkItem ()
+			{
+			Assert.Throws<WorkItemCancelException> (() =>
+				{
+					STPStartInfo stpStartInfo = new STPStartInfo ();
+					stpStartInfo.StartSuspended = true;
+
+					SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
+					IWorkItemResult wir = stp.QueueWorkItem (arg => null);
+
+					wir.Cancel ();
+
+					Assert.IsTrue (wir.IsCanceled);
+
+					try
+						{
+						wir.GetResult ();
+						}
+					finally
+						{
+						stp.Shutdown ();
+						}
+				});
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[Test]
+		public void CancelInProgressWorkItemSoft ()
+			{
+			Assert.Throws<WorkItemCancelException> (() =>
+				{
+					ManualResetEvent waitToStart = new ManualResetEvent (false);
+
+					SmartThreadPool stp = new SmartThreadPool ();
+					IWorkItemResult wir = stp.QueueWorkItem (state =>
+						{
+							waitToStart.Set ();
+							Thread.Sleep (100);
+							return null;
+						});
+
+					waitToStart.WaitOne ();
+
+					wir.Cancel (false);
+
+					Assert.IsTrue (wir.IsCanceled);
+
+					try
+						{
+						wir.GetResult ();
+						}
+					finally
+						{
+						stp.Shutdown ();
+						}
+				});
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[Test]
+		public void CancelCancelledWorkItemAbort ()
+			{
+			Assert.Throws<WorkItemCancelException> (() =>
+				{
+					ManualResetEvent waitToStart = new ManualResetEvent (false);
+
+					SmartThreadPool stp = new SmartThreadPool ();
+					IWorkItemResult wir = stp.QueueWorkItem (state =>
+						{
+							waitToStart.Set ();
+							while (true)
+								{
+								Thread.Sleep (1000);
+								}
+							return null;
+						});
+
+					waitToStart.WaitOne ();
+
+					wir.Cancel (false);
+
+					Assert.IsTrue (wir.IsCanceled);
+
+					bool completed = stp.WaitForIdle (1000);
+
+					Assert.IsFalse (completed);
+
+					wir.Cancel (true);
+
+					try
+						{
+						wir.GetResult ();
+						}
+					finally
+						{
+						stp.Shutdown ();
+						}
+				});
+			}
+
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[Test]
+		public void CancelInProgressWorkItemAbort ()
+			{
+			Assert.Throws<WorkItemCancelException> (() =>
+				{
+					ManualResetEvent waitToStart = new ManualResetEvent (false);
+					int counter = 0;
+
+					SmartThreadPool stp = new SmartThreadPool ();
+					IWorkItemResult wir = stp.QueueWorkItem (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 ();
+						}
+				});
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[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 (
+				state =>
+					{
+					waitToStart.Set ();
+					waitToComplete.WaitOne ();
+					cancelled = SmartThreadPool.IsWorkItemCanceled;
+					return null;
+					}
+				);
+
+			waitToStart.WaitOne ();
+
+			wir.Cancel (false);
+
+			waitToComplete.Set ();
+
+			stp.WaitForIdle ();
+
+			Assert.IsTrue (cancelled);
+
+			stp.Shutdown ();
+			}
+
+		/// <summary>
+		/// 1. Create STP
+		/// 2. Queue work item that takes some time
+		/// 3. Wait for it to start
+		/// 4. Cancel the work item (soft)
+		/// 5. Don't call to SmartThreadPool.IsWorkItemCanceled
+		/// 6. Wait for the STP to get idle
+		/// 7. Work item's GetResult should throw WorkItemCancelException
+		/// </summary>        
+		[Test]
+		public void CancelInProgressWorkItemSoftWithIgnoreSample ()
+			{
+			Assert.Throws<WorkItemCancelException> (() =>
+				{
+					ManualResetEvent waitToStart = new ManualResetEvent (false);
+					ManualResetEvent waitToComplete = new ManualResetEvent (false);
+
+					SmartThreadPool stp = new SmartThreadPool ();
+					IWorkItemResult wir = stp.QueueWorkItem (state =>
+						{
+							waitToStart.Set ();
+							Thread.Sleep (100);
+							waitToComplete.WaitOne ();
+							return null;
+						});
+
+					waitToStart.WaitOne ();
+
+					wir.Cancel (false);
+
+					waitToComplete.Set ();
+
+					stp.WaitForIdle ();
+
+					// Throws WorkItemCancelException
+					wir.GetResult ();
+
+					stp.Shutdown ();
+				});
+			}
+
+		/// <summary>
+		/// 1. Create STP
+		/// 2. Queue work item that takes some time
+		/// 3. Wait for it to start
+		/// 4. Cancel the work item (soft)
+		/// 5. Call to AbortOnWorkItemCancel
+		/// 5. Wait for the STP to get idle
+		/// 6. Make sure nothing ran in the work item after the AbortOnWorkItemCancel
+		/// </summary>        
+		[Test]
+		public void CancelInProgressWorkItemSoftWithAbortOnWorkItemCancel ()
+			{
+			bool abortFailed = false;
+			ManualResetEvent waitToStart = new ManualResetEvent (false);
+			ManualResetEvent waitToCancel = new ManualResetEvent (false);
+
+			SmartThreadPool stp = new SmartThreadPool ();
+			IWorkItemResult wir = stp.QueueWorkItem (
+				state =>
+					{
+					waitToStart.Set ();
+					waitToCancel.WaitOne ();
+					SmartThreadPool.AbortOnWorkItemCancel ();
+					abortFailed = true;
+					return null;
+					});
+
+			waitToStart.WaitOne ();
+
+			wir.Cancel (false);
+
+			waitToCancel.Set ();
+
+			stp.WaitForIdle ();
+
+			Assert.IsTrue (wir.IsCanceled);
+			Assert.IsFalse (abortFailed);
+
+			stp.Shutdown ();
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[Test]
+		public void CancelCanceledWorkItem ()
+			{
+			Assert.Throws<WorkItemCancelException> (() =>
+				{
+					STPStartInfo stpStartInfo = new STPStartInfo ();
+					stpStartInfo.StartSuspended = true;
+
+					SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
+					IWorkItemResult wir = stp.QueueWorkItem (state => 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 ();
+						}
+				});
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[Test]
+		public void CancelCompletedWorkItem ()
+			{
+			SmartThreadPool stp = new SmartThreadPool ();
+			IWorkItemResult wir = stp.QueueWorkItem (
+				state => 1
+				);
+
+			stp.WaitForIdle ();
+
+			Assert.AreEqual (wir.GetResult (), 1);
+
+			wir.Cancel ();
+
+			Assert.AreEqual (wir.GetResult (), 1);
+
+			stp.Shutdown ();
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[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 (
+					state => { Thread.Sleep (500); ++counter; return null; }
+					);
+				}
+
+			Thread.Sleep (100);
+
+			stp.Cancel (true);
+
+			Assert.AreEqual (counter, 0);
+
+			stp.Shutdown ();
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>        
+		[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 (
+					state => { Thread.Sleep (500); ++counter; return null; }
+					);
+				}
+
+			Thread.Sleep (100);
+
+			wig.Cancel (true);
+
+			Assert.AreEqual (counter, 0);
+
+			stp.Shutdown ();
+			}
+
+		/// <summary>
+		/// 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
+		/// </summary>                
+		[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 (
+					state => { Interlocked.Increment (ref counter1); Thread.Sleep (500); Interlocked.Increment (ref counter1); return null; }
+					);
+				}
+
+			for (int i = 0; i < 3; i++)
+				{
+				wig2.QueueWorkItem (
+					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 ();
+			}
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestChainedDelegates.cs b/SSharpSmartThreadPoolTests/TestChainedDelegates.cs
new file mode 100644
index 0000000..5890222
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestChainedDelegates.cs
@@ -0,0 +1,90 @@
+using System;
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+	{
+	/// <summary>
+	/// Summary description for TestChainedDelegates.
+	/// </summary>
+	[TestFixture]
+	[Category ("TestChainedDelegates")]
+	public class TestChainedDelegates
+		{
+		[Test]
+		public void GoodCallback ()
+			{
+			SmartThreadPool stp = new SmartThreadPool ();
+
+			stp.QueueWorkItem (new WorkItemCallback (DoWork));
+
+			stp.WaitForIdle ();
+
+			stp.Shutdown ();
+			}
+
+		[Test]
+		public void ChainedDelegatesCallback ()
+			{
+			Assert.Throws<NotSupportedException> (() =>
+				{
+					SmartThreadPool stp = new SmartThreadPool ();
+
+					WorkItemCallback workItemCallback = new WorkItemCallback (DoWork);
+					workItemCallback += new WorkItemCallback (DoWork);
+
+					stp.QueueWorkItem (workItemCallback);
+
+					stp.WaitForIdle ();
+
+					stp.Shutdown ();
+				});
+			}
+
+		[Test]
+		public void GoodPostExecute ()
+			{
+			SmartThreadPool stp = new SmartThreadPool ();
+
+			stp.QueueWorkItem (
+				new WorkItemCallback (DoWork),
+				null,
+				new PostExecuteWorkItemCallback (DoPostExecute));
+
+			stp.WaitForIdle ();
+
+			stp.Shutdown ();
+			}
+
+		[Test]
+		public void ChainedDelegatesPostExecute ()
+			{
+			Assert.Throws<NotSupportedException> (() =>
+				{
+					SmartThreadPool stp = new SmartThreadPool ();
+
+					PostExecuteWorkItemCallback postExecuteWorkItemCallback = DoPostExecute;
+					postExecuteWorkItemCallback += DoPostExecute;
+
+					stp.QueueWorkItem (new WorkItemCallback (DoWork), null, postExecuteWorkItemCallback);
+
+					stp.WaitForIdle ();
+
+					stp.Shutdown ();
+				});
+			}
+
+
+		private object DoWork (object state)
+			{
+			return null;
+			}
+
+		private void DoPostExecute (IWorkItemResult wir)
+			{
+			}
+
+
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestConcurrencyChanges.cs b/SSharpSmartThreadPoolTests/TestConcurrencyChanges.cs
new file mode 100644
index 0000000..b93b6b0
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestConcurrencyChanges.cs
@@ -0,0 +1,151 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSMono.Diagnostics;
+using Interlocked = Crestron.SimplSharp.Interlocked;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+	/// Summary description for TestConcurrencyChanges.
+	/// </summary>
+	[TestFixture]
+	[Category("TestConcurrencyChanges")]
+	public class TestConcurrencyChanges
+	{
+	    /// <summary>
+        /// Example of waiting for idle
+        /// </summary>
+        [Test]
+        public void TestMaxThreadsChange()
+        {
+            SmartThreadPool smartThreadPool = new SmartThreadPool(1 * 1000, 1, 0);
+
+            for (int i = 0; i < 100; ++i)
+            {
+                smartThreadPool.QueueWorkItem(
+                    new WorkItemCallback(this.DoSomeWork),
+                    null);
+            }
+
+            bool 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 = 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();
+        }
+
+        /// <summary>
+        /// Example of waiting for idle
+        /// </summary>
+        [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/SSharpSmartThreadPoolTests/TestExceptions.cs b/SSharpSmartThreadPoolTests/TestExceptions.cs
new file mode 100644
index 0000000..e943040
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestExceptions.cs
@@ -0,0 +1,87 @@
+using System;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+	/// Summary description for TestExceptions.
+	/// </summary>
+	[TestFixture]
+	[Category("TestExceptions")]
+	public class TestExceptions
+	{
+		private class DivArgs
+		{
+			public int x;
+			public int y;
+		}
+
+		[Test]
+		public void ExceptionThrowing() 
+		{ 
+			SmartThreadPool _smartThreadPool = new SmartThreadPool();
+
+			DivArgs divArgs = new DivArgs();
+			divArgs.x = 10;
+			divArgs.y = 0;
+
+			IWorkItemResult wir = 
+				_smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);
+
+			try
+			{
+				wir.GetResult();
+			}
+			catch(WorkItemResultException wire)
+			{
+				Assert.IsTrue(wire.InnerException is DivideByZeroException);
+				return;
+			}
+			catch(Exception e)
+			{
+                e.GetHashCode();
+				Assert.Fail();
+			}
+			Assert.Fail();
+		} 
+
+		[Test]
+		public void ExceptionReturning() 
+		{ 
+			bool success = true;
+
+			SmartThreadPool _smartThreadPool = new SmartThreadPool();
+
+			DivArgs divArgs = new DivArgs();
+			divArgs.x = 10;
+			divArgs.y = 0;
+
+			IWorkItemResult wir = 
+				_smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);
+
+			Exception e = null;
+			try
+			{
+				wir.GetResult(out e);
+			}
+			catch (Exception ex)
+			{
+                ex.GetHashCode();
+				success = false;
+			}
+
+			Assert.IsTrue(success);
+			Assert.IsTrue(e is DivideByZeroException);
+		} 
+
+		private object DoDiv(object state)
+		{ 
+			DivArgs divArgs = (DivArgs)state;
+			return (divArgs.x / divArgs.y);
+		}
+
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestFalseFillStateWithParams.cs b/SSharpSmartThreadPoolTests/TestFalseFillStateWithParams.cs
new file mode 100644
index 0000000..9c1c42d
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestFalseFillStateWithParams.cs
@@ -0,0 +1,246 @@
+using System;
+using Crestron.SimplSharp;
+using Amib.SSharpThreading;
+using NUnit.Framework;
+
+namespace STPTests
+{
+    /// <summary>
+    /// Summary description for TestFalseFillStateWithArgs.
+    /// </summary>
+    [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<int> wir = _stp.QueueWorkItem(new Func<int>(Func0));
+            Assert.IsNull(wir.State);
+        }
+
+        [Test]
+        public void STPFuncT1()
+        {
+            IWorkItemResult<bool> wir = _stp.QueueWorkItem(new Func<int, bool>(Func1), 17);
+            Assert.IsNull(wir.State);
+        }
+
+        [Test]
+        public void STPFuncT2()
+        {
+            IWorkItemResult<string> wir = _stp.QueueWorkItem(new Func<char, string, string>(Func2), 'a', "bla bla");
+            Assert.IsNull(wir.State);
+        }
+
+        [Test]
+        public void STPFuncT3()
+        {
+            char[] chars = new char[] { 'a', 'b' };
+            object obj = new object();
+
+            IWorkItemResult<char> wir = _stp.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _stp.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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<int> wir = _wig.QueueWorkItem(new Func<int>(Func0));
+            Assert.IsNull(wir.State);
+        }
+
+        [Test]
+        public void WIGFuncT1()
+        {
+            IWorkItemResult<bool> wir = _wig.QueueWorkItem(new Func<int, bool>(Func1), 17);
+            Assert.IsNull(wir.State);
+        }
+
+        [Test]
+        public void WIGFuncT2()
+        {
+            IWorkItemResult<string> wir = _wig.QueueWorkItem(new Func<char, string, string>(Func2), 'a', "bla bla");
+            Assert.IsNull(wir.State);
+        }
+
+        [Test]
+        public void WIGFuncT3()
+        {
+            char[] chars = new char[] { 'a', 'b' };
+            object obj = new object();
+
+            IWorkItemResult<char> wir = _wig.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _wig.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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/SSharpSmartThreadPoolTests/TestFillStateWithParams.cs b/SSharpSmartThreadPoolTests/TestFillStateWithParams.cs
new file mode 100644
index 0000000..fe10a93
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestFillStateWithParams.cs
@@ -0,0 +1,207 @@
+using System;
+using Crestron.SimplSharp;
+using Amib.SSharpThreading;
+using NUnit.Framework;
+
+namespace STPTests
+{
+    /// <summary>
+    /// Summary description for TestFillStateWithArgs.
+    /// </summary>
+    [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<int> wir = _stp.QueueWorkItem(new Func<int>(Func0));
+            Assert.AreEqual(wir.State, null);
+        }
+
+        [Test]
+        public void FuncT1()
+        {
+            IWorkItemResult<bool> wir = _stp.QueueWorkItem(new Func<int, bool>(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<string> wir = _stp.QueueWorkItem(new Func<char, string, string>(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<char> wir = _stp.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _stp.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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/SSharpSmartThreadPoolTests/TestFunc.cs b/SSharpSmartThreadPoolTests/TestFunc.cs
new file mode 100644
index 0000000..ee4184f
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestFunc.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;
+
+using Amib.SSharpThreading;
+using NUnit.Framework;
+
+namespace STPTests
+{
+    /// <summary>
+    /// Summary description for TestCancel.
+    /// </summary>
+    [TestFixture]
+    [Category("TestFuncT")]
+    public class TestFuncT
+    {
+        [Test]
+        public void FuncT()
+        {
+            SmartThreadPool stp = new SmartThreadPool();
+            IWorkItemResult<int> wir = 
+                stp.QueueWorkItem(new Func<int, int>(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/SSharpSmartThreadPoolTests/TestFuncT.cs b/SSharpSmartThreadPoolTests/TestFuncT.cs
new file mode 100644
index 0000000..4ddffb3
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestFuncT.cs
@@ -0,0 +1,109 @@
+using System;
+using Amib.SSharpThreading;
+using NUnit.Framework;
+
+namespace SmartThreadPoolTests
+{
+    /// <summary>
+    /// Summary description for TestCancel.
+    /// </summary>
+    [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<int> wir = _stp.QueueWorkItem(new Func<int>(MaxInt));
+
+            int result = wir.GetResult();
+
+            Assert.AreEqual(result, int.MaxValue);
+        }
+
+        [Test]
+        public void FuncT1()
+        {
+            IWorkItemResult<bool> wir = _stp.QueueWorkItem(new Func<bool, bool>(Not), true);
+
+            bool result = wir.Result;
+
+            Assert.AreEqual(result, false);
+        }
+
+        [Test]
+        public void FuncT2()
+        {
+            IWorkItemResult<string> wir = _stp.QueueWorkItem(new Func<string, string, string>(string.Concat), "ABC", "xyz");
+
+            string result = wir.Result;
+
+            Assert.AreEqual(result, "ABCxyz");
+        }
+
+        [Test]
+        public void FuncT3()
+        {
+            IWorkItemResult<string> wir = _stp.QueueWorkItem(new Func<string, int, int, string>(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<int[]> wir = _stp.QueueWorkItem(new Func<int[], int, int, int, int[]>(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/SSharpSmartThreadPoolTests/TestGetResult.cs b/SSharpSmartThreadPoolTests/TestGetResult.cs
new file mode 100644
index 0000000..fedeaf0
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestGetResult.cs
@@ -0,0 +1,111 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+	{
+	/// <summary>
+	/// Summary description for GetResultExample.
+	/// </summary>
+	[TestFixture]
+	[Category ("TestGetResult")]
+	public class TestGetResult
+		{
+		/// <summary>
+		/// Example of how to queue a work item and then wait infinitely for the result.
+		/// </summary>
+		[Test]
+		public void BlockingCall ()
+			{
+			SmartThreadPool smartThreadPool = new SmartThreadPool ();
+
+			bool success = false;
+
+			IWorkItemResult wir =
+				smartThreadPool.QueueWorkItem (new WorkItemCallback (this.DoSomeWork), null);
+
+			if (!wir.IsCompleted)
+				{
+				int result = (int)wir.GetResult ();
+				success = (1 == result);
+				}
+
+			smartThreadPool.Shutdown ();
+
+			Assert.IsTrue (success);
+			}
+
+		/// <summary>
+		/// Example of how to queue a work item and then wait on a timeout for the result.
+		/// </summary>
+		[Test]
+		public void Timeout ()
+			{
+			Assert.Throws<WorkItemTimeoutException> (() =>
+				{
+					SmartThreadPool smartThreadPool = new SmartThreadPool ();
+
+					IWorkItemResult wir = smartThreadPool.QueueWorkItem (new WorkItemCallback (this.DoSomeWork), null);
+
+					try
+						{
+						wir.GetResult (500, true);
+						}
+					finally
+						{
+						smartThreadPool.Shutdown ();
+						}
+				});
+			}
+
+		/// <summary>
+		/// Example of how to interrupt the waiting for a work item to complete.
+		/// </summary>
+		[Test]
+		public void WorkItemWaitCanceling ()
+			{
+			Assert.Throws<WorkItemTimeoutException> (() =>
+				{
+			SmartThreadPool smartThreadPool = new SmartThreadPool ();
+
+			ManualResetEvent cancelWaitHandle = new ManualResetEvent (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 (Crestron.SimplSharp.Timeout.Infinite, true, cancelWaitHandle);
+					}
+				finally
+					{
+					smartThreadPool.Shutdown ();
+					}
+				});
+			}
+
+		private object DoSomeWork (object state)
+			{
+			Thread.Sleep (1000);
+			return 1;
+			}
+
+		private object SignalCancel (object state)
+			{
+			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
+			Thread.Sleep (250);
+			cancelWaitHandle.Set ();
+			return null;
+			}
+
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestMultipleWorkItems.cs b/SSharpSmartThreadPoolTests/TestMultipleWorkItems.cs
new file mode 100644
index 0000000..fe24d43
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestMultipleWorkItems.cs
@@ -0,0 +1,296 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+	/// Summary description for MultipleWorkItemsExample.
+	/// </summary>
+	[TestFixture]
+	[Category("TestMultipleWorkItems")]
+	public class TestMultipleWorkItems
+	{
+		/// <summary>
+		/// Example of how to queue several work items and then wait infinitely for 
+		/// all of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAll() 
+		{ 
+			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 WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			SmartThreadPool.WaitAll(wirs);
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				if (!wirs[i].IsCompleted)
+				{
+					success = false;
+					break;
+				}
+				else
+				{
+					int result = (int)wirs[i].GetResult();
+					if (1 != result)
+					{
+						success = false;
+						break;
+					}
+				}
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait infinitely for 
+		/// one of them to complete.
+		/// 
+		/// You can use this technique if you have several work items that return the same 
+		/// infomration, but use different method to aquire it. Just execute all of them at
+		/// once and wait for the first work item to complete.
+		/// 
+		/// For example: You need an information about a person and you can query several 
+		/// information sites (FBI, CIA, etc.). Query all of them at once and use the first
+		/// answer to arrive.
+		/// </summary>
+		[Test]
+		public void WaitAny() 
+		{ 
+			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 WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			int index = SmartThreadPool.WaitAny(wirs);
+
+			if (wirs[index].IsCompleted)
+			{
+				int result = (int)wirs[index].GetResult();
+				if (1 == result)
+				{
+					success = true;
+				}
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for all
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAllWithTimeoutSuccess()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+		    IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			bool timeout = !SmartThreadPool.WaitAll(wirs, 1500, true);
+			bool success = !timeout;
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for all
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAllWithTimeoutFailure()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+		    IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			bool timeout = !SmartThreadPool.WaitAll(wirs, 10, true);
+			bool success = timeout;
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for any
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAnyWithTimeoutSuccess()
+		{ 
+			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 WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			int index = SmartThreadPool.WaitAny(wirs, 1500, true);
+
+			success = (index != WaitHandle.WaitTimeout);
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		}
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for any
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAnyWithTimeoutFailure()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+			bool success;
+
+			IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			int index = SmartThreadPool.WaitAny(wirs, 10, true);
+
+			success = (index == WaitHandle.WaitTimeout);
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		}
+
+		[Test]
+		public void WaitAllWithEmptyArray()
+		{ 
+
+			IWorkItemResult [] wirs = new IWorkItemResult[0];
+
+			bool success = SmartThreadPool.WaitAll(wirs);;
+
+			Assert.IsTrue(success);
+		}
+
+		private object DoSomeWork(object state)
+		{ 
+			Thread.Sleep(1000);
+			return 1;
+		}
+
+        [Test]
+        public void WaitAllT()
+        {
+            SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+            bool success = true;
+
+            IWorkItemResult<int>[] wirs = new IWorkItemResult<int>[5];
+
+            for (int i = 0; i < wirs.Length; ++i)
+            {
+                wirs[i] = smartThreadPool.QueueWorkItem(new Func<int, int, int>(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<int>[] wirs = new IWorkItemResult<int>[5];
+
+            for (int i = 0; i < wirs.Length; ++i)
+            {
+                wirs[i] = smartThreadPool.QueueWorkItem(new Func<int, int, int>(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/SSharpSmartThreadPoolTests/TestParallelMethods.cs b/SSharpSmartThreadPoolTests/TestParallelMethods.cs
new file mode 100644
index 0000000..b89519b
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestParallelMethods.cs
@@ -0,0 +1,110 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using Interlocked = Crestron.SimplSharp.Interlocked;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+    /// Summary description for TestParallelMethods.
+	/// </summary>
+	[TestFixture]
+	[Category("TestParallelMethods")]
+    public class TestParallelMethods
+	{
+		[Test]
+		public void TestJoin() 
+		{ 
+			SmartThreadPool stp = new SmartThreadPool();
+
+		    SafeCounter sc = new SafeCounter();
+
+            stp.Join(
+                sc.Increment,
+                sc.Increment,
+                sc.Increment);
+
+            Assert.AreEqual(3, sc.Counter);
+
+		    for (int j = 0; j < 10; j++)
+		    {
+                sc.Reset();
+
+                Action[] actions = new Action[1000];
+                for (int i = 0; i < actions.Length; i++)
+                {
+                    actions[i] = sc.Increment;
+                }
+
+                stp.Join(actions);
+
+                Assert.AreEqual(actions.Length, sc.Counter);
+		    }
+
+            stp.Shutdown();
+		}
+
+	    private class SafeCounter
+        {
+            private int _counter;
+
+            public void Increment()
+            {
+                Interlocked.Increment(ref _counter);
+            }
+
+            public int Counter
+            {
+                get { return _counter; }
+            }
+
+            public void Reset()
+            {
+                _counter = 0;
+            }
+        }
+
+	    [Test]
+	    public void TestChoice() 
+	    { 
+	        SmartThreadPool stp = new SmartThreadPool();
+
+	        int index = stp.Choice(
+	            () => Thread.Sleep(1000),
+	            () => Thread.Sleep(1500),
+	            () => Thread.Sleep(500));
+
+	        Assert.AreEqual(2, index);  
+            
+	        index = stp.Choice(
+	            () => Thread.Sleep(300),
+	            () => Thread.Sleep(100),
+	            () => Thread.Sleep(200));
+
+	        Assert.AreEqual(1, index);
+
+	        stp.Shutdown();
+	    } 
+        
+        [Test]
+	    public void TestPipe() 
+	    { 
+            SafeCounter sc = new SafeCounter();
+	        SmartThreadPool stp = new SmartThreadPool();
+
+            stp.Pipe(
+                sc,
+                sc1 => { if (sc.Counter == 0) { sc1.Increment(); }}, 
+                sc1 => { if (sc.Counter == 1) { sc1.Increment(); }}, 
+                sc1 => { if (sc.Counter == 2) { sc1.Increment(); }} 
+                );
+
+            Assert.AreEqual(3, sc.Counter);
+
+	        stp.Shutdown();
+	    }
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestPostExecute.cs b/SSharpSmartThreadPoolTests/TestPostExecute.cs
new file mode 100644
index 0000000..91ce1f9
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestPostExecute.cs
@@ -0,0 +1,228 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+	/// Summary description for DoTestPostExecute.
+	/// </summary>
+	[TestFixture]
+	[Category("DoTestPostExecute")]
+	public class TestPostExecution
+	{
+		/// <summary>
+		/// 
+		/// </summary>
+		[Test]
+		public void DefaultPostExecute_AlwaysCall()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Always, true));
+		}
+
+		[Test]
+		public void DefaultPostExecute_NeverCall()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Never, false));
+		}
+
+		[Test]
+		public void DefaultPostExecute_CallWhenCanceled()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
+		}
+
+		[Test]
+		public void DefaultPostExecute_CallWhenNotCanceled()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
+		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		[Test]
+		public void PostExecute_AlwaysCall()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Always, true));
+		}
+
+		[Test]
+		public void PostExecute_NeverCall()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Never, false));
+		}
+
+		[Test]
+		public void PostExecute_CallWhenCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
+		}
+
+		[Test]
+		public void PostExecute_CallWhenNotCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
+		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		[Test]
+		public void PostExecuteWithCancel_AlwaysCall()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Always, true));
+		}
+
+		[Test]
+		public void PostExecuteWithCancel_NeverCall()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Never, false));
+		}
+
+		[Test]
+		public void PostExecuteWithCancel_CallWhenCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemCanceled, true));
+		}
+
+		[Test]
+		public void PostExecuteWithCancel_CallWhenNotCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemNotCanceled, false));
+		}
+
+
+		private class PostExecuteResult
+		{
+			public ManualResetEvent wh = new ManualResetEvent(false);
+		}
+
+		/// <summary>
+		/// Example of how to use the post execute callback
+		/// </summary>
+		private bool DoTestDefaultPostExecute(CallToPostExecute callToPostExecute, bool answer)
+		{ 
+			STPStartInfo stpStartInfo = new STPStartInfo();
+			stpStartInfo.CallToPostExecute = callToPostExecute;
+			stpStartInfo.PostExecuteWorkItemCallback = new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork);
+
+			SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo);
+
+			bool success = false;
+
+			PostExecuteResult postExecuteResult = new PostExecuteResult();
+
+			IWorkItemResult wir = 
+				smartThreadPool.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				postExecuteResult);
+
+			if (!wir.IsCompleted)
+			{
+				int result = (int)wir.GetResult();
+				success = (1 == result);
+				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
+			}
+
+			smartThreadPool.Shutdown();
+
+			return success;
+		}
+
+
+		/// <summary>
+		/// Example of how to use the post execute callback
+		/// </summary>
+		private bool DoTestPostExecute(CallToPostExecute callToPostExecute, bool answer)
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+			bool success = false;
+
+			PostExecuteResult postExecuteResult = new PostExecuteResult();
+
+			IWorkItemResult wir = 
+				smartThreadPool.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				postExecuteResult,
+				new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
+				callToPostExecute);
+
+			if (!wir.IsCompleted)
+			{
+				int result = (int)wir.GetResult();
+				success = (1 == result);
+				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
+			}
+
+			smartThreadPool.Shutdown();
+
+			return success;
+		}
+
+		/// <summary>
+		/// Example of how to queue a work item and then cancel it while it is in the queue.
+		/// </summary>
+		private bool DoTestPostExecuteWithCancel(CallToPostExecute callToPostExecute, bool answer) 
+		{ 
+			// 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;
+			PostExecuteResult postExecuteResult = new PostExecuteResult();
+
+			// Queue a work item that will occupy the thread in the pool
+			smartThreadPool.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				null);
+
+			// Queue another work item that will wait for the first to complete
+			IWorkItemResult wir = 
+				smartThreadPool.QueueWorkItem(
+					new WorkItemCallback(this.DoSomeWork), 
+					postExecuteResult,
+					new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
+					callToPostExecute);
+
+
+			// Wait a while for the thread pool to start executing the first work item
+			Thread.Sleep(100);
+
+			// Cancel the second work item while it still in the queue
+			if (wir.Cancel())
+			{
+				success = (postExecuteResult.wh.WaitOne(1000, true) == answer);
+			}
+
+			smartThreadPool.Shutdown();
+
+			return success;
+		} 
+
+		private object DoSomeWork(object state)
+		{ 
+			Thread.Sleep(1000);
+			return 1;
+		}
+
+		private void DoSomePostExecuteWork(IWorkItemResult wir)
+		{ 
+			PostExecuteResult postExecuteResult = wir.State as PostExecuteResult;
+			postExecuteResult.wh.Set();
+		}
+
+		private object SignalCancel(object state)
+		{ 
+			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
+			Thread.Sleep(250);
+			cancelWaitHandle.Set();
+			return null;
+		}
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestPriorityQueue.cs b/SSharpSmartThreadPoolTests/TestPriorityQueue.cs
new file mode 100644
index 0000000..20d92db
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestPriorityQueue.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Linq;
+using OpenNETCF;
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+using Amib.SSharpThreading.Internal;
+using SmartThreadPoolTests;
+
+namespace PriorityQueueTests
+	{
+	/// <summary>
+	/// Summary description for TestPriorityQueue.
+	/// </summary>
+	[TestFixture]
+	[Category ("TestPriorityQueue")]
+	public class TestPriorityQueue
+		{
+		[Test]
+		public void Init ()
+			{
+			PriorityQueue pq = new PriorityQueue ();
+
+			Assert.AreEqual (0, pq.Count);
+
+			Assert.IsNull (pq.Dequeue ());
+
+			Assert.AreEqual (0, pq.Count);
+			}
+
+		[Test]
+		public void OneWorkItem ()
+			{
+			WorkItemPriority[] priorities = Enum2.GetValues (typeof (WorkItemPriority)).Cast<WorkItemPriority> ().ToArray ();
+			foreach (WorkItemPriority wip in priorities)
+				{
+				PriorityQueue pq = new PriorityQueue ();
+
+				PriorityItem pi = new PriorityItem (wip);
+
+				pq.Enqueue (pi);
+
+				Assert.AreEqual (1, pq.Count, "Failed for priority {0}", wip);
+
+				PriorityItem pi2 = pq.Dequeue () as PriorityItem;
+
+				Assert.IsNotNull (pi2, "Failed for priority {0}", wip);
+
+				Assert.AreSame (pi, pi2, "Failed for priority {0}", wip);
+
+				Assert.AreEqual (0, pq.Count, "Failed for priority {0}", wip);
+				}
+			}
+
+		[Test]
+		public void MultipleWorkItemsOnePriority ()
+			{
+			WorkItemPriority[] priorities = Enum2.GetValues (typeof (WorkItemPriority)).Cast<WorkItemPriority> ().ToArray ();
+			foreach (WorkItemPriority wip in priorities)
+				{
+				PriorityQueue pq = new PriorityQueue ();
+
+				PriorityItem[] priorityItems = new PriorityItem[10];
+
+				for (int i = 0; i < priorityItems.Length; ++i)
+					{
+					priorityItems[i] = new PriorityItem (wip);
+
+					pq.Enqueue (priorityItems[i]);
+
+					Assert.AreEqual (i + 1, pq.Count, "Failed for priority {0} item count {1}", wip, i + 1);
+					}
+
+				for (int i = 0; i < priorityItems.Length; ++i)
+					{
+					PriorityItem pi = pq.Dequeue () as PriorityItem;
+
+					Assert.AreEqual (priorityItems.Length - (i + 1), pq.Count, "Failed for priority {0} item count {1}", wip, i + 1);
+
+					Assert.IsNotNull (pi, "Failed for priority {0} item count {1}", wip, i + 1);
+
+					Assert.AreSame (pi, priorityItems[i], "Failed for priority {0} item count {1}", wip, i + 1);
+					}
+
+				Assert.AreEqual (0, pq.Count, "Failed for priority {0}", wip);
+
+				Assert.IsNull (pq.Dequeue ());
+
+				Assert.AreEqual (0, pq.Count);
+				}
+			}
+
+		[Test]
+		public void MultipleWorkItemsMultiplePriorities ()
+			{
+			// Get all the available priorities
+			WorkItemPriority[] priorities = Enum2.GetValues (typeof (WorkItemPriority)).Cast<WorkItemPriority> ().ToArray ();
+
+			// Create an array of priority items
+			PriorityItem[] priorityItems = new PriorityItem[priorities.Length];
+
+			// Create a priority item for each priority 
+			int i = priorities.Length;
+			foreach (WorkItemPriority workItemPriority in priorities)
+				{
+				--i;
+				priorityItems[i] = new PriorityItem (workItemPriority);
+				}
+
+			// Create a PermutationGenerator for the priority items
+			PermutationGenerator permutations = new PermutationGenerator (priorityItems);
+
+			int count = 0;
+			// Iterate over the permutations
+			foreach (object[] permutation in permutations)
+				{
+				++count;
+				//Console.Write ("Permutation #" + count + " : ");
+				for (int j = 0; j < permutation.Length; ++j)
+					{
+					PriorityItem pi = permutation[j] as PriorityItem;
+					//Console.Write (pi.WorkItemPriority + ", ");
+					}
+				//Console.WriteLine ();
+				// Create a priority queue
+				PriorityQueue pq = new PriorityQueue ();
+
+				// Enqueue each priority item according to the permutation
+				for (i = 0; i < permutation.Length; ++i)
+					{
+					PriorityItem priorityItem = permutation[i] as PriorityItem;
+					pq.Enqueue (priorityItem);
+					}
+
+				// Make sure all the priority items are in the queue
+				Assert.AreEqual (priorityItems.Length, pq.Count);
+
+				// Compare the order of the priority items
+				for (i = 0; i < priorityItems.Length; ++i)
+					{
+					PriorityItem priorityItem = pq.Dequeue () as PriorityItem;
+					Assert.AreSame (priorityItems[i], priorityItem);
+					}
+				}
+			}
+
+		private class PriorityItem : IHasWorkItemPriority
+			{
+			public PriorityItem (WorkItemPriority workItemPriority)
+				{
+				WorkItemPriority = workItemPriority;
+				}
+
+			public WorkItemPriority WorkItemPriority { get; private set; }
+			}
+
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestQueueWorkItem.cs b/SSharpSmartThreadPoolTests/TestQueueWorkItem.cs
new file mode 100644
index 0000000..10e0473
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestQueueWorkItem.cs
@@ -0,0 +1,98 @@
+
+using NUnit.Framework;
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+    /// Tests for QueueWorkItem.
+	/// </summary>
+	[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/SSharpSmartThreadPoolTests/TestStartSuspended.cs b/SSharpSmartThreadPoolTests/TestStartSuspended.cs
new file mode 100644
index 0000000..e92b19c
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestStartSuspended.cs
@@ -0,0 +1,115 @@
+using Crestron.SimplSharpPro.CrestronThread;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+	/// Summary description for TestStartSuspended.
+	/// </summary>
+	[TestFixture]
+	[Category("TestStartSuspended")]
+	public class TestStartSuspended
+	{
+	    [Test]
+		public void StartSuspended()
+		{
+			STPStartInfo stpStartInfo = new STPStartInfo();
+			stpStartInfo.StartSuspended = true;
+
+			SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+
+			stp.QueueWorkItem(new WorkItemCallback(this.DoWork));
+
+			Assert.IsFalse(stp.WaitForIdle(200));
+
+			stp.Start();
+
+			Assert.IsTrue(stp.WaitForIdle(200));
+		}
+
+		[Test]
+		public void WIGStartSuspended()
+		{
+			SmartThreadPool stp = new SmartThreadPool();
+
+			WIGStartInfo wigStartInfo = new WIGStartInfo();
+			wigStartInfo.StartSuspended = true;
+
+			IWorkItemsGroup wig = stp.CreateWorkItemsGroup(10, wigStartInfo);
+
+			wig.QueueWorkItem(new WorkItemCallback(this.DoWork));
+
+			Assert.IsFalse(wig.WaitForIdle(200));
+
+			wig.Start();
+
+			Assert.IsTrue(wig.WaitForIdle(200));
+		}
+
+		[Test]
+		public void STPAndWIGStartSuspended()
+		{
+			STPStartInfo stpStartInfo = new STPStartInfo();
+			stpStartInfo.StartSuspended = true;
+
+			SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+
+			WIGStartInfo wigStartInfo = new WIGStartInfo();
+			wigStartInfo.StartSuspended = true;
+
+			IWorkItemsGroup wig = stp.CreateWorkItemsGroup(10, wigStartInfo);
+
+			wig.QueueWorkItem(new WorkItemCallback(this.DoWork));
+
+			Assert.IsFalse(wig.WaitForIdle(200));
+
+			wig.Start();
+
+			Assert.IsFalse(wig.WaitForIdle(200));
+
+			stp.Start();
+
+			Assert.IsTrue(wig.WaitForIdle(5000), "WIG is not idle");
+            Assert.IsTrue(stp.WaitForIdle(5000), "STP is not idle");
+		}
+
+		[Test]
+		public void TwoWIGsStartSuspended()
+		{
+			SmartThreadPool stp = new SmartThreadPool();
+
+			WIGStartInfo wigStartInfo = new WIGStartInfo();
+			wigStartInfo.StartSuspended = true;
+
+			IWorkItemsGroup wig1 = stp.CreateWorkItemsGroup(10, wigStartInfo);
+			IWorkItemsGroup wig2 = stp.CreateWorkItemsGroup(10, wigStartInfo);
+
+			wig1.QueueWorkItem(new WorkItemCallback(this.DoWork));
+			wig2.QueueWorkItem(new WorkItemCallback(this.DoWork));
+
+			Assert.IsFalse(wig1.WaitForIdle(200));
+			Assert.IsFalse(wig2.WaitForIdle(200));
+
+			wig1.Start();
+
+			Assert.IsTrue(wig1.WaitForIdle(200));
+			Assert.IsFalse(wig2.WaitForIdle(200));
+
+			wig2.Start();
+
+			Assert.IsTrue(wig1.WaitForIdle(0));
+			Assert.IsTrue(wig2.WaitForIdle(200));
+		}
+
+
+		private object DoWork(object state)
+		{
+			Thread.Sleep(100);
+			return null;
+		}
+
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestStateDispose.cs b/SSharpSmartThreadPoolTests/TestStateDispose.cs
new file mode 100644
index 0000000..3f9a8a1
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestStateDispose.cs
@@ -0,0 +1,133 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+    public class CallerState
+    {
+        public int Value { get; private set; }
+
+        protected void IncValue()
+        {
+            ++Value;
+        }
+    }
+
+    public class NonDisposableCallerState : CallerState
+    {
+        public NonDisposableCallerState()
+        {
+            IncValue();
+        }
+    }
+
+    public class DisposableCallerState : CallerState, IDisposable
+    {
+        public DisposableCallerState()
+        {
+            IncValue();
+        }
+
+        #region IDisposable Members
+
+        public void Dispose()
+        {
+            IncValue();
+        }
+
+        #endregion
+    }
+
+
+	/// <summary>
+	/// Summary description for StateDisposeExample.
+	/// </summary>
+	[TestFixture]
+	[Category("TestStateDispose")]
+	public class TestStateDispose
+	{
+	    /// <summary>
+        /// Example of non disposable caller state
+        /// </summary>
+        [Test]
+        public void DisposeCallerState() 
+        { 
+            STPStartInfo stpStartInfo = new STPStartInfo();
+            stpStartInfo.DisposeOfStateObjects = true;
+     
+            SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo);
+
+            CallerState nonDisposableCallerState = new NonDisposableCallerState();
+            CallerState disposableCallerState = new DisposableCallerState();
+
+            IWorkItemResult wir1 = 
+                smartThreadPool.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                nonDisposableCallerState);
+
+            IWorkItemResult wir2 = 
+                smartThreadPool.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                disposableCallerState);
+
+            wir1.GetResult();
+			Assert.AreEqual(1, nonDisposableCallerState.Value);
+
+            wir2.GetResult();
+
+			// Wait a little bit for the working thread to call dispose on the 
+			// work item's state.
+			smartThreadPool.WaitForIdle();
+
+			Assert.AreEqual(2, disposableCallerState.Value);
+
+            smartThreadPool.Shutdown();
+        } 
+
+        /// <summary>
+        /// Example of non disposable caller state
+        /// </summary>
+        [Test]
+        public void DontDisposeCallerState() 
+        { 
+            STPStartInfo stpStartInfo = new STPStartInfo();
+            stpStartInfo.DisposeOfStateObjects = false;
+     
+            SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo);
+
+            CallerState nonDisposableCallerState = new NonDisposableCallerState();
+            CallerState disposableCallerState = new DisposableCallerState();
+
+            IWorkItemResult wir1 = 
+                smartThreadPool.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                nonDisposableCallerState);
+
+            IWorkItemResult wir2 = 
+                smartThreadPool.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                disposableCallerState);
+
+            wir1.GetResult();
+            bool success = (1 == nonDisposableCallerState.Value);
+
+            wir2.GetResult();
+
+            success = success && (1 == disposableCallerState.Value);
+
+            smartThreadPool.Shutdown();
+
+            Assert.IsTrue(success);
+        } 
+
+        private object DoSomeWork(object state)
+        { 
+            Thread.Sleep(1000);
+            return 1;
+        }
+    }
+}
diff --git a/SSharpSmartThreadPoolTests/TestThreadApartmentState.cs b/SSharpSmartThreadPoolTests/TestThreadApartmentState.cs
new file mode 100644
index 0000000..9e43a45
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestThreadApartmentState.cs
@@ -0,0 +1,47 @@
+using System.Threading;
+using NUnit.Framework;
+using Amib.Threading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+    /// Summary description for TestThreadApartment.
+	/// </summary>
+	[TestFixture]
+    [Category("TestThreadApartment")]
+	public class TestThreadApartmentState
+	{
+		[Test]
+		public void TestSTA()
+		{
+            CheckApartmentState(ApartmentState.STA);
+		}
+
+		[Test]
+		public void TestMTA()
+		{
+            CheckApartmentState(ApartmentState.MTA);
+		}
+
+	    private static void CheckApartmentState(ApartmentState requestApartmentState)
+	    {
+	        STPStartInfo stpStartInfo = new STPStartInfo();
+            stpStartInfo.ApartmentState = requestApartmentState;
+
+	        SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+
+	        IWorkItemResult<ApartmentState> wir = stp.QueueWorkItem(() => GetCurrentThreadApartmentState());
+
+	        ApartmentState resultApartmentState = wir.GetResult();
+
+	        stp.WaitForIdle();
+
+	        Assert.AreEqual(requestApartmentState, resultApartmentState);
+	    }
+
+	    private static ApartmentState GetCurrentThreadApartmentState()
+	    {
+	        return Thread.CurrentThread.GetApartmentState();
+	    }
+	}
+}
\ No newline at end of file
diff --git a/SSharpSmartThreadPoolTests/TestThreadIsBackground.cs b/SSharpSmartThreadPoolTests/TestThreadIsBackground.cs
new file mode 100644
index 0000000..5539880
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestThreadIsBackground.cs
@@ -0,0 +1,47 @@
+using Crestron.SimplSharpPro.CrestronThread;
+using NUnit.Framework;
+using Amib.Threading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+    /// Summary description for TestThreadIsBackground.
+	/// </summary>
+	[TestFixture]
+    [Category("TestThreadIsBackground")]
+	public class TestThreadIsBackground
+	{
+		[Test]
+        public void TestIsBackground()
+		{
+            CheckIsBackground(true);
+		}
+
+		[Test]
+        public void TestNotIsBackground()
+		{
+            CheckIsBackground(false);
+		}
+
+        private static void CheckIsBackground(bool isBackground)
+	    {
+	        STPStartInfo stpStartInfo = new STPStartInfo();
+	        stpStartInfo.AreThreadsBackground = isBackground;
+
+	        SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
+
+            IWorkItemResult<bool> wir = stp.QueueWorkItem(() => GetCurrentThreadIsBackground());
+
+	        bool resultIsBackground = wir.GetResult();
+
+	        stp.WaitForIdle();
+
+            Assert.AreEqual(isBackground, resultIsBackground);
+	    }
+
+	    private static bool GetCurrentThreadIsBackground()
+	    {
+	        return Thread.CurrentThread.IsBackground;
+	    }
+	}
+}
\ No newline at end of file
diff --git a/SSharpSmartThreadPoolTests/TestThreadPriority.cs b/SSharpSmartThreadPoolTests/TestThreadPriority.cs
new file mode 100644
index 0000000..6b5112a
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestThreadPriority.cs
@@ -0,0 +1,78 @@
+
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;
+using WaitHandle = Crestron.SimplSharp.CEventHandle;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+	{
+	/// <summary>
+	/// Summary description for TestThreadPriority.
+	/// </summary>
+	[TestFixture]
+	[Category ("TestThreadPriority")]
+	public class TestThreadPriority
+		{
+		[Test]
+		public void TestDefaultPriority ()
+			{
+			CheckSinglePriority (SmartThreadPool.DefaultThreadPriority);
+			}
+
+		[Test]
+		public void TestLowestPriority ()
+			{
+			CheckSinglePriority (ThreadPriority.LowestPriority);
+			}
+
+#if !SSHARP
+        [Test]
+        public void TestBelowNormalPriority()
+		{
+            CheckSinglePriority(ThreadPriority.BelowNormal);
+		}
+#endif
+		[Test]
+		public void TestNormalPriority ()
+			{
+			CheckSinglePriority (ThreadPriority.MediumPriority);
+			}
+
+#if !SSHARP
+		[Test]
+		public void TestAboveNormalPriority ()
+			{
+			CheckSinglePriority (ThreadPriority.AboveNormal);
+			}
+#endif
+
+		[Test]
+		public void TestHighestPriority ()
+			{
+			CheckSinglePriority (ThreadPriority.HighPriority);
+			}
+
+		private void CheckSinglePriority (ThreadPriority threadPriority)
+			{
+			STPStartInfo stpStartInfo = new STPStartInfo ();
+			stpStartInfo.ThreadPriority = threadPriority;
+
+			SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
+
+			IWorkItemResult wir = stp.QueueWorkItem (new WorkItemCallback (GetThreadPriority));
+			ThreadPriority currentThreadPriority = (ThreadPriority)wir.GetResult ();
+
+			Assert.AreEqual (threadPriority, currentThreadPriority);
+			}
+
+		private object GetThreadPriority (object state)
+			{
+			return Thread.CurrentThread.Priority;
+			}
+		}
+	}
\ No newline at end of file
diff --git a/SSharpSmartThreadPoolTests/TestThreadsCreate.cs b/SSharpSmartThreadPoolTests/TestThreadsCreate.cs
new file mode 100644
index 0000000..e275304
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestThreadsCreate.cs
@@ -0,0 +1,143 @@
+using System;
+using Crestron.SimplSharp;
+using Crestron.SimplSharpPro.CrestronThread;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+	{
+	/// <summary>
+	/// </summary>
+	[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 += OnInitialization;
+			stp.OnThreadTermination += OnTermination;
+
+			stp.QueueWorkItem (new WorkItemCallback (DoSomeWork), null);
+
+			stp.WaitForIdle ();
+			stp.Shutdown ();
+
+			Assert.IsTrue (_initSuccess, "initSuccess");
+			Assert.IsTrue (_workItemSuccess, "workItemSuccess");
+			Assert.IsTrue (_termSuccess, "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);
+			}
+
+
+		// Can't run this test, StackOverflowException crashes the application and can't be catched and ignored
+		//[Test]
+		public void NotTestThreadsMaxStackSize ()
+			{
+			STPStartInfo stpStartInfo = new STPStartInfo ()
+			{
+#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)
+				MaxStackSize = 64 * 1024,
+#endif
+			};
+
+			SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
+			stp.Start ();
+
+			IWorkItemResult<bool> wir = stp.QueueWorkItem (() => AllocateBufferOnStack (10 * 1024));
+
+			bool result = wir.GetResult ();
+			Assert.IsTrue (result);
+
+			wir = stp.QueueWorkItem (() => AllocateBufferOnStack (1000 * 1024));
+
+			result = wir.GetResult ();
+			Assert.IsFalse (result);
+			}
+
+		private static unsafe bool AllocateBufferOnStack (int size)
+			{
+			try
+				{
+				byte* p = stackalloc byte[size];
+				}
+			catch (StackOverflowException ex)
+				{
+				return false;
+				}
+			catch (Exception ex)
+				{
+				return false;
+				}
+
+			return true;
+			}
+		}
+
+	internal class ThreadContextState
+		{
+		private static LocalDataStoreSlot _localData;
+
+		static ThreadContextState ()
+			{
+			_localData = Thread.AllocateDataSlot ();
+			}
+
+		public int Counter { get; set; }
+
+		// Static member so it can be used anywhere in code of the work item method
+		public static ThreadContextState Current
+			{
+			get
+				{
+				ThreadContextState _threadContextState = (ThreadContextState)Thread.GetData (_localData);
+				// If the _threadContextState is null then it was not yet initialized
+				// for this thread.
+				if (null == _threadContextState)
+					{
+					// Create a ThreadContextState object
+					_threadContextState = new ThreadContextState ();
+					Thread.SetData (_localData, _threadContextState);
+					}
+				return _threadContextState;
+				}
+			}
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestWIGActionT.cs b/SSharpSmartThreadPoolTests/TestWIGActionT.cs
new file mode 100644
index 0000000..f8b75e3
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGActionT.cs
@@ -0,0 +1,126 @@
+using Amib.SSharpThreading;
+using NUnit.Framework;
+
+namespace WorkItemsGroupTests
+{
+    /// <summary>
+    /// Summary description for TestCancel.
+    /// </summary>
+    [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/SSharpSmartThreadPoolTests/TestWIGChainedDelegates.cs b/SSharpSmartThreadPoolTests/TestWIGChainedDelegates.cs
new file mode 100644
index 0000000..ef7e671
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGChainedDelegates.cs
@@ -0,0 +1,94 @@
+using System;
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+	{
+	/// <summary>
+	/// Summary description for TestChainedDelegates.
+	/// </summary>
+	[TestFixture]
+	[Category ("Test WorkItemsGroup ChainedDelegates")]
+	public class TestChainedDelegates
+		{
+		[Test]
+		public void GoodCallback ()
+			{
+			SmartThreadPool smartThreadPool = new SmartThreadPool ();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);
+
+			workItemsGroup.QueueWorkItem (new WorkItemCallback (DoWork));
+
+			workItemsGroup.WaitForIdle ();
+
+			smartThreadPool.Shutdown ();
+			}
+
+		[Test]
+		public void ChainedDelegatesCallback ()
+			{
+			Assert.Throws<NotSupportedException> (() =>
+				{
+					SmartThreadPool smartThreadPool = new SmartThreadPool ();
+					IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);
+
+					WorkItemCallback workItemCallback = new WorkItemCallback (DoWork);
+					workItemCallback += new WorkItemCallback (DoWork);
+
+					workItemsGroup.QueueWorkItem (workItemCallback);
+
+					workItemsGroup.WaitForIdle ();
+
+					smartThreadPool.Shutdown ();
+				});
+			}
+
+		[Test]
+		public void GoodPostExecute ()
+			{
+			SmartThreadPool smartThreadPool = new SmartThreadPool ();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);
+
+			workItemsGroup.QueueWorkItem (
+				new WorkItemCallback (DoWork),
+				null,
+				new PostExecuteWorkItemCallback (DoPostExecute));
+
+			workItemsGroup.WaitForIdle ();
+
+			smartThreadPool.Shutdown ();
+			}
+
+		[Test]
+		public void ChainedDelegatesPostExecute ()
+			{
+			Assert.Throws<NotSupportedException> (() =>
+				{
+					SmartThreadPool smartThreadPool = new SmartThreadPool ();
+					IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);
+
+					PostExecuteWorkItemCallback postExecuteWorkItemCallback = new PostExecuteWorkItemCallback (DoPostExecute);
+					postExecuteWorkItemCallback += new PostExecuteWorkItemCallback (DoPostExecute);
+
+					workItemsGroup.QueueWorkItem (new WorkItemCallback (DoWork), null, postExecuteWorkItemCallback);
+
+					workItemsGroup.WaitForIdle ();
+
+					smartThreadPool.Shutdown ();
+				});
+			}
+
+
+		private object DoWork (object state)
+			{
+			return null;
+			}
+
+		private void DoPostExecute (IWorkItemResult wir)
+			{
+			}
+
+
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestWIGConcurrency.cs b/SSharpSmartThreadPoolTests/TestWIGConcurrency.cs
new file mode 100644
index 0000000..cdfd9ad
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGConcurrency.cs
@@ -0,0 +1,97 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using Interlocked = Crestron.SimplSharp.Interlocked;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+	{
+	/// <summary>
+	/// Summary description for TestWIGConcurrency.
+	/// </summary>
+	[TestFixture]
+	[Category ("TestWIGConcurrency")]
+	public class TestWIGConcurrency
+		{
+		private Random _randGen;
+		private int[] _concurrentOps;
+		private int _concurrencyPerWig;
+		private bool _success;
+
+		[Test]
+		public void TestConcurrencies ()
+			{
+			Concurrency (1, 1, 10);
+			Concurrency (1, 1, 100);
+
+			Concurrency (1, 5, 10);
+			Concurrency (1, 5, 100);
+
+			Concurrency (5, 5, 10);
+			Concurrency (5, 5, 100);
+			}
+
+		private void Concurrency (
+			int concurrencyPerWig,
+			int wigsCount,
+			int workItemsCount)
+			{
+			/*
+			Console.WriteLine (
+				"Testing : concurrencyPerWig = {0}, wigsCount = {1}, workItemsCount = {2}",
+				concurrencyPerWig,
+				wigsCount,
+				workItemsCount);
+			*/
+			_success = true;
+			_concurrencyPerWig = concurrencyPerWig;
+			_randGen = new Random (0);
+
+			STPStartInfo stpStartInfo = new STPStartInfo ();
+			stpStartInfo.StartSuspended = true;
+
+			SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
+
+			_concurrentOps = new int[wigsCount];
+
+			IWorkItemsGroup[] wigs = new IWorkItemsGroup[wigsCount];
+
+			for (int i = 0; i < wigs.Length; ++i)
+				{
+				wigs[i] = stp.CreateWorkItemsGroup (_concurrencyPerWig);
+				for (int j = 0; j < workItemsCount; ++j)
+					{
+					wigs[i].QueueWorkItem (new WorkItemCallback (this.DoWork), i);
+					}
+
+				wigs[i].Start ();
+				}
+
+			stp.Start ();
+
+			stp.WaitForIdle ();
+
+			Assert.IsTrue (_success);
+
+			stp.Shutdown ();
+			}
+
+		private object DoWork (object state)
+			{
+			int wigsIndex = (int)state;
+
+			int val = Interlocked.Increment (ref _concurrentOps[wigsIndex]);
+			_success = _success && (val <= _concurrencyPerWig);
+
+			int waitTime = _randGen.Next (50);
+			Thread.Sleep (waitTime);
+
+			val = Interlocked.Decrement (ref _concurrentOps[wigsIndex]);
+			_success = _success && (val >= 0);
+
+			return null;
+			}
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestWIGConcurrencyChanges.cs b/SSharpSmartThreadPoolTests/TestWIGConcurrencyChanges.cs
new file mode 100644
index 0000000..019064b
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGConcurrencyChanges.cs
@@ -0,0 +1,208 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+    /// Summary description for TestWIGConcurrencyChanges.
+	/// </summary>
+	[TestFixture]
+	[Category("TestWIGConcurrencyChanges")]
+	public class TestWIGConcurrencyChanges
+	{
+	    /// <summary>
+        /// Example of waiting for idle
+        /// </summary>
+        [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();
+        }
+
+        /// <summary>
+        /// Example of waiting for idle
+        /// </summary>
+        [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;
+        }
+
+
+
+
+/*
+        /// <summary>
+        /// Example of waiting for idle
+        /// </summary>
+        [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/SSharpSmartThreadPoolTests/TestWIGExceptions.cs b/SSharpSmartThreadPoolTests/TestWIGExceptions.cs
new file mode 100644
index 0000000..52a998f
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGExceptions.cs
@@ -0,0 +1,89 @@
+using System;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+	/// Summary description for TestExceptions.
+	/// </summary>
+	[TestFixture]
+	[Category("Test WorkItemsGroup Exceptions")]
+	public class TestExceptions
+	{
+		private class DivArgs
+		{
+			public int x;
+			public int y;
+		}
+
+		[Test]
+		public void ExceptionThrowing() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			DivArgs divArgs = new DivArgs();
+			divArgs.x = 10;
+			divArgs.y = 0;
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);
+
+			try
+			{
+				wir.GetResult();
+			}
+			catch(WorkItemResultException wire)
+			{
+				Assert.IsTrue(wire.InnerException is DivideByZeroException);
+				return;
+			}
+			catch(Exception e)
+			{
+                e.GetHashCode();
+				Assert.Fail();
+			}
+			Assert.Fail();
+		} 
+
+		[Test]
+		public void ExceptionReturning() 
+		{ 
+			bool success = true;
+
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			DivArgs divArgs = new DivArgs();
+			divArgs.x = 10;
+			divArgs.y = 0;
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);
+
+			Exception e = null;
+			try
+			{
+				wir.GetResult(out e);
+			}
+			catch (Exception ex)
+			{
+                ex.GetHashCode();
+				success = false;
+			}
+
+			Assert.IsTrue(success);
+			Assert.IsTrue(e is DivideByZeroException);
+		} 
+
+		private object DoDiv(object state)
+		{ 
+			DivArgs divArgs = (DivArgs)state;
+			return (divArgs.x / divArgs.y);
+		}
+
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestWIGFillStateWithParams.cs b/SSharpSmartThreadPoolTests/TestWIGFillStateWithParams.cs
new file mode 100644
index 0000000..d55a77c
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGFillStateWithParams.cs
@@ -0,0 +1,210 @@
+using System;
+using Crestron.SimplSharp;
+using Amib.SSharpThreading;
+using NUnit.Framework;
+
+namespace STPTests
+{
+    /// <summary>
+    /// Summary description for TestWIGFillStateWithArgs.
+    /// </summary>
+    [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<int> wir = _wig.QueueWorkItem(new Func<int>(Func0));
+            Assert.AreEqual(wir.State, null);
+        }
+
+        [Test]
+        public void FuncT1()
+        {
+            IWorkItemResult<bool> wir = _wig.QueueWorkItem(new Func<int, bool>(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<string> wir = _wig.QueueWorkItem(new Func<char, string, string>(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<char> wir = _wig.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _wig.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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/SSharpSmartThreadPoolTests/TestWIGFillStateWithParams1.cs b/SSharpSmartThreadPoolTests/TestWIGFillStateWithParams1.cs
new file mode 100644
index 0000000..a867b4b
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGFillStateWithParams1.cs
@@ -0,0 +1,207 @@
+using System;
+using Amib.Threading;
+using NUnit.Framework;
+using System.Net;
+
+namespace STPTests
+	{
+	/// <summary>
+	/// Summary description for TestFillStateWithParams.
+	/// </summary>
+	[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<int> wir = _stp.QueueWorkItem (new Func<int> (Func0));
+			Assert.AreEqual (wir.State, null);
+			}
+
+		[Test]
+		public void FuncT1 ()
+			{
+			IWorkItemResult<bool> wir = _stp.QueueWorkItem (new Func<int, bool> (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<string> wir = _stp.QueueWorkItem (new Func<char, string, string> (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<char> wir = _stp.QueueWorkItem (new Func<bool, char[], object, char> (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<IPAddress> wir = _stp.QueueWorkItem (new Func<long, IntPtr, IPAddress, Guid, IPAddress> (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/SSharpSmartThreadPoolTests/TestWIGFuncT.cs b/SSharpSmartThreadPoolTests/TestWIGFuncT.cs
new file mode 100644
index 0000000..d69b341
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGFuncT.cs
@@ -0,0 +1,108 @@
+using System;
+using Amib.SSharpThreading;
+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<int> wir = _wig.QueueWorkItem(new Func<int>(MaxInt));
+
+            int result = wir.GetResult();
+
+            Assert.AreEqual(result, int.MaxValue);
+        }
+
+        [Test]
+        public void FuncT1()
+        {
+            IWorkItemResult<bool> wir = _wig.QueueWorkItem(new Func<bool, bool>(Not), true);
+
+            bool result = wir.Result;
+
+            Assert.AreEqual(result, false);
+        }
+
+        [Test]
+        public void FuncT2()
+        {
+            IWorkItemResult<string> wir = _wig.QueueWorkItem(new Func<string, string, string>(string.Concat), "ABC", "xyz");
+
+            string result = wir.Result;
+
+            Assert.AreEqual(result, "ABCxyz");
+        }
+
+        [Test]
+        public void FuncT3()
+        {
+            IWorkItemResult<string> wir = _wig.QueueWorkItem(new Func<string, int, int, string>(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<int[]> wir = _wig.QueueWorkItem(new Func<int[], int, int, int, int[]>(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/SSharpSmartThreadPoolTests/TestWIGGetResult.cs b/SSharpSmartThreadPoolTests/TestWIGGetResult.cs
new file mode 100644
index 0000000..652cc51
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGGetResult.cs
@@ -0,0 +1,120 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+	/// Summary description for GetResultExample.
+	/// </summary>
+	[TestFixture]
+	[Category("Test WorkItemsGroup GetResult")]
+	public class TestGetResult
+	{
+		/// <summary>
+		/// Example of how to queue a work item and then wait infinitely for the result.
+		/// </summary>
+		[Test]
+		public void BlockingCall() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			bool success = false;
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+
+			if (!wir.IsCompleted)
+			{
+				int result = (int)wir.GetResult();
+				success = (1 == result);
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue a work item and then wait on a timeout for the result.
+		/// </summary>
+		[Test]
+		public void Timeout() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			bool success = false;
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+
+			try
+			{
+				wir.GetResult(500, true);
+			}
+			catch (WorkItemTimeoutException)
+			{
+				success = true;
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to interrupt the waiting for a work item to complete.
+		/// </summary>
+		[Test]
+		public void WorkItemWaitCanceling() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			ManualResetEvent cancelWaitHandle = new ManualResetEvent(false);
+
+			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.SignalCancel), cancelWaitHandle);
+
+			try
+			{
+				wir1.GetResult(Crestron.SimplSharp.Timeout.Infinite, true, cancelWaitHandle);
+			}
+			catch (WorkItemTimeoutException) 
+			{
+				success = true;
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		private object DoSomeWork(object state)
+		{ 
+			Thread.Sleep(1000);
+			return 1;
+		}
+
+		private object SignalCancel(object state)
+		{ 
+			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
+			Thread.Sleep(250);
+			cancelWaitHandle.Set();
+			return null;
+		}
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestWIGMultipleWorkItems.cs b/SSharpSmartThreadPoolTests/TestWIGMultipleWorkItems.cs
new file mode 100644
index 0000000..3cc046a
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGMultipleWorkItems.cs
@@ -0,0 +1,221 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+	/// Summary description for MultipleWorkItemsExample.
+	/// </summary>
+	[TestFixture]
+	[Category("Test WorkItemsGroup MultipleWorkItems")]
+	public class TestMultipleWorkItems
+	{
+		/// <summary>
+		/// Example of how to queue several work items and then wait infinitely for 
+		/// all of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAll() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			bool success = true;
+
+			IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			SmartThreadPool.WaitAll(wirs);
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				if (!wirs[i].IsCompleted)
+				{
+					success = false;
+					break;
+				}
+				else
+				{
+					int result = (int)wirs[i].GetResult();
+					if (1 != result)
+					{
+						success = false;
+						break;
+					}
+				}
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait infinitely for 
+		/// one of them to complete.
+		/// 
+		/// You can use this technique if you have several work items that return the same 
+		/// infomration, but use different method to aquire it. Just execute all of them at
+		/// once and wait for the first work item to complete.
+		/// 
+		/// For example: You need an information about a person and you can query several 
+		/// information sites (FBI, CIA, etc.). Query all of them at once and use the first
+		/// answer to arrive.
+		/// </summary>
+		[Test]
+		public void WaitAny() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			bool success = false;
+
+			IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			int index = SmartThreadPool.WaitAny(wirs);
+
+			if (wirs[index].IsCompleted)
+			{
+				int result = (int)wirs[index].GetResult();
+				if (1 == result)
+				{
+					success = true;
+				}
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for all
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAllWithTimeoutSuccess()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+		    IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			bool timeout = !SmartThreadPool.WaitAll(wirs, 1500, true);
+			bool success = !timeout;
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for all
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAllWithTimeoutFailure()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+		    IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			bool timeout = !SmartThreadPool.WaitAll(wirs, 10, true);
+			bool success = timeout;
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for any
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAnyWithTimeoutSuccess()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			bool success;
+
+			IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			int index = SmartThreadPool.WaitAny(wirs, 1500, true);
+
+			success = (index != WaitHandle.WaitTimeout);
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		}
+
+		/// <summary>
+		/// Example of how to queue several work items and then wait on a timeout for any
+		/// of them to complete.
+		/// </summary>
+		[Test]
+		public void WaitAnyWithTimeoutFailure()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+		    IWorkItemResult [] wirs = new IWorkItemResult[5];
+
+			for(int i = 0; i < wirs.Length; ++i)
+			{
+				wirs[i] = 
+					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+			}
+
+			int index = SmartThreadPool.WaitAny(wirs, 10, true);
+
+			bool success = (index == WaitHandle.WaitTimeout);
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		}
+
+		private object DoSomeWork(object state)
+		{ 
+			Thread.Sleep(1000);
+			return 1;
+		}
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestWIGPostExecute.cs b/SSharpSmartThreadPoolTests/TestWIGPostExecute.cs
new file mode 100644
index 0000000..fdbe02b
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGPostExecute.cs
@@ -0,0 +1,232 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+	/// Summary description for DoTestPostExecute.
+	/// </summary>
+	[TestFixture]
+	[Category("WorkItemsGroup")]
+	public class TestPostExecution
+	{
+		/// <summary>
+		/// 
+		/// </summary>
+		[Test]
+		public void DefaultPostExecute_AlwaysCall()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Always, true));
+		}
+
+		[Test]
+		public void DefaultPostExecute_NeverCall()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Never, false));
+		}
+
+		[Test]
+		public void DefaultPostExecute_CallWhenCanceled()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
+		}
+
+		[Test]
+		public void DefaultPostExecute_CallWhenNotCanceled()
+		{
+			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
+		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		[Test]
+		public void PostExecute_AlwaysCall()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Always, true));
+		}
+
+		[Test]
+		public void PostExecute_NeverCall()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Never, false));
+		}
+
+		[Test]
+		public void PostExecute_CallWhenCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
+		}
+
+		[Test]
+		public void PostExecute_CallWhenNotCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
+		}
+
+		/// <summary>
+		/// 
+		/// </summary>
+		[Test]
+		public void PostExecuteWithCancel_AlwaysCall()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Always, true));
+		}
+
+		[Test]
+		public void PostExecuteWithCancel_NeverCall()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Never, false));
+		}
+
+		[Test]
+		public void PostExecuteWithCancel_CallWhenCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemCanceled, true));
+		}
+
+		[Test]
+		public void PostExecuteWithCancel_CallWhenNotCanceled()
+		{
+			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemNotCanceled, false));
+		}
+
+
+		private class PostExecuteResult
+		{
+			public ManualResetEvent wh = new ManualResetEvent(false);
+		}
+
+		/// <summary>
+		/// Example of how to use the post execute callback
+		/// </summary>
+		private bool DoTestDefaultPostExecute(CallToPostExecute callToPostExecute, bool answer)
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			
+			WIGStartInfo wigStartInfo = new WIGStartInfo();
+			wigStartInfo.CallToPostExecute = callToPostExecute;
+			wigStartInfo.PostExecuteWorkItemCallback = new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork);
+
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue, wigStartInfo);
+
+			bool success = false;
+
+			PostExecuteResult postExecuteResult = new PostExecuteResult();
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				postExecuteResult);
+
+			if (!wir.IsCompleted)
+			{
+				int result = (int)wir.GetResult();
+				success = (1 == result);
+				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
+			}
+
+			smartThreadPool.Shutdown();
+
+			return success;
+		}
+
+
+		/// <summary>
+		/// Example of how to use the post execute callback
+		/// </summary>
+		private bool DoTestPostExecute(CallToPostExecute callToPostExecute, bool answer)
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+			
+			bool success = false;
+
+			PostExecuteResult postExecuteResult = new PostExecuteResult();
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				postExecuteResult,
+				new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
+				callToPostExecute);
+
+			if (!wir.IsCompleted)
+			{
+				int result = (int)wir.GetResult();
+				success = (1 == result);
+				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
+			}
+
+			smartThreadPool.Shutdown();
+
+			return success;
+		}
+
+		/// <summary>
+		/// Example of how to queue a work item and then cancel it while it is in the queue.
+		/// </summary>
+		private bool DoTestPostExecuteWithCancel(CallToPostExecute callToPostExecute, bool answer) 
+		{ 
+			// Create a SmartThreadPool with only one thread.
+			// It just to show how to use the work item canceling feature
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(1);
+
+			bool success = false;
+			PostExecuteResult postExecuteResult = new PostExecuteResult();
+
+			// Queue a work item that will occupy the thread in the pool
+			workItemsGroup.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				null);
+
+			// Queue another work item that will wait for the first to complete
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(
+					new WorkItemCallback(this.DoSomeWork), 
+					postExecuteResult,
+					new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
+					callToPostExecute);
+
+
+			// Wait a while for the thread pool to start executing the first work item
+			Thread.Sleep(100);
+
+			// Cancel the second work item while it still in the queue
+			if (wir.Cancel())
+			{
+				success = (postExecuteResult.wh.WaitOne(1000, true) == answer);
+			}
+
+			smartThreadPool.Shutdown();
+
+			return success;
+		} 
+
+		private object DoSomeWork(object state)
+		{ 
+			Thread.Sleep(1000);
+			return 1;
+		}
+
+		private void DoSomePostExecuteWork(IWorkItemResult wir)
+		{ 
+			PostExecuteResult postExecuteResult = wir.State as PostExecuteResult;
+			postExecuteResult.wh.Set();
+		}
+
+		private object SignalCancel(object state)
+		{ 
+			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
+			Thread.Sleep(250);
+			cancelWaitHandle.Set();
+			return null;
+		}
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestWIGQueueWorkItem.cs b/SSharpSmartThreadPoolTests/TestWIGQueueWorkItem.cs
new file mode 100644
index 0000000..74ff8fe
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGQueueWorkItem.cs
@@ -0,0 +1,101 @@
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+using SmartThreadPoolTests;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+    /// Tests for QueueWorkItem.
+	/// </summary>
+	[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/SSharpSmartThreadPoolTests/TestWIGStateDispose.cs b/SSharpSmartThreadPoolTests/TestWIGStateDispose.cs
new file mode 100644
index 0000000..a046766
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGStateDispose.cs
@@ -0,0 +1,137 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+    public class CallerState
+    {
+        public int Value { get; private set; }
+
+        protected void IncValue()
+        {
+            ++Value;
+        }
+    }
+
+    public class NonDisposableCallerState : CallerState
+    {
+        public NonDisposableCallerState()
+        {
+            IncValue();
+        }
+    }
+
+    public class DisposableCallerState : CallerState, IDisposable
+    {
+        public DisposableCallerState()
+        {
+            IncValue();
+        }
+
+        #region IDisposable Members
+
+        public void Dispose()
+        {
+            IncValue();
+        }
+
+        #endregion
+    }
+
+
+	/// <summary>
+	/// Summary description for StateDisposeExample.
+	/// </summary>
+	[TestFixture]
+	[Category("WorkItemsGroup")]
+	public class TestStateDispose
+	{
+	    /// <summary>
+        /// Example of non disposable caller state
+        /// </summary>
+        [Test]
+        public void DisposeCallerState() 
+        { 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+			WIGStartInfo wigStartInfo = new WIGStartInfo();
+            wigStartInfo.DisposeOfStateObjects = true;
+
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue, wigStartInfo);
+
+            CallerState nonDisposableCallerState = new NonDisposableCallerState();
+            CallerState disposableCallerState = new DisposableCallerState();
+
+            IWorkItemResult wir1 = 
+                workItemsGroup.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                nonDisposableCallerState);
+
+            IWorkItemResult wir2 = 
+                workItemsGroup.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                disposableCallerState);
+
+            wir1.GetResult();
+			Assert.AreEqual(1, nonDisposableCallerState.Value);
+
+            wir2.GetResult();
+
+			// Wait a little bit for the working thread to call dispose on the 
+			// work item's state.
+			workItemsGroup.WaitForIdle();
+
+			Assert.AreEqual(2, disposableCallerState.Value);
+
+            smartThreadPool.Shutdown();
+        } 
+
+        /// <summary>
+        /// Example of non disposable caller state
+        /// </summary>
+        [Test]
+        public void DontDisposeCallerState() 
+        { 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+
+			WIGStartInfo wigStartInfo = new WIGStartInfo();
+			wigStartInfo.DisposeOfStateObjects = false;
+
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue, wigStartInfo);
+
+            CallerState nonDisposableCallerState = new NonDisposableCallerState();
+            CallerState disposableCallerState = new DisposableCallerState();
+
+            IWorkItemResult wir1 = 
+                workItemsGroup.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                nonDisposableCallerState);
+
+            IWorkItemResult wir2 = 
+                workItemsGroup.QueueWorkItem(
+                new WorkItemCallback(this.DoSomeWork), 
+                disposableCallerState);
+
+            wir1.GetResult();
+            bool success = (1 == nonDisposableCallerState.Value);
+
+            wir2.GetResult();
+
+            success = success && (1 == disposableCallerState.Value);
+
+            smartThreadPool.Shutdown();
+
+            Assert.IsTrue(success);
+        } 
+
+        private object DoSomeWork(object state)
+        { 
+            Thread.Sleep(1000);
+            return 1;
+        }
+    }
+}
diff --git a/SSharpSmartThreadPoolTests/TestWIGWaitForIdle.cs b/SSharpSmartThreadPoolTests/TestWIGWaitForIdle.cs
new file mode 100644
index 0000000..af2d44b
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWIGWaitForIdle.cs
@@ -0,0 +1,176 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+using NUnit.Framework;
+using Interlocked = Crestron.SimplSharp.Interlocked;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+	/// Summary description for TestWaitForIdle.
+	/// </summary>
+	[TestFixture]
+	[Category("WorkItemsGroup")]
+	public class TestWaitForIdle
+	{
+	    /// <summary>
+        /// Example of waiting for idle
+        /// </summary>
+        [Test]
+        public void WaitForIdle() 
+        { 
+            SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+            ManualResetEvent isRunning = new ManualResetEvent(false);
+
+            for (int i = 0; i < 4; ++i)
+            {
+                workItemsGroup.QueueWorkItem(delegate { isRunning.WaitOne(); });
+            }
+
+            bool success = !workItemsGroup.WaitForIdle(1000);
+
+            isRunning.Set();
+
+            success = success && workItemsGroup.WaitForIdle(1000);
+
+            smartThreadPool.Shutdown();
+
+            Assert.IsTrue(success);
+        } 
+
+		[Test]
+		public void WaitForIdleOnSTPThread() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			IWorkItemResult wir = workItemsGroup.QueueWorkItem(
+				new WorkItemCallback(this.DoWaitForIdle), 
+				workItemsGroup);
+
+			Exception e;
+			wir.GetResult(out e);
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsNotNull(e);
+		}
+
+		[Test]
+		public void WaitForIdleOnSTPThreadForAnotherWorkItemsGroup()
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
+			IWorkItemsGroup workItemsGroup1 = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+			IWorkItemsGroup workItemsGroup2 = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			workItemsGroup1.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				1000);
+
+			workItemsGroup1.QueueWorkItem(
+				new WorkItemCallback(this.DoSomeWork), 
+				1000);
+
+			IWorkItemResult wir = workItemsGroup2.QueueWorkItem(
+				new WorkItemCallback(this.DoWaitForIdle), 
+				workItemsGroup1);
+
+			Exception e;
+			wir.GetResult(out e);
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsNull(e);
+		} 
+
+
+        private int _x = 0;
+        private object DoSomeWork(object state)
+        {
+            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;
+        }
+
+		private object DoWaitForIdle(object state)
+		{ 
+			IWorkItemsGroup workItemsGroup = state as IWorkItemsGroup;
+			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();
+        }
+
+        [Test]
+        public void WaitForIdleEvent()
+        {
+            SmartThreadPool smartThreadPool = new SmartThreadPool();
+            IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(1);
+            ManualResetEvent wigIsIdle = new ManualResetEvent(false);
+
+            workItemsGroup.OnIdle += wig => wigIsIdle.Set();
+
+            workItemsGroup.QueueWorkItem(() => { });
+
+            bool eventFired = wigIsIdle.WaitOne(100, true);
+
+            smartThreadPool.Shutdown();
+
+            Assert.IsTrue(eventFired);
+        }
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestWaitForIdle.cs b/SSharpSmartThreadPoolTests/TestWaitForIdle.cs
new file mode 100644
index 0000000..7ab5bad
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWaitForIdle.cs
@@ -0,0 +1,79 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSMono.Diagnostics;
+using SSharp.Threading;
+using Interlocked = Crestron.SimplSharp.Interlocked;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+{
+	/// <summary>
+	/// Summary description for TestWaitForIdle.
+	/// </summary>
+	[TestFixture]
+	[Category("TestWaitForIdle")]
+	public class TestWaitForIdle
+	{
+	    /// <summary>
+        /// Example of waiting for idle
+        /// </summary>
+        [Test]
+        public void WaitForIdle() 
+        { 
+            SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
+
+            ManualResetEvent isRunning = new ManualResetEvent(false);
+
+            for(int i = 0; i < 4; ++i)
+            {
+                smartThreadPool.QueueWorkItem(delegate { isRunning.WaitOne(); });
+            }
+
+            bool success = !smartThreadPool.WaitForIdle(1000);
+
+            isRunning.Set();
+
+            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/SSharpSmartThreadPoolTests/TestWorkItemTimeout.cs b/SSharpSmartThreadPoolTests/TestWorkItemTimeout.cs
new file mode 100644
index 0000000..698a825
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWorkItemTimeout.cs
@@ -0,0 +1,164 @@
+ï»¿using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace SmartThreadPoolTests
+	{
+	[TestFixture]
+	[Category ("TestWorkItemTimeout")]
+	public class TestWorkItemTimeout
+		{
+		/// <summary>
+		/// 1. Create STP in suspended mode
+		/// 2. Queue work item into the STP
+		/// 3. Wait for the work item to expire
+		/// 4. Work item's GetResult should throw WorkItemCancelException
+		/// </summary>        
+		[Test]
+		public void CancelInQueueWorkItem ()
+			{
+			Assert.Throws<WorkItemCancelException> (() =>
+				{
+					STPStartInfo stpStartInfo = new STPStartInfo ();
+					stpStartInfo.StartSuspended = true;
+
+					bool hasRun = false;
+
+					SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
+					IWorkItemResult wir = stp.QueueWorkItem (new WorkItemInfo ()
+						{
+							Timeout = 500
+						}, state =>
+							{
+								hasRun = true;
+								return null;
+							});
+
+					Assert.IsFalse (wir.IsCanceled);
+
+					Thread.Sleep (2000);
+
+					Assert.IsTrue (wir.IsCanceled);
+
+					stp.Start ();
+					stp.WaitForIdle ();
+
+					Assert.IsFalse (hasRun);
+
+					try
+						{
+						wir.GetResult ();
+						}
+					finally
+						{
+						stp.Shutdown ();
+						}
+				});
+			}
+
+		/// <summary>
+		/// 1. Create STP
+		/// 2. Queue work item that takes some time
+		/// 3. Wait for it to start
+		/// 4. The work item timeout expires
+		/// 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
+		/// </summary>        
+		[Test]
+		public void TimeoutInProgressWorkItemWithSample ()
+			{
+			bool timedout = false;
+			ManualResetEvent waitToStart = new ManualResetEvent (false);
+			ManualResetEvent waitToComplete = new ManualResetEvent (false);
+
+			SmartThreadPool stp = new SmartThreadPool ();
+			IWorkItemResult wir = stp.QueueWorkItem (
+				new WorkItemInfo () { Timeout = 500 },
+				state =>
+					{
+					waitToStart.Set ();
+					Thread.Sleep (1000);
+					timedout = SmartThreadPool.IsWorkItemCanceled;
+					waitToComplete.Set ();
+					return null;
+					});
+
+			waitToStart.WaitOne ();
+
+			waitToComplete.WaitOne ();
+
+			Assert.IsTrue (timedout);
+
+			stp.Shutdown ();
+			}
+
+		/// <summary>
+		/// 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. The work item expires
+		/// 5. Work item's GetResult should return value
+		/// </summary>        
+		[Test]
+		public void TimeoutCompletedWorkItem ()
+			{
+			SmartThreadPool stp = new SmartThreadPool ();
+			IWorkItemResult wir =
+				stp.QueueWorkItem (
+				new WorkItemInfo () { Timeout = 500 },
+				state => 1);
+
+			stp.WaitForIdle ();
+
+			Assert.AreEqual (wir.GetResult (), 1);
+
+			Thread.Sleep (1000);
+
+			Assert.AreEqual (wir.GetResult (), 1);
+
+			stp.Shutdown ();
+			}
+
+		/// <summary>
+		/// 1. Create STP
+		/// 2. Queue work item that takes some time
+		/// 3. Wait for it to start
+		/// 4. Cancel the work item (soft)
+		/// 5. Call to AbortOnWorkItemCancel
+		/// 5. Wait for the STP to get idle
+		/// 6. Make sure nothing ran in the work item after the AbortOnWorkItemCancel
+		/// </summary>        
+		[Test]
+		public void TimeoutInProgressWorkItemSoftWithAbortOnWorkItemCancel ()
+			{
+			bool abortFailed = false;
+			ManualResetEvent waitToStart = new ManualResetEvent (false);
+			ManualResetEvent waitToComplete = new ManualResetEvent (false);
+
+			SmartThreadPool stp = new SmartThreadPool ();
+			IWorkItemResult wir = stp.QueueWorkItem (
+				new WorkItemInfo () { Timeout = 500 },
+				state =>
+					{
+					waitToStart.Set ();
+					Thread.Sleep (1000);
+					SmartThreadPool.AbortOnWorkItemCancel ();
+					abortFailed = true;
+					return null;
+					});
+
+			waitToStart.WaitOne ();
+
+			stp.WaitForIdle ();
+
+			Assert.IsTrue (wir.IsCanceled);
+			Assert.IsFalse (abortFailed);
+			stp.Shutdown ();
+			}
+		}
+	}
diff --git a/SSharpSmartThreadPoolTests/TestWorkItemsGroups.cs b/SSharpSmartThreadPoolTests/TestWorkItemsGroups.cs
new file mode 100644
index 0000000..118e7a6
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWorkItemsGroups.cs
@@ -0,0 +1,138 @@
+using System;
+using Crestron.SimplSharpPro.CrestronThread;
+using SSharp.Threading;
+using NUnit.Framework;
+
+using Amib.SSharpThreading;
+
+namespace WorkItemsGroupTests
+{
+	/// <summary>
+	/// Summary description for TestWorkItemsGroups.
+	/// </summary>
+	[TestFixture]
+	[Category("TestWorkItemsGroups")]
+	public class TestWorkItemsGroups
+	{
+	    [Test]
+		public void BlockingCall() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			bool success = false;
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+
+			if (!wir.IsCompleted)
+			{
+				int result = (int)wir.GetResult();
+				success = (1 == result);
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+
+		/// <summary>
+		/// Example of how to queue a work item and then wait on a timeout for the result.
+		/// </summary>
+		[Test]
+		public void Timeout() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			bool success = false;
+
+			IWorkItemResult wir = 
+				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
+
+			try
+			{
+				wir.GetResult(500, true);
+			}
+			catch (WorkItemTimeoutException)
+			{
+				success = true;
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		/// <summary>
+		/// Example of how to interrupt the waiting for a work item to complete.
+		/// </summary>
+		[Test]
+		public void WorkItemWaitCanceling() 
+		{ 
+			SmartThreadPool smartThreadPool = new SmartThreadPool();
+			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
+
+			ManualResetEvent cancelWaitHandle = new ManualResetEvent(false);
+
+			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.SignalCancel), cancelWaitHandle);
+
+			try
+			{
+				wir1.GetResult(Crestron.SimplSharp.Timeout.Infinite, true, cancelWaitHandle);
+			}
+			catch (WorkItemTimeoutException) 
+			{
+				success = true;
+			}
+
+			smartThreadPool.Shutdown();
+
+			Assert.IsTrue(success);
+		} 
+
+		private object DoSomeWork(object state)
+		{ 
+			Thread.Sleep(1000);
+			return 1;
+		}
+
+		private object SignalCancel(object state)
+		{ 
+			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
+			Thread.Sleep(250);
+			cancelWaitHandle.Set();
+			return null;
+		}
+
+		[Test]
+		public void Concurrency() 
+		{ 
+		}
+
+		[Test]
+		public void WaitForIdle() 
+		{ 
+		}
+
+		[Test]
+		public void OnIdleEvent() 
+		{ 
+		}
+
+		[Test]
+		public void MultipleGroups() 
+		{ 
+		}
+
+	}
+}
diff --git a/SSharpSmartThreadPoolTests/TestWorkItemsQueue.cs b/SSharpSmartThreadPoolTests/TestWorkItemsQueue.cs
new file mode 100644
index 0000000..c7a160f
--- /dev/null
+++ b/SSharpSmartThreadPoolTests/TestWorkItemsQueue.cs
@@ -0,0 +1,44 @@
+using NUnit.Framework;
+using Amib.SSharpThreading.Internal;
+
+namespace PriorityQueueTests
+{
+	/// <summary>
+	/// Summary description for TestWorkItemsQueue.
+	/// </summary>
+	[TestFixture]
+	[Category("TestWorkItemsQueue")]
+	public class TestWorkItemsQueue
+	{
+	    [Test]
+		public void Init()
+		{
+		}
+
+		[Test]
+		public void IdempotenceWaiterEntry()
+		{
+			WorkItemsQueue q = new WorkItemsQueue();
+
+			Assert.AreEqual(0, q.WaitersCount);
+
+			WorkItemsQueue.WaiterEntry we1 = new WorkItemsQueue.WaiterEntry();
+			q.PushWaiter(we1);
+
+			Assert.AreEqual(1, q.WaitersCount);
+
+			q.PushWaiter(we1);	
+	
+			Assert.AreEqual(1, q.WaitersCount);
+
+			WorkItemsQueue.WaiterEntry we2 = new WorkItemsQueue.WaiterEntry();
+			q.PushWaiter(we2);	
+
+			Assert.AreEqual(2, q.WaitersCount);
+
+			q.PushWaiter(we2);	
+
+			Assert.AreEqual(2, q.WaitersCount);
+		}
+	}
+}
diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..d38c8d9
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,20 @@
+Microsoft Public License (MS-PL)
+[OSI Approved License]
+This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
+
+1. Definitions
+The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
+A "contribution" is the original software, or any additions or changes to the software.
+A "contributor" is any person that distributes its contribution under this license.
+"Licensed patents" are a contributor's patent claims that read directly on its contribution.
+
+2. Grant of Rights
+(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+
+3. Conditions and Limitations
+(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
+(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
+(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
+(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
\ No newline at end of file

Fixed code: # Auto detect text files and perform LF normalization
* text=auto

# Custom for Visual Studio
*.cs     diff=csharp

# Standard to msysgit
*.doc	 diff=astextplain
*.DOC	 diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot  diff=astextplain
*.DOT  diff=astextplain
*.pdf  diff=astextplain
*.PDF	 diff=astextplain
*.rtf	 diff=astextplain
*.RTF	 diff=astextplain
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/

# Roslyn cache directories
*.ide/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

#NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile

# Visual Studio profiler
*.psess
*.vsp
*.vspx

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding addin-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings 
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# If using the old MSBuild-Integrated Package Restore, uncomment this:
#!**/packages/repositories.config

# Windows Azure Build Output
csx/
*.build.csdef

# Windows Store app package directory
AppPackages/

# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# =========================
# Operating System Files
# =========================

# OSX
# =========================

.DS_Store
.AppleDouble
.LSOverride

# Thumbnails
._*

# Files that might appear on external disk
.Spotlight-V100
.Trashes

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# Windows
# =========================

# Windows image file caches
Thumbs.db
ehthumbs.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk


# Crestron S# Files
ControlSystem.cfg


<ul class="download">
<li><a href="smartthreadpool_2.2.3.zip">Download SmartThreadPool 2.2.3 source, examples, tests, and demos - 651KB</a></li></ul>


<p><img width="600" height="405" src="smartthreadpool.jpg" /></p>

<p>See the <a href="#History">History</a> section at the bottom for changes.</p>

<h2>Basic usage&nbsp;</h2>

<p>This is a Thread Pool; if you got here, you probably 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 <a href="#SimpleExample">examples</a> for advanced usage.</p>

<pre lang="cs">// Create an instance of the Smart Thread Pool
SmartThreadPool smartThreadPool = new SmartThreadPool();

// Queue an action (Fire and forget)
smartThreadPool.QueueWorkItem(System.IO.File.Copy, 
  @&quot;C:\Temp\myfile.bin&quot;, @&quot;C:\Temp\myfile.bak&quot;);

// The action (file copy) will be done in the background by the Thread Pool</pre>

<h2><a name="Introduction">Introduction</a></h2>

<p>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:</p>

<ul>
<li>The number of threads dynamically changes according to the workload on the threads in the pool.&nbsp;</li>

<li>Work items can return a value.</li>

<li>A work item can be cancelled if it hasn't been executed yet.</li>

<li>The caller thread's context is used when the work item is executed (limited).</li>

<li>Usage of minimum number of Win32 event handles, so the handle count of the application won't explode.</li>

<li>The caller can wait for multiple or all the work items to complete.</li>

<li>A work item can have a <code>PostExecute</code> callback, which is called as soon the work item is completed.</li>

<li>The state object that accompanies the work item can be disposed automatically.</li>

<li>Work item exceptions are sent back to the caller.</li>

<li>Work items have priority.</li>

<li>Work items group.</li>

<li>The caller can suspend the start of a thread pool and work items group.</li>

<li>Threads have priority.</li>

<li>Threads have initialization and termination events.</li>

<li>WinCE platform is supported (limited).</li>

<li><code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code> generic methods are supported.</li>

<li><t>Silverlight is supported.</t></li>

<li>Mono is supported.</li>

<li><t>Performance counters (Windows and internal).</t></li>

<li>Work item timeout (passive).</li>
<li>Threads ApartmentState</li>
<li>Threads IsBakcground</li>
<li>Threads name template</li>
<li>Windows Phone is supported (limited)</li>
<li>Threads MaxStackSize</li>

</ul>

<h2>Why do you need a thread pool?</h2>

<blockquote>
<p>&quot;Many applications create threads that spend a great deal of time in the sleeping state, waiting for an event to occur. Other threads might enter a sleeping state only to be awakened periodically to poll for a change or update status information. Thread pooling enables you to use threads more efficiently by providing your application with a pool of worker threads that are managed by the system. One thread monitors the status of several wait operations queued to the thread pool. When a wait operation completes, a worker thread from the thread pool executes the corresponding callback function&quot;.</p>

<p>MSDN, April 2004, ThreadPool Class [C#].</p>
</blockquote>

<h2>Smart Thread Pool features</h2>

<p>When I wrote my application, I discovered that I needed a thread pool with the following features:</p>

<ul>
<li>The thread pool should implement the <code>QueueUserWorkItem()</code> method to comply with the .NET ThreadPool.</li>

<li>The thread pool should be instantiated. (No static methods.) So, the threads in the pool are used only for one purpose.</li>

<li>The number of threads in the pool should be dynamic with lower and upper limits.</li>
</ul>

<p>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:</p>

<ol>
<li><a href="#Feature1">The thread pool is instantiable</a>.</li>

<li><a href="#Feature2">The number of threads dynamically changes</a>.</li>

<li><a href="#Feature3">Work items return a value</a>.</li>

<li><a href="#Feature4">The caller can wait for multiple work items to complete</a>.</li>

<li><a href="#Feature5">A work item can be cancelled</a>.</li>

<li><a href="#Feature6">The caller thread's context is used when the work item is executed (limited)</a>.</li>

<li><a href="#Feature7">Usage of minimum number of Win32 event handles, so the handle count of the application won't explode</a>.</li>
</ol>

<p>Because of features 3 and 5, the thread pool no longer complies to the .NET ThreadPool, and so I could add more features.</p>

<p>See the additional features section below for the new features added in this version.</p>

<h2>Additional features added in December 2004</h2>

<ol>
<li value="8"><a href="#Feature8">Every 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>.</li>

<li><a href="#Feature9">The user can choose to automatically dispose off the state object that accompanies the work item</a>.</li>

<li><a href="#Feature10">The user can wait for the Smart Thread Pool to become idle</a>.</li>

<li><a href="#Feature11">Exception handling has been changed, so if a work item throws an exception, it is re-thrown at <code>GetResult()</code>, rather than firing an <code>UnhandledException</code> event. Note that <code>PostExecute</code> exceptions are always ignored</a>.</li>
</ol>

<h2>New features added in January 2006</h2>

<ol>
<li value="12"><a href="#Feature12">Work items have priority</a>.</li>

<li><a href="#Feature13">The caller thread's HTTP context can be used when the work item is executed (improves 6)</a>.</li>

<li><a href="#Feature14">Work items group</a>.</li>

<li><a href="#Feature15">The caller can create thread pools and work item groups in suspended state</a>.</li>

<li><a href="#Feature16">Threads have priority</a>.</li>
</ol>

<h2>New features added in May 2008</h2>

<ol>
<li value="17"><a href="#Feature17">Enabled the change of <code>MaxThreads</code>/<code>MinThreads</code>/<code>Concurrency</code> at run time</a>.</li>

<li><a href="#Feature5">Improved the Cancel behavior (see section 5)</a>.</li>

<li><a href="#Feature19">Added callbacks for initialization and termination of threads</a>.</li>

<li><a href="#Feature20">Added support for WinCE (limited)</a>.</li>

<li><a href="#Feature21">Added the <code>IsIdle</code> flag to <code>SmartThreadPool</code> and to <code>IWorkItemsGroup</code></a>.</li>

<li><a href="#Feature3">Added support for <code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code> (strong typed work items) (see section 3)</a>.</li>
</ol>

<h2>New features added in April 2009</h2>

<ol>
<li value="23"><a href="#Feature23">Added support for Silverlight</a>.</li>

<li><a href="#Feature24">Added support for Mono</a>.</li>

<li><a href="#Feature25">Added internal performance counters (for Windows CE, Silverlight, and Mono)</a>.</li>

<li><a href="#Feature26">Added new methods: <code>Join</code>, <code>Choice</code>, and <code>Pipe</code></a>.</li>
</ol>

<h2>New features added in December 2009</h2>

<ol>
<li value="27"><a href="#Feature27">Added work item timeout (passive)</a>.</li>
</ol>
<h2>
    New features added in August 2012</h2>
<ol>
    <li value="28"><a href="#Feature28">Enabled to set threads IsBackground.</a></li>
    <li value="29"><a href="#Feature29">Enabled to set threads ApartmentState.
    </a></li>
<li value="30"><a href="#Feature30">Added support fore Windows Phone (limited).</a></li>
<li value="31"><a href="#Feature30"></a><a href="#Feature31">Enabled to set threads MaxStackSize.</a></li>
</ol>
<p>


</p><h2>What about the .NET ThreadPool?&nbsp;</h2>

<p>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 the .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 &quot;Begin&quot; (e.g., <code>BeginInvoke</code>, <code>BeginSend</code>, <code>BeginReceive</code>, 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.</p>

<p>This thread pool doesn't comply with the requirements 1, 5, 6, 8, 9, 10, 12-25.</p>

<p>Note that the requirements 3 and 4 are implemented in .NET ThreadPool with delegates.</p>

<h2>What about Stephen Toub's thread pool?</h2>

<p>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 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.</p>

<p>This thread pool doesn't comply with the requirements 1, 2, 3, 4, 5, 6, 8-25.</p>

<h2>The Smart Thread Pool design and features</h2>

<p>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.</p>

<p>Features implementation:</p>

<ol>
<li><a name="Feature1"><strong>The thread pool is instantiable</strong></a>.</li>

<p>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 a 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.</p>

<p>To implement this feature, I just copied Toub's implementation and removed the <code lang="cs">static</code> keyword from the methods. That's the easy part of it.</p>

<li><a name="Feature2"><strong>The number of threads dynamically changes</strong></a>.</li>

<p>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.</p>

<p>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?</p>

<p>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:</p>

<pre lang="text">_currentWorkItemsCount &gt; WorkerThreads</pre>

<p>where <code>WorkerThreads</code> is the current number of threads in the pool, <code>InUseThreads</code> is the number of threads in the pool that are currently working on a work item, and <code>WaitingCallbacks</code> is the number of waiting work items. (Thanks to <strong>jrshute</strong> for the comment.)</p>

<p>The <code>SmartThreadPool.Enqueue()</code> method looks like this:</p>

<pre lang="cs">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 (_currentWorkItemsCount &gt; _workerThreads.Count) 
    { 
        StartThreads(1); 
    } 
}</pre>

<p>When the number of threads reaches the upper limit, no more threads are created.</p>

<p>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.</p>

<p>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 (use 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.</p>

<p>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?</p>

<p>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.</p>

<p>Have a look at the following scenario:</p>

<p>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:</p>

<table width="550" class="ArticleTable">
<thead>
<tr>
<td>
<p align="center">Work item arrival time (sec)</p>
</td>

<td>
<p align="center">Work item work duration (sec)</p>
</td>

<td>
<p align="center">Threads queue state</p>
</td>

<td>
<p align="center">The thread that will execute the arrived work item</p>
</td>
</tr>
</thead>

<tbody>
<tr>
<td align="center">
<p align="center">00:00:00</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td width="113" align="center">
<p align="center">A, B, C, D</p>
</td>

<td align="center">
<p align="center">A</p>
</td>
</tr>

<tr>
<td align="center">
<p align="center">00:00:01</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td width="113" align="center">
<p align="center">B, C, D</p>
</td>

<td align="center">
<p align="center">B</p>
</td>
</tr>

<tr>
<td align="center">
<p align="center">00:00:02</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td width="113" align="center">
<p align="center">C, D, A</p>
</td>

<td align="center">
<p align="center">C</p>
</td>
</tr>

<tr>
<td align="center">
<p align="center">00:00:03</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td width="113" align="center">
<p align="center">D, A, B</p>
</td>

<td align="center">
<p align="center">D</p>
</td>
</tr>
</tbody>
</table>

<p>In this scenario, all the four threads are used, although two threads could handle all the work items.</p>

<p>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.</p>

<p>The previous scenario will look like this with the new implementation:</p>

<table width="550" class="ArticleTable">
<thead>
<tr>
<td>
<p align="center">Work item arrival time (sec)</p>
</td>

<td>
<p align="center">Work item work duration (sec)</p>
</td>

<td>
<p align="center">Threads queue state</p>
</td>

<td>
<p align="center">The thread that will execute the arrived work item</p>
</td>
</tr>
</thead>

<tbody>
<tr>
<td align="center">
<p align="center">00:00:00</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td align="center">
<p align="center">A, B, C, D</p>
</td>

<td align="center">
<p align="center">A</p>
</td>
</tr>

<tr>
<td align="center">
<p align="center">00:00:01</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td align="center">
<p align="center">B, C, D</p>
</td>

<td align="center">
<p align="center">B</p>
</td>
</tr>

<tr>
<td align="center">
<p align="center">00:00:02</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td align="center">
<p align="center">A, C, D</p>
</td>

<td align="center">
<p align="center">A</p>
</td>
</tr>

<tr>
<td align="center">
<p align="center">00:00:03</p>
</td>

<td align="center">
<p align="center">1.5</p>
</td>

<td align="center">
<p align="center">B, C, D</p>
</td>

<td align="center">
<p align="center">B</p>
</td>
</tr>
</tbody>
</table>

<p>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 threads C and D will have to quit.</p>

<p>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 the 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.</p>

<p>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.</p>

<p>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 quit. 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.</p>

<li lang="cs"><a name="Feature3"><strong>Work items return a value</strong></a> (this feature is enhanced).</li>

<p>This feature is very useful in cases where you want to know the result of a work item.</p>

<p>The .NET ThreadPool supports this feature via delegates. Each time you create a delegate, you get the <code>BeginInvoke()</code> and <code>EndInvoke()</code> methods for free. <code>BeginInvoke()</code> queues the method and its parameters on the .NET ThreadPool, and <code>EndInvoke()</code> returns the result of the method. The delegate class is <code lang="cs">sealed</code>, so I couldn't override the <code>BeginInvoke()</code> and <code>EndInvoke()</code> methods. I took a different approach to implement this.</p>

<p>First, the work item callback delegate can return a value:</p>

<pre lang="cs">public delegate object WorkItemCallback(object state);</pre>

<p>Or in its enhanced form, the callback can be any of the following forms:</p>

<pre lang="cs">public delegate void Action(); 
public delegate void Action&lt;T&gt;(T arg); 
public delegate void Action&lt;T1, T2&gt;(T1 arg1, T2 arg2); 
public delegate void Action&lt;T1, T2, T3&gt;(T1 arg1, 
                     T2 arg2, T3 arg3); 
public delegate void Action&lt;T1, T2, T3, T4&gt;(T1 arg1, 
                     T2 arg2, T3 arg3, T4 arg4); 

public delegate TResult Func(); 
public delegate TResult Func&lt;T&gt;(T arg1); 
public delegate TResult Func&lt;T1, T2&gt;(T1 arg1, T2 arg2); 
public delegate TResult Func&lt;T1, T2, T3&gt;(T1 arg1, 
                        T2 arg2, T3 arg3); 
public delegate TResult Func&lt;T1, T2, T3, T4&gt;(T1 arg1, 
                        T2 arg2, T3 arg3, T4 arg4);</pre>

<p>(Note that the above delegates are defined in .NET 3.5. In .NET 2.0 &amp; 3.0, only <code lang="cs">public delegate void Action&lt;T&gt;(T arg)</code> is defined.) Second, the <code>SmartThreadPool.QueueWorkItem()</code> method returns a reference to an object that implements the <code>IWorkItemResult&lt;TResult&gt;</code> interface. The caller can use this object to get the result of the work item. The interface is similar to the <code>IAsyncResult</code> interface:</p>

<pre lang="cs">public interface IWorkItemResult&lt;TResult&gt;
{ 
    /// 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<span class="code-SummaryComment">&lt;...&gt;
</span>    /// or Func<span class="code-SummaryComment">&lt;...&gt; the State value
</span>    /// 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.<span class="code-SummaryComment">&lt;/param&gt;
</span>    /// 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; }
}</pre>

<p>If the work item callback is <code lang="cs">object WorkItemCallback(object state)</code>, then <code>IWorkItemResult</code> is returned, and <code>GetResult()</code> returns <code lang="cs">object</code>. Same as in previous versions.</p>

<p>If the work item callback is one of the <code>Func&lt;...&gt;</code> methods I mentioned above, the result of <code>QueueWorkItem</code> is <code>IWorkItemResult&lt;TResult&gt;</code>. So, the result of the work item is strongly typed.</p>

<p>If the work item callback is one of the <code>Action&lt;...&gt;</code> methods I mentioned above, the result of <code>QueueWorkItem</code> is <code>IWorkItemResult</code> and <code>GetResult()</code> always returns <code lang="cs">null</code>.</p>

<p>If the work item callback is <code>Action&lt;...&gt;</code> or <code>Func&lt;...&gt;</code> and <code>WIGStartInfo.FillStateWithArgs</code> is set to <code lang="cs">true</code>, then the <code>State</code> of the <code>IWorkItemResult</code> is initialized with a <code lang="cs">object []</code> that contains the work item arguments. Otherwise, the <code>State</code> is <code lang="cs">null</code>.</p>

<p>The <a href="#SimpleExample">code examples</a> in the section below show some snippets of how to use it.</p>

<p>To get the result of the work item, use the <code>Result</code> property or the <code>GetResult()</code> 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 <code>GetResult()</code> 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:</p>

<table width="550" class="ArticleTable">
<thead>
<tr>
<td width="266">GetResult() return reason</td>

<td width="344">GetResult() return value</td>
</tr>
</thead>

<tbody>
<tr>
<td>The work item has been executed and completed.</td>

<td>The result of the work item.</td>
</tr>

<tr>
<td height="39">The work item has been canceled.</td>

<td height="39">Throws <code>WorkItemCancelException</code>.</td>
</tr>

<tr>
<td height="39">The timeout expired.</td>

<td height="39">Throws <code>WorkItemTimeoutException</code>.</td>
</tr>

<tr>
<td>The <code>cancelWaitHandle</code> is signaled.</td>

<td>Throws <code>WorkItemTimeoutException</code>.</td>
</tr>

<tr>
<td>The work item threw an exception.</td>

<td>Throws <code>WorkItemResultException</code> with the work item's exception as the inner exception.</td>
</tr>
</tbody>
</table>

<p>There are two ways to wait for a single work item to complete:</p>

<ol>
<li>The following function uses the <code>GetResult()</code> method which blocks the caller until the result is available:</li>

<pre lang="cs">private void WaitForResult1(IWorkItemResult wir)
{
    wir.GetResult();
}</pre>

<li>The following function is not recommended, because it uses a busy wait loop. You can use it if you know what you are doing:</li>

<pre lang="cs">private void WaitForResult2(IWorkItemResult wir)
{
    while(!wir.IsCompleted)
    {
        Thread.Sleep(100);
    }
}</pre>
</ol>

<li><a name="Feature4"><strong>The caller can wait for multiple work items to complete</strong></a>.</li>

<p>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 <code>SmartThreadPool</code> class has two static methods for this: <code>WaitAny()</code> and <code>WaitAll()</code> (they have several overloads). Their signature is similar to the <code>WaitHandle</code> equivalent methods except that in the <code>SmartThreadPool</code> case, it gets an array of <code>IWaitableResult</code> (the <code>IWorkItemResult</code> interface inherits from <code>IWaitableResult</code>) objects instead of <code>WaitHandle</code> objects.</p>

<p>The following snippets show how to wait for several work item results at once. Assume <code>wir1</code> and <code>wir2</code> are of type <code>IWorkItemResult</code>. You can wait for both work items to complete:</p>

<pre lang="cs">// Wait for both work items complete 
SmartThreadPool.WaitAll(new IWaitableResult[] { wir1, wir2});</pre>

<p>Or, for any of the work items to complete:</p>

<pre lang="cs">// Wait for at least one of the work items complete 
SmartThreadPool.WaitAny(new IWaitableResult[] { wir1, wir2});</pre>

<p>The <code>WaitAll()</code> and <code>WaitAny()</code> methods are overloaded, so you can specify the timeout, the exit context, and <code>cancelWaitHandle</code> (just like in the <code>GetResult()</code> method mentioned earlier).</p>

<p><strong>Note</strong> that in order to use <code>WaitAny()</code> and <code>WaitAll()</code>, you need to work in MTA, because internally, I use <code>WaitHandle.WaitAny()</code> and <code>WaitHandle.WaitAll()</code> which require it. If you don't do that, the methods will throw an exception to remind you.</p>

<p><strong>Also note</strong> that Windows supports <code>WaitAny()</code> of up to 64 handles. <code>WaitAll()</code> is more flexible, and I re-implemented it so it is not limited to 64 handles.</p>

<p>See in the examples section below, the code snippets for <a href="#WaitForAllExample"><code>WaitAll</code></a> and <a href="#WaitForAnyExample"><code>WaitAny</code></a>.</p>

<li><a name="Feature5"><strong>A work item can be cancelled</strong></a> (this feature is enhanced).</li>

<p>This feature enables to cancel work items.</p>

<p>There are several options to cancel work items. To cancel a single work item, call <code>IWorkItemResult.Cancel()</code>. To cancel more than one, call <code>IWorkItemsGroup.Cancel()</code> or <code>SmartThreadPool.Cancel()</code>. All cancels works in O(1).</p>

<p>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 that the work item's state I mention here has nothing to do with the state object argument provided in the <code>QueueWorkItem</code>.)</p>

<p>These are the possible states of a work item (defined in the <code>WorkItemState</code> enum):</p>

<ul class="enum">
<li><code>Queued</code>  The work item is waiting in a queue to be executed.</li>

<li><code>InProgress</code>  A thread from the pool is executing the work item.</li>

<li><code>Completed</code>  The work item execution has been completed.</li>

<li><code>Cancelled</code>  The work item has been cancelled.</li>
</ul>

<p>The cancel behavior depends on the state of the work item.</p>

<table class="ArticleTable">
<thead>
<tr>
<td width="140" align="center">Initial State</td>

<td width="99" align="center">Next State</td>

<td width="332" align="center">Notes</td>
</tr>
</thead>

<tbody>
<tr>
<td><code>Queued</code></td>

<td><code>Cancelled</code></td>

<td>A queued work item becomes cancelled and is not executed at all.</td>
</tr>

<tr>
<td><code>InProgress</code></td>

<td><code>Cancelled</code></td>

<td>An executing work item becomes cancelled even if it completes its execution!</td>
</tr>

<tr>
<td><code>Completed</code></td>

<td><code>Completed</code></td>

<td>A completed work item stays completed.</td>
</tr>

<tr>
<td><code>Cancelled</code></td>

<td><code>Cancelled</code></td>

<td>A cancelled work item stays cancelled.</td>
</tr>
</tbody>
</table>

<p>A cancelled work item throws a <code>WorkItemCancelException</code> when its <code>GetResult()</code> method is called.</p>

<p>The behavior of <code>Cancel()</code> when the work item is in <code>Completed</code> or <code>Cancelled</code> states is straightforward, so I won't get into the details. A queued work item is marked as cancelled and is discarded once a thread from the pool dequeues it.</p>

<p>If a work item is in the <code>InProgress</code> state, then the behavior depends on the value of <code>abortExecution</code> in the <code>Cancel</code> call. When <code>abortExecution</code> is <code lang="cs">true</code>, a <code>Thread.Abort()</code> will be called upon the executing thread. When <code>abortExecution</code> is <code lang="cs">false</code>, the work item method is responsible to sample the <code>SmartThreadPool.IsWorkItemCanceled</code> static method and quit. Note that, in both cases, the work item is cancelled, and throws a <code>WorkItemCancelException</code> on <code>GetResult()</code>.</p>

<p>Here is an example of a cooperative work item:</p>

<pre lang="cs">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
    }
}</pre>

<li><a name="Feature6"><strong>The caller thread's context is used when the work item is executed (limited)</strong></a>.</li>

<p>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 <code>CompressedStack</code> 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:</p>

<ul>
<li><code>CurrentCulture</code> - The culture of the thread.</li>

<li><code>CurrentUICulture</code> - The culture used by the resource manager to look up culture-specific resources at run time.</li>

<li><code>CurrentPrincipal</code> - The current principal (for role-based security).</li>

<li><code>CurrentContext</code> - The current context in which the thread is executing (used in remoting).</li>
</ul>

<p>The first three belong to the <code>System.Threading.Thread</code> 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.</p>

<p>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 <code>CallerThreadContext</code>, and it is used internally. When Microsoft unblocks the protection on the <code>CompressedStack</code>, I will add it there.</p>

<p>The caller thread's context is stored when the work item is created, within the <code>EnqueueWorkItem()</code> method. Each time a thread from the pool executes a work item, the thread's context changes in the following order:</p>

<ol>
<li>The current thread context is captured.</li>

<li>The caller thread context is applied.</li>

<li>The work item is executed.</li>

<li>The current old thread context is restored.</li>
</ol>

<li><strong>Usage of <a name="Feature7">minimum number of Win32 event handles, so the handle count of the application won't explode</a></strong>.</li>

<p>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 <code>Close()</code> method of the <code>ManualResetEvent</code> 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.</p>

<p>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 <code>WaitHandle</code>, but use them internally and then close them.</p>

<p>In order to create fewer handles, I created the <code>ManualResetEvent</code> objects only when the user asks for them (lazy creation). For example, if you don't use the <code>GetResult()</code> of the <code>IWorkItemResult</code> interface, then a handle is not created. Using <code>SmartThreadPool.WaitAll()</code> and <code>SmartThreadPool.WaitAny()</code> creates a handle.</p>

<p>The work item queue creates a lot of handles since each new wait for a work item creates a new <code>ManualResetEvent</code>. 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 <code>ManualResetEvent</code> 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.</p>

<li><a name="Feature8"><strong>Work item can have a PostExecute callback. This is a method that will be called right after the work item execution has been completed</strong></a>.</li>

<p>A <code>PostExecute</code> 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 executes the work item. The user can choose the cases in which <code>PostExecute</code> is called. The options are represented in the <code>CallToPostExecute</code> flagged enumerator:</p>

<pre lang="cs">[Flags]
public enum CallToPostExecute
{
    Never                    = 0x00,
    WhenWorkItemCanceled     = 0x01,
    WhenWorkItemNotCanceled  = 0x02,
    Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
}</pre>

<p>Explanation:</p>

<ul class="enum">
<li><code>Never</code>  Don't run <code>PostExecute</code>.</li>

<li><code>WhenWorkItemCanceled</code> - Run <code>PostExecute</code> only when the work item has been canceled.</li>

<li><code>WhenWorkItemNotCanceled</code> - Run <code>PostExecute</code> only when the work item has not been canceled.</li>

<li><code>Always</code>  Always run <code>PostExecute</code>.</li>
</ul>

<p><code>SmartThreadPool</code> has a default <code>CallToPostExecute</code> value of <code>CallToPostExecute.Always</code>. This can be changed during the construction of <code>SmartThreadPool</code> in the <code>STPStartInfo</code> class argument. Another way to give the <code>CallToPostExecute</code> value is in one of the <code>SmartThreadPool.QueueWorkItem</code> overloads. Note that, as opposed to the <code>WorkItem</code> execution, if an exception has been thrown during <code>PostExecute</code>, then it is ignored. <code>PostExecute</code> is a delegate with the following signature:</p>

<pre lang="cs">public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir);</pre>

<p>As you can see, <code>PostExecute</code> receives an argument of type <code>IWorkItemResult</code>. It can be used to get the result of the work item, or any other information made available by the <code>IWorkItemResult</code> interface.</p>

<li><a name="Feature9"><strong>The user can choose to automatically dispose off the state object that accompanies the work item</strong></a>.</li>

<p>When the user calls <code>QueueWorkItem</code>, he/she can provide a state object. The state object usually stores specific information, such as arguments that should be used within the <code>WorkItemCallback</code> delegate.</p>

<p>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.</p>

<p>For this reason, I added a boolean to <code>SmartThreadPool</code> that indicates to call <code>Dispose</code> on the state object when the work item has been completed. The boolean is initialized when the thread pool is constructed with <code>STPStartInfo</code>. <code>Dispose</code> is called only if the state object implements the <code>IDisposable</code> interface. <code>Dispose</code> is called after the <code>WorkItem</code> has been completed and its <code>PostExecute</code> has run (if a <code>PostExecute</code> exists). The state object is disposed even if the work item has been canceled or the thread pool has been shutdown.</p>

<p><strong>Note</strong> that this feature only applies to the <code>state</code> argument that comes with <code>WorkItemCallback</code>, it doesn't apply to the arguments supplied in <code>Action&lt;...&gt;</code> and <code>Func&lt;...&gt;</code>!!!</p>

<li><a name="Feature10"><strong>The user can wait for the Smart Thread Pool / Work Items Group to become idle. </strong></a>
<p>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.</p>

<p>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 <code>IWorkItemResult</code> objects in case you just want to wait for all of the work items to complete.</p>

<p>The <code>SmartThreadPool</code> and WorkItemsGroup classes both implement the <code>IWorkItemsGroup</code> interface which defines the <code>WaitForIdle</code> methods.</p>

<p>To take advantage of this feature, use the <code>IWorkItemsGroup.WaitForIdle()</code> method (both <code>SmartThreadPool</code> and WorkItemsGroup implement the <code>IWorkItemsGroup</code> interface which defines the <code>WaitForIdle</code> methods). It has several overloads which provide a timeout argument. The <code>WaitForIdle()</code> method is not <code lang="cs">static</code>, and should be used on a <code>SmartThreadPool</code> instance.</p>

<p><code>SmartThreadPool</code> 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.</p>

<p>The <code>WaitForIdle()</code> mechanism works with a private <code>ManualResetEvent</code>. When a work item is queued, the <code>ManualResetEvent</code> is reset (changed to non-signaled state). When the work items count becomes zero (initial state of the Smart Thread Pool), the <code>ManualResetEvent</code> is set (changed to signaled state). The <code>WaitForIdle()</code> method just waits for the <code>ManualResetEvent</code> to implement its functionality.</p>

<p>See the <a href="#WaitForIdleExample">example</a> below.</p>
</li>

<li><a name="Feature11"><strong>Exception handling has been changed, so if a work item throws an exception, it is rethrown at <code>GetResult()</code>, rather than firing an <code>UnhandledException</code> event. Note that <code>PostExecute</code> exceptions are always ignored</strong></a>. 
<p>After I did some reading about delegates and their implementation, I decided to change the way <code>SmartThreadPool</code> treats exceptions. In the previous versions, I used an event driven mechanism. Entities were registered to the <code>SmartThreadPool.UnhandledException</code> event, and when a work item threw an exception, this event was fired. This is the behavior of Toubs thread pool.</p>

<p>.NET delegates behave differently. Instead of using an event driven mechanism, it re-throws the exception of the delegated method at <code>EndInvoke()</code>. Similarly, the <code>SmartThreadPool</code> exception mechanism is changed so that exceptions are no longer fired by the <code>UnhandledException</code> event, but rather re-thrown again when <code>IWorkItemResult.GetResult()</code> is called.</p>

<p>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 <code>GetResult()</code> 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.</p>

<p><code>GetResult()</code> can be called unlimited number of times, and it re-throws the same exception each time.</p>

<p>Note that <code>PostExecute</code> is called as and when needed, even if the work item has thrown an exception. Of course, the <code>PostExecute</code> implementation should handle exceptions if it calls <code>GetResult()</code>.</p>

<p>Also note that if <code>PostExecute</code> throws an exception, then its exception is ignored.</p>

<p>See the <a href="#CatchExceptionExample">example</a> below.</p>
</li>

<li><a name="Feature12"><strong>Work items have priority</strong></a>.</li>

<p>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:</p>

<pre lang="cs">public enum WorkItemPriority 
{ 
    Lowest, 
    BelowNormal, 
    Normal, 
    AboveNormal, 
    Highest,
}</pre>

<p>The default priority is <code>Normal</code>.</p>

<p>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.</p>

<p>This is the easiest solution to sort the work items.</p>

<li><a name="Feature13"><strong>The caller thread's HTTP context can be used when the work item is executed</strong></a>.</li>

<p>This feature improves 6, and was implemented by Steven T. I just replaced my code with that implementation.</p>

<p>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.</p>

<li><a name="Feature14"><strong>Work items group</strong></a>.</li>

<p>This feature enables the user to execute a group of work items specifying the maximum level of concurrency.</p>

<p>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 latter solution is too expensive (many threads) if the resources are idle most of the time.</p>

<p>The Smart Thread Pool solution is to create a WorkItemsGroup per resource, with a 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 the work items.</p>

<p>Here is a code snippet to show how this works:</p>

<pre lang="cs">...

// 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) {...}

...</pre>

<p>As you can see from the snippet, a WorkItemsGroup 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.</p>

<p>The WorkItemsGroup has a priority queue (the same as the <code>SmartThreadPool</code>). 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 <code>SmartThreadPool</code> with the same priority.</p>

<p>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 <code>SmartThreadPool</code>. If this number is less than the maximum level of concurrency, it queues the work item into the <code>SmartThreadPool</code>. If this number is equal (it cannot be greater), then the WorkItemsGroup stores the work item in its own priority queue.</p>

<p>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 <code>SmartThreadPool</code> up to the maximum level of concurrency.</p>

<p>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 <code>SmartThreadPool</code> queue.</p>

<p>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 <code>SmartThreadPool</code>. The event is the only way to accomplish the concurrency level. When I tried to do it with <code>PostExecute</code>, I got fluctuating <code>WaitForIdle</code>.</p>

<p>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 (<code>InQueue</code>, <code>InProgress</code>, <code>Completed</code>, or <code>Canceled</code>). The final state considers this object's value to know if the work item was cancelled.</p>

<p>The Work Items Group can also be used as a conjunction point. Say, you want to accomplish a task by splitting it to subtasks. Once the subtasks are completed, a new task is issued and split to subtasks too. This can be achieved by using the <code>OnIdle</code> event of the WorkItemsGroup. See the <a href="#WorkItemsGroupExample">examples</a> below.</p>

<p>See the WorkItemsGroupDemo demo in the source code solution.</p>

<p><img width="550" height="648" src="WorkItemsGroup.jpg" /></p>

<li><a name="Feature15"><strong>The caller can create thread pools and work item groups in suspended state</strong></a>.</li>

<p>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.</p>

<p>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 <code>Start()</code> method. The same method exists in the Work Items Group for the same purpose.</p>

<p>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.</p>

<li><a name="Feature16"><strong>Threads have priority</strong></a>.</li>

<p><code>STPStartInfo</code> contains a property that defines the priority in which the threads are started in the <code>SmartThreadPool</code>. Use it if you know what you are doing. Playing with threads priority may end up with dead locks, live lock, and days locked <img align="top" alt="Frown | :-(" src="http://www.codeproject.com/script/Forums/Images/smiley_frown.gif" /> .</p>

<li><a name="Feature17"><strong><code>MaxThreads</code>/<code>MinThreads</code>/<code>Concurrency</code> can be changed at run time</strong></a>.</li>

<p>This addition allows the user to control the concurrency of work items execution. It is useful to make the STP adaptable.</p>

<p>To execute more work items in parallel, increment the concurrency. To limit the number of executed work items and/or lower the CPU usage, decrement the concurrency.</p>

<p>This option is available in the <code>SmartThreadPool</code> and in the WorkItemsGroup with the <code>IWokItemsGroup</code> interface:</p>

<pre lang="cs">public interface IWorkItemsGroup
{
    ...
    int Concurrency { get; set; }
    ...
}</pre>

<p>The value of <code>Concurrency</code> must be positive.</p>

<p>Although <code>Concurrency</code> has the same meaning and the same behavior as in the <code>SmartThreadPool</code> and in the WorkItemsGroup, its implemented differently.</p>

<p><code>SmartThreadPool</code>s <code>Concurrency</code> is equivalent to the <code>MaxThreads</code> property. When <code>Concurrency</code> is incremented, the <code>SmartThreadPool</code> can create more threads to handle its work items up to the <code>Concurrency</code> limit. The creation of thread, in this case, is immediate. The threads are still created as explained in <a href="#Feature2">section 2</a>.</p>

<p>When <code>Concurrency</code> is decremented, the <code>SmartThreadPool</code> doesnt create new threads and let existing threads to be terminated in order to decrement the number of threads in the thread pool. Note that the lowering of <code>Concurrency</code> may take a while to take effect, since we need to wait for work items to complete. <code>SmartThreadPool</code> doesnt abort a thread actively, but waits passively until it quits.</p>

<p>WorkItemsGroups <code>Concurrency</code> is responsible for how many work items may be handled in parallel in the <code>SmartThreadPool</code>, as explained in <a href="#Feature14">section 14</a>. When <code>Concurrency</code> is incremented, more work items are queued to the <code>SmartThreadPool</code>. When <code>Concurrency</code> is decremented, the WorkItemsGroup stops to queue work items until the number of work items in the <code>SmartThreadPool</code> of this WorkItemsGroup is lower than the WorkItemsGroups <code>Concurrency</code>.</p>

<p>In addition, <code>SmartThreadPool</code> also lets the <code>MinThreads</code> property to be changed after its creation. When <code>MinThreads</code> is created, the number of threads in the pool is raised so it will be at least <code>MinThreads</code>.</p>

<p>The number of <code>MaxThreads</code> must be greater or equal to <code>MinThreads</code>. If <code>MaxThreads</code> is set to a number lower than <code>MinThreads</code>, then <code>MinThreads</code> is also set to the new value of <code>MaxThreads</code>. And, vice versa for <code>MinThreads</code>.</p>

<li><a name="Feature18"><strong>Improved Cancel behavior</strong></a>.</li>

<p><a href="#Feature5">(See section 5)</a>.</p>

<li><a name="Feature19"><strong>Threads initialization and termination generate events</strong></a>.</li>

<p>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 <code>SmartThreadPool</code> class:</p>

<pre lang="cs">// 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;
{...}</pre>

<p>The <code>OnThreadInitialization</code> 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 code to initialize resources that are used by the thread, and should be initialized once per thread instead of once per work item.</p>

<p>The <code>OnThreadTermination</code> 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 <code>OnThreadInitialization</code> event.</p>

<li><a name="Feature20"><strong>Supports Windows CE (limited)</strong>.</a></li>

<p>The SmartThreadPool project has a similar project named SmartThreadPoolCE. This version of the SmartThreadPool can be run on Windows CE.</p>

<p>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.</p>

<p><img width="346" height="586" src="WindowsCE.jpg" /></p>

<li><a name="Feature21"><strong>SmartThreadPool and IWorkItemsGroup has the IsIdle flag</strong></a>.</li>

<p>This flag enables the user to poll the Smart Thread Pool / Work Items Group if they are idle.</p>

<li><a name="Feature22"><strong>Supports Action&lt;T&gt; and Func&lt;T&gt; (strongly typed work items)</strong></a>.</li>

<p><a href="#Feature3">(See section 3)</a>.</p>

<li><a name="Feature23"><strong>Supports Silverlight</strong></a>.</li>

<p>Add a reference to <em>SmartThreadPoolSL.dll</em> in your code and use it.</p>

<p><img width="550" height="641" src="Silverlight.jpg" /></p>

<li><a name="Feature24"><strong>Supports Mono</strong></a>.</li>

<p>Add a reference to <em>SmartThreadPoolMono.dll</em> in your code and use it.</p>

<p>Note that the binaries of Mono were compiled on Windows with Visual Studio 2008.</p>

<p><img width="550" height="344" src="Mono.jpg" /></p>

<li><a name="Feature25"><strong>Internal performance counters</strong></a>.</li>

<p>The internal performance counter should be used on platforms that don't support Performance Counters, such as Windows CE, Silverlight, and Mono.</p>

<p>The internal performance counters are variables inside STP that collect the data. To enable them, set <code>STPStartInfo.EnableLocalPerformanceCounters</code> to <code lang="cs">true</code>. I use this feature in the new demos (WindowsCE, Silverlight, and Mono).</p>

<li><a name="Feature26"><strong>Join, Choice, and Pipe</strong></a>.</li>

<p>The new methods are added to the <code>SmartThreadPool</code> class and implemented using WorkItemsGroup. Their purpose is to simplify the initiation of a parallel task.</p>

<ul>
<li><code>Join</code> - Executes several work items and waits for all of them to complete <a href="#JoinExample">(Join example)</a>.</li>

<li><code>Choice</code> - Executes several work items and returns when the first one completes <a href="#ChoiceExample">(Choice example)</a>.</li>

<li><code>Pipe</code> - Executes several work items sequentially and waits until the last work item completes <a href="#PipeExample">(Pipe example)</a>.&nbsp;&nbsp;&nbsp;&nbsp;</li></ul><li><a name="Feature27"><strong>Work item timeout (passive)</strong></a>.&nbsp;</li>

<p>This feature lets the user specify a timeout for the work item to complete, in milliseconds. When the timeout expires, the work item is cancelled automatically if it didn't complete. The cancel works the same as a call to <code>Cancel</code> with the <code>abortExecution</code> argument set to <code lang="cs">false</code> (this is why the timeout is passive).</p>

<p>To sample the cancel, use <code>SmartThreadPool.IsWorkItemCanceled</code> which is a static property, or you can use <code>SmartThreadPool.AbortOnWorkItemCancel</code> which checks if the current work item is cancelled, and if so, abort the thread (<code>Thread.CurrentThread.Abort()</code>).</p>

<p>See the timeout <a href="#TimeoutExample">example</a> below.</p>
<li><a name="Feature28"><strong>Enabled to set threads IsBackground</strong></a></li>
<p>This featue enables to set the <code>IsBackground</code> of the STP threads. The default is <code>true</code>.</p>
<p>To use it:<br />
	        </p><pre lang="cs">STPStartInfo stpStartInfo = new STPStartInfo();
stpStartInfo.AreThreadsBackground = false; 
SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
</pre>
<p>

</p><li><a name="Feature29"><strong>Enabled to set threads ApartmentState</strong></a></li>
<p>This featue enables to set the <code>ApartmentState</code> of the STP threads. The default is not to set.</p>
<p>To use it:<br />
</p><pre lang="cs">STPStartInfo stpStartInfo = new STPStartInfo();
stpStartInfo.ApartmentState = ApartmentState.MTA;
SmartThreadPool stp = new SmartThreadPool(stpStartInfo);</pre><ol><p>
</p>
</ol>
<li><a name="Feature30"><strong>Support Windows Phone (limited)</strong></a></li>
<p>Add reference to SmartThreadPoolWP.dll in your code and use it.</p>
<p><img width="404" height="749" src="WindowsPhone.png" /></p>

<li><a name="Feature31"><strong>Enabled to set thread MaxStackSize</strong></a></li>
<p>With this feature the user is able to set the MaxStackSize of the threads in the thread pool</p>
<p>To use it set the MaxStackSize member in the STPStartInfo.</p>
<p>
</p><pre lang="cs">STPStartInfo stpStartInfo = new STPStartInfo();
stpStartInfo.MaxStackSize = 1024*1024;
SmartThreadPool stp = new SmartThreadPool(stpStartInfo);
</pre>
<p>
</p></ol>

<h2>When to use?</h2>

<p>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.</p>

<h2>How to use?</h2>

<p>When the Smart Thread Pool or Work Items Group is created, it requires a few parameters; when a value is not provided, a default value is used.</p>

<table class="ArticleTable">
<thead>
<tr>
<td>Value name</td>

<td>Default value</td>

<td>Smart Thread Pool</td>

<td>Work Items Group</td>

<td>Description</td>
</tr>
</thead>

<tbody>
<tr>
<td><code>IdleTimeout</code></td>

<td>60 seconds</td>

<td>Used</td>

<td>Not used</td>

<td>Idle timeout</td>
</tr>

<tr>
<td><code>MaxWorkerThreads</code></td>

<td>25</td>

<td>Used</td>

<td>Not used</td>

<td>Maximum number of threads</td>
</tr>

<tr>
<td><code>MinWorkerThreads</code></td>

<td>0</td>

<td>Used</td>

<td>Not used</td>

<td>Minimum number of threads</td>
</tr>

<tr>
<td><code>UseCallerCallContext</code></td>

<td><code lang="cs">false</code></td>

<td>Used</td>

<td>Used</td>

<td>Use caller thread call context</td>
</tr>

<tr>
<td><code>UseCallerHttpContext</code></td>

<td><code lang="cs">false</code></td>

<td>Used</td>

<td>Used</td>

<td>Use caller thread HTTP context</td>
</tr>

<tr>
<td><code>DisposeOfStateObjects</code></td>

<td><code lang="cs">false</code></td>

<td>Used</td>

<td>Used</td>

<td>Dispose of the state objects (if the state implements <code lang="cs">IDisposable</code>)</td>
</tr>

<tr>
<td><code>CallToPostExecute</code></td>

<td><code class="SmallText">CallToPostExecute.Always</code></td>

<td>Used</td>

<td>Used</td>

<td>Call to <code>PostExecute</code></td>
</tr>

<tr>
<td><code class="SmallText">PostExecuteWorkItemCallback</code></td>

<td><code lang="cs">null</code> (Do nothing)</td>

<td>Used</td>

<td>Used</td>

<td><code>PostExecute</code> method</td>
</tr>

<tr>
<td><code>StartSuspended</code></td>

<td><code lang="cs">false</code></td>

<td>Used</td>

<td>Used</td>

<td>Start suspended</td>
</tr>

<tr>
<td><code lang="cs">FillStateWithArgs</code></td>

<td><code lang="cs">false</code></td>

<td>Used</td>

<td>Used</td>

<td>Fill state with args (<code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code>)</td>
</tr>

<tr>
<td><code lang="cs">ThreadPriority</code></td>

<td><code class="SmallText">ThreadPriority.Normal</code></td>

<td>Used</td>

<td>Not used</td>

<td>Thread priority in thread pool (not supported in Mono)</td>
</tr>

<tr>
<td><code lang="cs">WorkItemPriority</code></td>

<td><code class="SmallText">WorkItemPriority.Normal</code></td>

<td>Used</td>

<td>Used</td>

<td>Work item default priority</td>
</tr>

<tr>
<td><code class="SmallText">PerformanceCounterInstanceName</code></td>

<td><code lang="cs">null</code></td>

<td>Used</td>

<td>Not used</td>

<td>Performance counter instance name</td>
</tr>

<tr>
<td><code class="SmallText">EnableLocalPerformanceCounters</code></td>

<td><code lang="cs">false</code></td>

<td>Used</td>

<td>Not used</td>

<td>Enable local performance counters (for platforms which don't support performance counters)</td>
</tr>
</tbody>
</table>

<p>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.</p>

<h2>Code examples</h2>

<p>Creating a Smart Thread Pool instance:</p>

<pre lang="cs">SmartThreadPool smartThreadPool = 
 new SmartThreadPool(
    10*1000,    // Idle timeout in milliseconds
    25,         // Threads upper limit
    5,          // Threads lower limit
    true);      // Use caller thread context</pre>

<p>Another way to create an instance:</p>

<pre lang="cs">// 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);</pre>

<h2>Using the Smart Thread Pool</h2>

<p>The following snippet is a simple example. The user queues a work item and then gets the result. Note that the <code>Result</code> property blocks until a result is available or the work item is cancelled:<a name="SimpleExample"></a></p>

<pre lang="cs">public class SimpleExample
{
    public void DoWork(int [] numbers) 
    { 
        SmartThreadPool smartThreadPool = new SmartThreadPool();

        // Queue the work item
        IWorkItemResult&lt;double&gt; wir = smartThreadPool.QueueWorkItem(
                    new Func&lt;int[], double&gt;(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;
    }
}</pre>

<p>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:<a name="WaitForAllExample"></a></p>

<pre lang="cs">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;
    }
}</pre>

<p>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<a name="WaitForAnyExample"></a>:</p>

<pre lang="cs">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;
    }
}</pre>

<p>The following example shows the use of <code>WaitForIdle()</code>. 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:<a name="WaitForIdleExample"></a></p>

<pre lang="cs">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;
    }
}</pre>

<p>The following example shows how to handle exceptions. Pay attention to the <code>Result</code> property that throws the <code>WorkItemResultException</code> and not the real exception:<a name="CatchExceptionExample"></a></p>

<pre lang="cs">public class CatchExceptionExample
{
    public void DoWork() 
    { 
        SmartThreadPool smartThreadPool = new SmartThreadPool();

        IWorkItemResult&lt;double&gt; wir = smartThreadPool.QueueWorkItem(
                  new Func&lt;double, double, double&gt;(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;
    }
}</pre>

<p>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:<a name="GetExceptionExample"></a></p>

<pre lang="cs">public class GetExceptionExample
{
    public void DoWork() 
    { 
        SmartThreadPool smartThreadPool = new SmartThreadPool();

        IWorkItemResult&lt;double&gt; wir = smartThreadPool.QueueWorkItem(
                   new Func&lt;double, double, double&gt;(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;
    }
}</pre>

<p>The next example shows how to create a Work Items Group and use it:<a name="WorkItemsGroupExample"></a></p>

<pre lang="cs">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;
    }
}</pre>

<p>The next example shows how to create a suspended Smart Thread Pool:<a name="SuspendedSTPStartExample"></a></p>

<pre lang="cs">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;
    }
}</pre>

<p>The next example shows how to create a suspended Work Items Group:<a name="SuspendedWIGStartExample"></a></p>

<pre lang="cs">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;
    }
}</pre>

<p>This example shows how to get the Work Items Group's <code>OnIdle</code> event:<a name="OnWIGIdleEventExample"></a></p>

<pre lang="cs">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(&quot;WIG is idle&quot;);
    }
}</pre>

<p>This example shows how to use <code>Join</code>:<a name="JoinExample"></a></p>

<pre lang="cs">public class JoinExample
{
    public void DoWork() 
    { 
        SmartThreadPool stp = new SmartThreadPool();

        stp.Join(DoSomeWork1, DoSomeWork2);

        smartThreadPool.Shutdown();
    } 

    private void DoSomeWork1()
    { 
        // ...
    }

    private void DoSomeWork2()
    { 
        // ...
    }
   
}</pre>

<p>This example shows how to use <code>Choice</code>:<a name="ChoiceExample"></a></p>

<pre lang="cs">public class ChoiceExample
{
    public void DoWork() 
    { 
        SmartThreadPool stp = new SmartThreadPool();

        int index = stp.Choice(GetDataFromA, GetDataFromB);

        if (index == 0)
        {
            // Got data from A
        }
        else if (index == 1)
        {
            // Got data from B
        }

        smartThreadPool.Shutdown();
    } 

    private void GetDataFromA()
    { 
        // ...
    }

    private void GetDataFromB()
    { 
        // ...
    }
   
}</pre>

<p>This example shows how to use <code>Pipe</code>:<a name="PipeExample"></a></p>

<pre lang="cs">public class PipeExample
{
    public void DoWork() 
    { 
        SmartThreadPool stp = new SmartThreadPool();
        
        int [] data = new int[2];

        stp.Pipe(data, DoStep1, DoStep2);

        smartThreadPool.Shutdown();
    } 

    private void DoStep1(int [] data)
    { 
        data[0] = ...
    }

    private void DoStep2(int [] data)
    { 
        data[1] = ...
    }
   
}</pre>

<p>This example shows how to use <code>Timeout</code>:<a name="TimeoutExample"></a></p>

<pre lang="cs">public class TimeoutExample
{
    public void DoWork() 
    { 
        SmartThreadPool stp = new SmartThreadPool();

        ...

        // Queue a work item that will be cancelled within 5 seconds
        IWorkItemResult wir = 
            stp.QueueWorkItem(
                new WorkItemInfo() { Timeout = 5*1000 }, 
                DoSomething);
        ...

        smartThreadPool.Shutdown();
    } 

    private object DoSomething(object state)
    { 
        ...

        for(...)
        {
            // If the work item was cancelled then abort the thread.
            SmartThreadPool.AbortOnWorkItemCancel();
        }
        
        ...

        return result;
    }
}</pre>

<h2>Disclaimer</h2>

<p>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.</p>

<h2><a name="History">History</a></h2>

<ul>
<li>7<sup>th</sup> Aug, 2004: Initial version.</li>

<li>14<sup>th</sup> Sep, 2004: Bug fixes:</li>

<ol>
<li>Changed the start thread formula to '<code lang="cs">if ((InUseThreads + WaitingCallbacks) &gt; _workerThreads.Count)</code>'.</li>

<li>Fixed handle leaks.</li>
</ol>

<li>16<sup>th</sup> Oct, 2004: Added a few features:</li>

<ul>
<li>Work items return result.</li>

<li>Supports waiting synchronization for multiple work items.</li>

<li>Work items can be cancelled.</li>

<li>Passage of the caller thread's context to the thread in the pool.</li>

<li>Minimal usage of Win32 handles.</li>

<li>Minor bug fixes.</li>
</ul>

<li>26<sup>th</sup> Dec, 2004: Changes:</li>

<ul>
<li>Added <code>PostExecute</code>, with options on which cases to call it.</li>

<li>Added a <code>WaitForIdle()</code> method that waits until the work items queue is empty.</li>

<li>Added an option to dispose off the state objects.</li>

<li>Updated <code>FireUnhandledException</code> so it will be more robust.</li>

<li>Removed static constructors.</li>

<li>Added finalizers.</li>

<li>Changed exceptions so that they are serializable.</li>

<li>Fixed the bug in one of the <code>SmartThreadPool</code> constructors.</li>

<li>Changed <code>SmartThreadPool.WaitAll()</code> so that it will support any number of waiters. <code>SmartThreadPool.WaitAny()</code> is still limited by the .NET Framework.</li>

<li>Changed exception handling so if a work item throws an exception, it is re-thrown at <code>GetResult()</code>, rather than firing an <code>UnhandledException</code> event. Note that <code>PostExecute</code> exceptions are always ignored.</li>
</ul>

<li>25<sup>th</sup> Mar, 2005: Changes:</li>

<ul>
<li>Fixed a bug where the work items got lost. It happens especially when the idle timeout is small.</li>
</ul>

<li>3<sup>rd</sup> Jul, 2005: Changes:</li>

<ul>
<li>Fixed a bug where <code>Enqueue()</code> throws an exception when <code>PopWaiter()</code> returns <code lang="cs">null</code>, hardly reconstructed.</li>
</ul>

<li>16<sup>th</sup> Aug 2005: Changes:</li>

<ul>
<li>Fixed a bug where <code>InUseThreads</code> becomes negative while canceling work items.</li>
</ul>

<li>31<sup>st</sup> Jan, 2006: Changes:</li>

<ul>
<li>Added work items priority.</li>

<li>Removed support for chained delegates in callbacks and post executes (nobody really uses it).</li>

<li>Added work item groups.</li>

<li>Added work item group's idle event.</li>

<li>Changed <code>SmartThreadPool.WaitAll()</code> behavior so that when it gets an empty array, it returns <code lang="cs">true</code> rather than throwing an exception.</li>

<li>Added option to start the Smart Thread Pool and the Work Items Group as suspended.</li>

<li>Exception behavior changed (again). The real exception is returned by an inner exception.</li>

<li>Added option to keep the HTTP context of the caller thread (thanks to Steven T).</li>

<li>Added performance counters.</li>

<li>Added priority to the threads in the pool.</li>
</ul>

<li>13<sup>th</sup> Feb, 2006: Changes:</li>

<ul>
<li>Fixed the demo so that it won't crash with exception on start.</li>

<li>Added a call to dispose off the performance counters so that there is no performance counter leak.</li>

<li>Added exception catch in case the performance counters cannot be created.</li>
</ul>

<li>16<sup>th</sup> May 2008: Changes:</li>

<ul>
<li>Changed the dispose behavior and removed the finalizers.</li>

<li>Enabled the change of <code>MaxThreads</code> and <code>MinThreads</code> at run time.</li>

<li>Enabled the change of <code>Concurrency</code> of a <code>IWorkItemsGroup</code> at run time. If the <code>IWorkItemsGroup</code> is a <code>SmartThreadPool</code>, then <code>Concurrency</code> refers to <code>MaxThreads</code>.</li>

<li>Improved the Cancel behavior.</li>

<li>Added events for thread creation and termination.</li>

<li>Fixed the HttpContext context capture.</li>

<li>Changed internal collections so they use generic collections.</li>

<li>Added the IsIdle flag to the <code>SmartThreadPool</code> and <code>IWorkItemsGroup</code>.&nbsp;</li>

<li>Added support for WinCE.</li>

<li>Added support for <code>Action&lt;T&gt;</code> and <code>Func&lt;T&gt;</code>.&nbsp;</li>
</ul>

<li>7<sup>th</sup> April 2009: Changes:</li>

<ul>
<li>Added support for Silverlight and Mono.</li>

<li>Added <code>Join</code>, <code>Choice</code>, and <code>Pipe</code> to <code>SmartThreadPool</code>.</li>

<li>Added local performance counters (for Mono, Silverlight, and Windows CE).</li>

<li>Changed duration measures from <code>DateTime.Now</code> to <code>Stopwatch</code>.</li>

<li>Queues changed from <code>System.Collections.Queue</code> to <code>System.Collections.Generic.LinkedList&lt;T&gt;</code>.</li>
</ul>

<li>21<sup>st</sup> December 2009: Changes:&nbsp;</li>

<ul>
<li>Added work item timeout (passive).&nbsp;</li>
</ul>

    <li>27<sup>th</sup> August 2012: Changes</li>
        <ul>
            <li><strong>Added set name to threads</strong>&nbsp;</li>
            <li><strong>Added IsBackground option to threads</strong></li>
            <li><strong>Added ApartmentState to threads</strong></li><li><strong>Added MaxStackSize to threads</strong>&nbsp;</li>
            <li><strong>Fixed SmartThreadPool.Pipe</strong>&nbsp;</li>
            <li><strong>Fixed thread creation when queuing many work items at the same time.</strong></li>
            <li><strong>Fixed the WorkItemsQueue.Dequeue.</strong><br /><strong>
            Replaced <code>while(!Monitor.TryEnter(this));</code> with <code>lock(this) { ... }</code>&nbsp;</strong>&nbsp;</li>
        </ul>


</ul>
ï»¿
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpSmartThreadPool", "SSharpSmartThreadPool\SSharpSmartThreadPool.csproj", "{06299ED2-056A-427C-8C84-D716197080EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpSmartThreadPoolTests", "SSharpSmartThreadPoolTests\SSharpSmartThreadPoolTests.csproj", "{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpCrestronExtensionsLibrary", "..\..\Simpl# Libraries\SSharpCrestronExtensionsLibrary\SSharpCrestronExtensionsLibrary\SSharpCrestronExtensionsLibrary.csproj", "{9384CC19-3161-498C-897C-2E972C2628A4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpNUnitLiteSSharpTestRunner", "..\SSharpNUnitLite3\SSharpNUnitLiteSSharpTestRunner\SSharpNUnitLiteSSharpTestRunner.csproj", "{7E041F2A-01CA-4770-B485-BC9AE41881E0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SSharpThreadingLibrary", "..\..\Simpl# Libraries\SSharpThreadingLibrary\SSharpThreadingLibrary\SSharpThreadingLibrary.csproj", "{779FD9FC-526E-47A0-9BB8-AA86F8D75032}"
EndProject
Global
	GlobalSection(SourceCodeControl) = preSolution
		SccNumberOfProjects = 2
		SccProjectUniqueName0 = ..\\SSharpNUnitLite3\\SSharpNUnitLiteSSharpTestRunner\\SSharpNUnitLiteSSharpTestRunner.csproj
		SccProjectName0 = $/S#\u0020Pro\u0020Libraries/SSharpNUnitLite3/SSharpNUnitLiteSSharpTestRunner
		SccLocalPath0 = ..\\SSharpNUnitLite3\\SSharpNUnitLiteSSharpTestRunner
		SccProvider0 = MSSCCI:SourceGear\u0020Vault\u0020Professional\u0020Classic\u0020Client
		CanCheckoutShared = false
		SccProjectUniqueName1 = ..\\..\\Simpl#\u0020Libraries\\SSharpThreadingLibrary\\SSharpThreadingLibrary\\SSharpThreadingLibrary.csproj
		SccProjectName1 = $/S#\u0020Libraries/SSharpThreadingLibrary
		SccLocalPath1 = ..\\..\\Simpl#\u0020Libraries\\SSharpThreadingLibrary\\SSharpThreadingLibrary
		SccProvider1 = MSSCCI:SourceGear\u0020Vault\u0020Professional\u0020Classic\u0020Client
		CanCheckoutShared = false
	EndGlobalSection
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{06299ED2-056A-427C-8C84-D716197080EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{06299ED2-056A-427C-8C84-D716197080EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{06299ED2-056A-427C-8C84-D716197080EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{06299ED2-056A-427C-8C84-D716197080EB}.Release|Any CPU.Build.0 = Release|Any CPU
		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}.Release|Any CPU.Build.0 = Release|Any CPU
		{9384CC19-3161-498C-897C-2E972C2628A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{9384CC19-3161-498C-897C-2E972C2628A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{9384CC19-3161-498C-897C-2E972C2628A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{9384CC19-3161-498C-897C-2E972C2628A4}.Release|Any CPU.Build.0 = Release|Any CPU
		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{7E041F2A-01CA-4770-B485-BC9AE41881E0}.Release|Any CPU.Build.0 = Release|Any CPU
		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{779FD9FC-526E-47A0-9BB8-AA86F8D75032}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
EndGlobal

#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)

using System;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
using System.Web;
using System.Runtime.Remoting.Messaging;


namespace Amib.Threading.Internal
{
#region CallerThreadContext class

	/// <summary>
	/// This class stores the caller call context in order to restore
	/// it when the work item is executed in the thread pool environment. 
	/// </summary>
	internal class CallerThreadContext 
	{
#region Prepare reflection information

		// Cached type information.
		private static readonly MethodInfo getLogicalCallContextMethodInfo =
			typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo setLogicalCallContextMethodInfo =
			typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic);

		private static string HttpContextSlotName = GetHttpContextSlotName();

		private static string GetHttpContextSlotName()
		{
			FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic);

            if (fi != null)
            {
                return (string) fi.GetValue(null);
            }

		    return "HttpContext";
		}

        #endregion

#region Private fields

		private HttpContext _httpContext;
		private LogicalCallContext _callContext;

        #endregion

		/// <summary>
		/// Constructor
		/// </summary>
		private CallerThreadContext()
		{
		}

		public bool CapturedCallContext
		{
			get
			{
				return (null != _callContext);
			}
		}

		public bool CapturedHttpContext
		{
			get
			{
				return (null != _httpContext);
			}
		}

		/// <summary>
		/// Captures the current thread context
		/// </summary>
		/// <returns></returns>
		public static CallerThreadContext Capture(
			bool captureCallContext, 
			bool captureHttpContext)
		{
			//Debug.Assert(captureCallContext || captureHttpContext);

			CallerThreadContext callerThreadContext = new CallerThreadContext();

			// TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture()
			// Capture Call Context
			if(captureCallContext && (getLogicalCallContextMethodInfo != null))
			{
				callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null);
				if (callerThreadContext._callContext != null)
				{
					callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone();
				}
			}

			// Capture httpContext
			if (captureHttpContext && (null != HttpContext.Current))
			{
				callerThreadContext._httpContext = HttpContext.Current;
			}

			return callerThreadContext;
		}

		/// <summary>
		/// Applies the thread context stored earlier
		/// </summary>
		/// <param name="callerThreadContext"></param>
		public static void Apply(CallerThreadContext callerThreadContext)
		{
			if (null == callerThreadContext) 
			{
				throw new ArgumentNullException("callerThreadContext");			
			}

			// Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run()
			// Restore call context
			if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null))
			{
				setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext });
			}

			// Restore HttpContext 
			if (callerThreadContext._httpContext != null)
			{
                HttpContext.Current = callerThreadContext._httpContext;
				//CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext);
			}
		}
	}

    #endregion
}
#endif
namespace Amib.SSharpThreading.Internal
{
    internal class CanceledWorkItemsGroup
    {
        public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup();

        public CanceledWorkItemsGroup()
        {
            IsCanceled = false;
        }

        public bool IsCanceled { get; set; }
    }
}
#if (_WINDOWS_CE) && !SSHARP

using System;
using System.Runtime.InteropServices;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using WaitHandle = Crestron.SimplSharp.CEventHandle;

namespace Amib.Threading.Internal
{
    /// <summary>
    /// EventWaitHandle class
    /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation.
    /// So I wrote this class to implement these two methods with some of their overloads.
    /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny.
    /// Note that this class doesn't even inherit from WaitHandle!
    /// </summary>
    public class STPEventWaitHandle
    {
        #region Public Constants

        public const int WaitTimeout = Timeout.Infinite;

        #endregion

        #region Private External Constants

        private const Int32 WAIT_FAILED = -1;
        private const Int32 WAIT_TIMEOUT = 0x102;
        private const UInt32 INFINITE = 0xFFFFFFFF;

        #endregion

        #region WaitAll and WaitAny

        internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
        {
            return waitHandle.WaitOne(millisecondsTimeout, exitContext);
        }

	    private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles)
	    {
	        IntPtr[] nativeHandles = new IntPtr[waitHandles.Length];
	        for (int i = 0; i < waitHandles.Length; i++)
	        {
                nativeHandles[i] = waitHandles[i].Handle;
	        }
	        return nativeHandles;
	    }

	    public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
	    {
            uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;

            IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);

	        int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout);

            if (result == WAIT_TIMEOUT || result == WAIT_FAILED)
            {
                return false;
            }

	        return true;
	    }


	    public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
        {
            uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout;

            IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles);

            int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout);

            if (result >= 0 && result < waitHandles.Length)
            {
                return result;
            }

            return -1;
        }

        public static int WaitAny(WaitHandle[] waitHandles)
        {
            return WaitAny(waitHandles, Timeout.Infinite, false);
        }

        public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext)
        {
            int millisecondsTimeout = (int)timeout.TotalMilliseconds;

            return WaitAny(waitHandles, millisecondsTimeout, false);
        }
 
        #endregion

        #region External methods

        [DllImport("coredll.dll", SetLastError = true)]
        public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds);

        #endregion
    }
}
#endif
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

#if (_WINDOWS_CE)
using System;
using System.Runtime.InteropServices;
#endif

namespace Amib.SSharpThreading.Internal
{
    /// <summary>
    /// EventWaitHandleFactory class.
    /// This is a static class that creates AutoResetEvent and ManualResetEvent objects.
    /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property 
    /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API.
    /// Consequently this class creates the needed XxxResetEvent and replaces the handle if
    /// it's a WindowsCE OS.
    /// </summary>
    public static class EventWaitHandleFactory
    {
        /// <summary>
        /// Create a new AutoResetEvent object
        /// </summary>
        /// <returns>Return a new AutoResetEvent object</returns>
        public static AutoResetEvent CreateAutoResetEvent()
        {
            AutoResetEvent waitHandle = new AutoResetEvent(false);

#if (_WINDOWS_CE) && !SSHARP
            ReplaceEventHandle(waitHandle, false, false);
#endif

            return waitHandle;
        }

        /// <summary>
        /// Create a new ManualResetEvent object
        /// </summary>
        /// <returns>Return a new ManualResetEvent object</returns>
        public static ManualResetEvent CreateManualResetEvent(bool initialState)
        {
            ManualResetEvent waitHandle = new ManualResetEvent(initialState);

#if (_WINDOWS_CE) && !SSHARP
            ReplaceEventHandle(waitHandle, true, initialState);
#endif

            return waitHandle;
        }

#if (_WINDOWS_CE) && !SSHARP

        /// <summary>
        /// Replace the event handle
        /// </summary>
        /// <param name="waitHandle">The WaitHandle object which its handle needs to be replaced.</param>
        /// <param name="manualReset">Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false)</param>
        /// <param name="initialState">The initial state of the event</param>
        private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState)
        {
            // Store the old handle 
            IntPtr oldHandle = waitHandle.Handle;

            // Create a new event
            IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null);

            // Replace the old event with the new event
            waitHandle.Handle = newHandle;

            // Close the old event
            CloseHandle (oldHandle);        
        }

        [DllImport("coredll.dll", SetLastError = true)]
        public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);

        //Handle
        [DllImport("coredll.dll", SetLastError = true)]
        public static extern bool CloseHandle(IntPtr hObject);
#endif

    }
}
using System;
#if !(_WINDOWS_CE)
using System.Runtime.Serialization;
#endif

namespace Amib.SSharpThreading
{
    #region Exceptions

    /// <summary>
    /// Represents an exception in case IWorkItemResult.GetResult has been canceled
    /// </summary>
    public sealed partial class WorkItemCancelException : Exception
    {
        public WorkItemCancelException()
        {
        }

        public WorkItemCancelException(string message)
            : base(message)
        {
        }

        public WorkItemCancelException(string message, Exception e)
            : base(message, e)
        {
        }
    }

    /// <summary>
    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
    /// </summary>
    public sealed partial class WorkItemTimeoutException : Exception
    {
        public WorkItemTimeoutException()
        {
        }

        public WorkItemTimeoutException(string message)
            : base(message)
        {
        }

        public WorkItemTimeoutException(string message, Exception e)
            : base(message, e)
        {
        }
    }

    /// <summary>
    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
    /// </summary>
    public sealed partial class WorkItemResultException : Exception
    {
        public WorkItemResultException()
        {
        }

        public WorkItemResultException(string message)
            : base(message)
        {
        }

        public WorkItemResultException(string message, Exception e)
            : base(message, e)
        {
        }
    }


#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
    /// <summary>
    /// Represents an exception in case IWorkItemResult.GetResult has been canceled
    /// </summary>
    [Serializable]
    public sealed partial class WorkItemCancelException
    {
        public WorkItemCancelException(SerializationInfo si, StreamingContext sc)
            : base(si, sc)
        {
        }
    }

    /// <summary>
    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
    /// </summary>
    [Serializable]
    public sealed partial class WorkItemTimeoutException
    {
        public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc)
            : base(si, sc)
        {
        }
    }

    /// <summary>
    /// Represents an exception in case IWorkItemResult.GetResult has been timed out
    /// </summary>
    [Serializable]
    public sealed partial class WorkItemResultException
    {
        public WorkItemResultException(SerializationInfo si, StreamingContext sc)
            : base(si, sc)
        {
        }
    }

#endif

    #endregion
}
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

namespace Amib.SSharpThreading
	{
	#region Delegates

	/// <summary>
	/// A delegate that represents the method to run as the work item
	/// </summary>
	/// <param name="state">A state object for the method to run</param>
	public delegate object WorkItemCallback (object state);

	/// <summary>
	/// A delegate to call after the WorkItemCallback completed
	/// </summary>
	/// <param name="wir">The work item result object</param>
	public delegate void PostExecuteWorkItemCallback (IWorkItemResult wir);

	/// <summary>
	/// A delegate to call after the WorkItemCallback completed
	/// </summary>
	/// <param name="wir">The work item result object</param>
	public delegate void PostExecuteWorkItemCallback<TResult> (IWorkItemResult<TResult> wir);

	/// <summary>
	/// A delegate to call when a WorkItemsGroup becomes idle
	/// </summary>
	/// <param name="workItemsGroup">A reference to the WorkItemsGroup that became idle</param>
	public delegate void WorkItemsGroupIdleHandler (IWorkItemsGroup workItemsGroup);

	/// <summary>
	/// A delegate to call after a thread is created, but before 
	/// it's first use.
	/// </summary>
	public delegate void ThreadInitializationHandler ();

	/// <summary>
	/// A delegate to call when a thread is about to exit, after 
	/// it is no longer belong to the pool.
	/// </summary>
	public delegate void ThreadTerminationHandler ();

	#endregion

	#region WorkItem Priority

	/// <summary>
	/// Defines the availeable priorities of a work item.
	/// The higher the priority a work item has, the sooner
	/// it will be executed.
	/// </summary>
	public enum WorkItemPriority
		{
		Lowest,
		BelowNormal,
		Normal,
		AboveNormal,
		Highest,
		}

	#endregion

	#region IWorkItemsGroup interface

	/// <summary>
	/// IWorkItemsGroup interface
	/// Created by SmartThreadPool.CreateWorkItemsGroup()
	/// </summary>
	public interface IWorkItemsGroup
		{
		/// <summary>
		/// Get/Set the name of the WorkItemsGroup
		/// </summary>
		string Name { get; set; }

		/// <summary>
		/// Get/Set the maximum number of workitem that execute cocurrency on the thread pool
		/// </summary>
		int Concurrency { get; set; }

		/// <summary>
		/// Get the number of work items waiting in the queue.
		/// </summary>
		int WaitingCallbacks { get; }

		/// <summary>
		/// Get an array with all the state objects of the currently running items.
		/// The array represents a snap shot and impact performance.
		/// </summary>
		object[] GetStates ();

		/// <summary>
		/// Get the WorkItemsGroup start information
		/// </summary>
		WIGStartInfo WIGStartInfo { get; }

		/// <summary>
		/// Starts to execute work items
		/// </summary>
		void Start ();

		/// <summary>
		/// Cancel all the work items.
		/// Same as Cancel(false)
		/// </summary>
		void Cancel ();

		/// <summary>
		/// Cancel all work items using thread abortion
		/// </summary>
		/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
		void Cancel (bool abortExecution);

		/// <summary>
		/// Wait for all work item to complete.
		/// </summary>
		void WaitForIdle ();

		/// <summary>
		/// Wait for all work item to complete, until timeout expired
		/// </summary>
		/// <param name="timeout">How long to wait for the work items to complete</param>
		/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
		bool WaitForIdle (TimeSpan timeout);

		/// <summary>
		/// Wait for all work item to complete, until timeout expired
		/// </summary>
		/// <param name="millisecondsTimeout">How long to wait for the work items to complete in milliseconds</param>
		/// <returns>Returns true if work items completed within the timeout, otherwise false.</returns>
		bool WaitForIdle (int millisecondsTimeout);

		/// <summary>
		/// IsIdle is true when there are no work items running or queued.
		/// </summary>
		bool IsIdle { get; }

		/// <summary>
		/// This event is fired when all work items are completed.
		/// (When IsIdle changes to true)
		/// This event only work on WorkItemsGroup. On SmartThreadPool
		/// it throws the NotImplementedException.
		/// </summary>
		event WorkItemsGroupIdleHandler OnIdle;

		#region QueueWorkItem

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <returns>Returns a work item result</returns>        
		IWorkItemResult QueueWorkItem (WorkItemCallback callback);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="workItemPriority">The priority of the work item</param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemCallback callback, WorkItemPriority workItemPriority);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, WorkItemPriority workItemPriority);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="workItemInfo">Work item info</param>
		/// <param name="callback">A callback to execute</param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback);

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="workItemInfo">Work item information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <returns>Returns a work item result</returns>
		IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback, object state);

		#endregion

		#region QueueWorkItem(Action<...>)

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
		IWorkItemResult QueueWorkItem (Action action);
		IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
		IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg);
		IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
		IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2);
		IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
		IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3);
		IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult object, but its GetResult() will always return null</returns>
		IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
		IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);

		#endregion

		#region QueueWorkItem(Func<...>)

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
		/// its GetResult() returns a TResult object</returns>
		IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func);
		IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
		/// its GetResult() returns a TResult object</returns>
		IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg);
		IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
		/// its GetResult() returns a TResult object</returns>
		IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2);
		IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
		/// its GetResult() returns a TResult object</returns>
		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3);
		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority);

		/// <summary>
		/// Queue a work item.
		/// </summary>
		/// <returns>Returns a IWorkItemResult&lt;TResult&gt; object. 
		/// its GetResult() returns a TResult object</returns>
		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4);
		IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority);

		#endregion
		}

	#endregion

	#region CallToPostExecute enumerator

	[Flags]
	public enum CallToPostExecute
		{
		/// <summary>
		/// Never call to the PostExecute call back
		/// </summary>
		Never = 0x00,

		/// <summary>
		/// Call to the PostExecute only when the work item is cancelled
		/// </summary>
		WhenWorkItemCanceled = 0x01,

		/// <summary>
		/// Call to the PostExecute only when the work item is not cancelled
		/// </summary>
		WhenWorkItemNotCanceled = 0x02,

		/// <summary>
		/// Always call to the PostExecute
		/// </summary>
		Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled,
		}

	#endregion

	#region IWorkItemResult interface

	/// <summary>
	/// The common interface of IWorkItemResult and IWorkItemResult&lt;T&gt;
	/// </summary>
	public interface IWaitableResult
		{
		/// <summary>
		/// This method intent is for internal use.
		/// </summary>
		/// <returns></returns>
		IWorkItemResult GetWorkItemResult ();

		/// <summary>
		/// This method intent is for internal use.
		/// </summary>
		/// <returns></returns>
		IWorkItemResult<TResult> GetWorkItemResultT<TResult> ();
		}

	/// <summary>
	/// IWorkItemResult interface.
	/// Created when a WorkItemCallback work item is queued.
	/// </summary>
	public interface IWorkItemResult : IWorkItemResult<object>
		{
		}

	/// <summary>
	/// IWorkItemResult&lt;TResult&gt; interface.
	/// Created when a Func&lt;TResult&gt; work item is queued.
	/// </summary>
	public interface IWorkItemResult<TResult> : IWaitableResult
		{
		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits.
		/// </summary>
		/// <returns>The result of the work item</returns>
		TResult GetResult ();

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout.
		/// </summary>
		/// <returns>The result of the work item</returns>
		/// On timeout throws WorkItemTimeoutException
		TResult GetResult (
			int millisecondsTimeout,
			bool exitContext);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout.
		/// </summary>
		/// <returns>The result of the work item</returns>
		/// On timeout throws WorkItemTimeoutException
		TResult GetResult (
			TimeSpan timeout,
			bool exitContext);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
		/// </summary>
		/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
		/// <returns>The result of the work item</returns>
		/// On timeout throws WorkItemTimeoutException
		/// On cancel throws WorkItemCancelException
		TResult GetResult (
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
		/// </summary>
		/// <returns>The result of the work item</returns>
		/// On timeout throws WorkItemTimeoutException
		/// On cancel throws WorkItemCancelException
		TResult GetResult (
			TimeSpan timeout,
			bool exitContext,
			WaitHandle cancelWaitHandle);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits.
		/// </summary>
		/// <param name="e">Filled with the exception if one was thrown</param>
		/// <returns>The result of the work item</returns>
		TResult GetResult (out Exception e);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout.
		/// </summary>
		/// <param name="millisecondsTimeout"></param>
		/// <param name="exitContext"></param>
		/// <param name="e">Filled with the exception if one was thrown</param>
		/// <returns>The result of the work item</returns>
		/// On timeout throws WorkItemTimeoutException
		TResult GetResult (
			int millisecondsTimeout,
			bool exitContext,
			out Exception e);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout.
		/// </summary>
		/// <param name="exitContext"></param>
		/// <param name="e">Filled with the exception if one was thrown</param>
		/// <param name="timeout"></param>
		/// <returns>The result of the work item</returns>
		/// On timeout throws WorkItemTimeoutException
		TResult GetResult (
			TimeSpan timeout,
			bool exitContext,
			out Exception e);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
		/// </summary>
		/// <param name="millisecondsTimeout">Timeout in milliseconds, or -1 for infinite</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the blocking if needed</param>
		/// <param name="e">Filled with the exception if one was thrown</param>
		/// <returns>The result of the work item</returns>
		/// On timeout throws WorkItemTimeoutException
		/// On cancel throws WorkItemCancelException
		TResult GetResult (
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle,
			out Exception e);

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled.
		/// </summary>
		/// <returns>The result of the work item</returns>
		/// <param name="cancelWaitHandle"></param>
		/// <param name="e">Filled with the exception if one was thrown</param>
		/// <param name="timeout"></param>
		/// <param name="exitContext"></param>
		/// On timeout throws WorkItemTimeoutException
		/// On cancel throws WorkItemCancelException
		TResult GetResult (
			TimeSpan timeout,
			bool exitContext,
			WaitHandle cancelWaitHandle,
			out Exception e);

		/// <summary>
		/// Gets an indication whether the asynchronous operation has completed.
		/// </summary>
		bool IsCompleted { get; }

		/// <summary>
		/// Gets an indication whether the asynchronous operation has been canceled.
		/// </summary>
		bool IsCanceled { get; }

		/// <summary>
		/// Gets the user-defined object that contains context data 
		/// for the work item method.
		/// </summary>
		object State { get; }

		/// <summary>
		/// Same as Cancel(false).
		/// </summary>
		bool Cancel ();

		/// <summary>
		/// Cancel the work item execution.
		/// If the work item is in the queue then it won't execute
		/// If the work item is completed, it will remain completed
		/// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled
		///   property to check if the work item has been cancelled. If the abortExecution is set to true then
		///   the Smart Thread Pool will send an AbortException to the running thread to stop the execution 
		///   of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException.
		/// If the work item is already cancelled it will remain cancelled
		/// </summary>
		/// <param name="abortExecution">When true send an AbortException to the executing thread.</param>
		/// <returns>Returns true if the work item was not completed, otherwise false.</returns>
		bool Cancel (bool abortExecution);

		/// <summary>
		/// Get the work item's priority
		/// </summary>
		WorkItemPriority WorkItemPriority { get; }

		/// <summary>
		/// Return the result, same as GetResult()
		/// </summary>
		TResult Result { get; }

		/// <summary>
		/// Returns the exception if occured otherwise returns null.
		/// </summary>
		object Exception { get; }
		}

	#endregion

	#region .NET 3.5

#if false
	// All these delegate are built-in .NET 3.5
	// Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity.

	public delegate void Action ();
	public delegate void Action<T1, T2> (T1 arg1, T2 arg2);
	public delegate void Action<T1, T2, T3> (T1 arg1, T2 arg2, T3 arg3);
	public delegate void Action<T1, T2, T3, T4> (T1 arg1, T2 arg2, T3 arg3, T4 arg4);

	public delegate TResult Func<TResult> ();
	public delegate TResult Func<T, TResult> (T arg1);
	public delegate TResult Func<T1, T2, TResult> (T1 arg1, T2 arg2);
	public delegate TResult Func<T1, T2, T3, TResult> (T1 arg1, T2 arg2, T3 arg3);
	public delegate TResult Func<T1, T2, T3, T4, TResult> (T1 arg1, T2 arg2, T3 arg3, T4 arg4);
#endif
	#endregion
	}
ï»¿
namespace Amib.SSharpThreading.Internal
{
    /// <summary>
    /// An internal delegate to call when the WorkItem starts or completes
    /// </summary>
    internal delegate void WorkItemStateCallback(WorkItem workItem);

    internal interface IInternalWorkItemResult
    {
        event WorkItemStateCallback OnWorkItemStarted;
        event WorkItemStateCallback OnWorkItemCompleted;
    }

    internal interface IInternalWaitableResult
    {
        /// <summary>
        /// This method is intent for internal use.
        /// </summary>   
        IWorkItemResult GetWorkItemResult();
    }

    public interface IHasWorkItemPriority
    {
        WorkItemPriority WorkItemPriority { get; }
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
//using SSMono.Diagnostics;

namespace Amib.SSharpThreading.Internal
{
	#region PriorityQueue class

	/// <summary>
	/// PriorityQueue class
	/// This class is not thread safe because we use external lock
	/// </summary>
	public sealed class PriorityQueue : IEnumerable
	{
		#region Private members

		/// <summary>
		/// The number of queues, there is one for each type of priority
		/// </summary>
		private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1;

		/// <summary>
		/// Work items queues. There is one for each type of priority
		/// </summary>
        private readonly LinkedList<IHasWorkItemPriority>[] _queues = new LinkedList<IHasWorkItemPriority>[_queuesCount];

		/// <summary>
		/// The total number of work items within the queues 
		/// </summary>
		private int _workItemsCount;

		/// <summary>
		/// Use with IEnumerable interface
		/// </summary>
		private int _version;

		#endregion

		#region Contructor

		public PriorityQueue()
		{
			for(int i = 0; i < _queues.Length; ++i)
			{
                _queues[i] = new LinkedList<IHasWorkItemPriority>();
			}
		}

		#endregion

		#region Methods

		/// <summary>
		/// Enqueue a work item.
		/// </summary>
		/// <param name="workItem">A work item</param>
		public void Enqueue(IHasWorkItemPriority workItem)
		{
			//Debug.Assert(null != workItem);

			int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1;
			//Debug.Assert(queueIndex >= 0);
			//Debug.Assert(queueIndex < _queuesCount);

			_queues[queueIndex].AddLast(workItem);
			++_workItemsCount;
			++_version;
		}

		/// <summary>
		/// Dequeque a work item.
		/// </summary>
		/// <returns>Returns the next work item</returns>
		public IHasWorkItemPriority Dequeue()
		{
			IHasWorkItemPriority workItem = null;

			if(_workItemsCount > 0)
			{
				int queueIndex = GetNextNonEmptyQueue(-1);
				//Debug.Assert(queueIndex >= 0);
                workItem = _queues[queueIndex].First.Value;
				_queues[queueIndex].RemoveFirst();
				//Debug.Assert(null != workItem);
				--_workItemsCount;
				++_version;
			}

			return workItem;
		}

		/// <summary>
		/// Find the next non empty queue starting at queue queueIndex+1
		/// </summary>
		/// <param name="queueIndex">The index-1 to start from</param>
		/// <returns>
		/// The index of the next non empty queue or -1 if all the queues are empty
		/// </returns>
		private int GetNextNonEmptyQueue(int queueIndex)
		{
			for(int i = queueIndex+1; i < _queuesCount; ++i)
			{
				if(_queues[i].Count > 0)
				{
					return i;
				}
			}
			return -1;
		}

		/// <summary>
		/// The number of work items 
		/// </summary>
		public int Count
		{
			get
			{
				return _workItemsCount;
			}
		}

		/// <summary>
		/// Clear all the work items 
		/// </summary>
		public void Clear()
		{
			if (_workItemsCount > 0)
			{
				foreach(LinkedList<IHasWorkItemPriority> queue in _queues)
				{
					queue.Clear();
				}
				_workItemsCount = 0;
				++_version;
			}
		}

		#endregion

		#region IEnumerable Members

		/// <summary>
		/// Returns an enumerator to iterate over the work items
		/// </summary>
		/// <returns>Returns an enumerator</returns>
		public IEnumerator GetEnumerator()
		{
			return new PriorityQueueEnumerator(this);
		}

		#endregion

		#region PriorityQueueEnumerator

		/// <summary>
		/// The class the implements the enumerator
		/// </summary>
		private class PriorityQueueEnumerator : IEnumerator
		{
			private readonly PriorityQueue _priorityQueue;
			private int _version;
			private int _queueIndex;
			private IEnumerator _enumerator;

			public PriorityQueueEnumerator(PriorityQueue priorityQueue)
			{
				_priorityQueue = priorityQueue;
				_version = _priorityQueue._version;
				_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
				if (_queueIndex >= 0)
				{
					_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
				}
				else
				{
					_enumerator = null;
				}
			}

			#region IEnumerator Members

			public void Reset()
			{
				_version = _priorityQueue._version;
				_queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1);
				if (_queueIndex >= 0)
				{
					_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
				}
				else
				{
					_enumerator = null;
				}
			}

			public object Current
			{
				get
				{
					//Debug.Assert(null != _enumerator);
					return _enumerator.Current;
				}
			}

			public bool MoveNext()
			{
				if (null == _enumerator)
				{
					return false;
				}

				if(_version != _priorityQueue._version)
				{
					throw new InvalidOperationException("The collection has been modified");

				}
				if (!_enumerator.MoveNext())
				{
					_queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex);
					if(-1 == _queueIndex)
					{
						return false;
					}
					_enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator();
					_enumerator.MoveNext();
					return true;
				}
				return true;
			}

			#endregion
		}

		#endregion
	}

	#endregion
}
ï»¿using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyTitle ("SSharpSmartThreadPool")]
[assembly: AssemblyDescription ("S# Port of Amib Smart Thread Pool")]
[assembly: AssemblyCompany ("Nivloc Enterprises Ltd")]
[assembly: AssemblyProduct ("SSharpSmartThreadPool")]
[assembly: AssemblyVersion ("2.2.3.0")]

[assembly: InternalsVisibleTo ("SSharpSmartThreadPoolTests")]
ï»¿#if _SILVERLIGHT

using System.Threading;

namespace Amib.Threading
{
    public enum ThreadPriority
    {
        Lowest,
        BelowNormal,
        Normal,
        AboveNormal,
        Highest,
    }
}
#endif
ï»¿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.30729</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{06299ED2-056A-427C-8C84-D716197080EB}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>SSharpSmartThreadPool</RootNamespace>
    <AssemblyName>SSharpSmartThreadPool</AssemblyName>
    <ProjectTypeGuids>{0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <PlatformFamilyName>WindowsCE</PlatformFamilyName>
    <PlatformID>E2BECB1F-8C8C-41ba-B736-9BE7D946A398</PlatformID>
    <OSVersion>5.0</OSVersion>
    <DeployDirSuffix>SmartDeviceProject1</DeployDirSuffix>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <NativePlatformName>Windows CE</NativePlatformName>
    <FormFactorID>
    </FormFactorID>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE;_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <FileAlignment>512</FileAlignment>
    <NoStdLib>true</NoStdLib>
    <NoConfig>true</NoConfig>
    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
    <DebugType>none</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <FileAlignment>512</FileAlignment>
    <NoStdLib>true</NoStdLib>
    <NoConfig>true</NoConfig>
    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
    <DefineConstants>_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="mscorlib" />
    <Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="SimplSharpHelperInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="SimplSharpPro, Version=1.4.0.14, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="SSharpThreadingLibrary, Version=1.0.0.10633, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\Simpl# Libraries\SSharpThreadingLibrary\SSharpThreadingLibrary\bin\Release\SSharpThreadingLibrary.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Data" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="CallerThreadContext.cs" />
    <Compile Include="CanceledWorkItemsGroup.cs" />
    <Compile Include="EventWaitHandle.cs" />
    <Compile Include="EventWaitHandleFactory.cs" />
    <Compile Include="Exceptions.cs" />
    <Compile Include="Interfaces.cs" />
    <Compile Include="InternalInterfaces.cs" />
    <Compile Include="PriorityQueue.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="SmartThreadPool.cs" />
    <Compile Include="SmartThreadPool.ThreadEntry.cs" />
    <Compile Include="STPEventWaitHandle.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="STPPerformanceCounter.cs">
      <SubType>Code</SubType>
    </Compile>
    <Compile Include="STPStartInfo.cs" />
    <Compile Include="SynchronizedDictionary.cs" />
    <Compile Include="WIGStartInfo.cs" />
    <Compile Include="WorkItem.cs" />
    <Compile Include="WorkItem.WorkItemResult.cs" />
    <Compile Include="WorkItemFactory.cs" />
    <Compile Include="WorkItemInfo.cs" />
    <Compile Include="WorkItemResultTWrapper.cs" />
    <Compile Include="WorkItemsGroup.cs" />
    <Compile Include="WorkItemsGroupBase.cs" />
    <Compile Include="WorkItemsQueue.cs" />
    <None Include="Properties\ControlSystem.cfg" />
  </ItemGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{0B4745B0-194B-4BB6-8E21-E9057CA92300}">
        <ProgramIdTag>SSharpSmartThreadPoo</ProgramIdTag>
        <SystemName>SSharpSmartThreadPool</SystemName>
        <Programmer />
        <ArchiveFilename>C:\Projects\Crestron\Simpl# Pro Libraries\SSharpSmartThreadPool\SSharpSmartThreadPool\bin\Debug\SSharpSmartThreadPool.cplz</ArchiveFilename>
        <MinFirmwareVersion>1.009.0029</MinFirmwareVersion>
        <CompiledOn>3/14/2015 11:03:51 AM</CompiledOn>
        <AdditionalInfo />
        <EmbedSourceArchive>False</EmbedSourceArchive>
        <CopyTo />
        <OriginalReferenceSources>
          <Reference sourceFilename="../../../Simpl%23%20Libraries/SSharpThreadingLibrary/SSharpThreadingLibrary/bin/Release/SSharpThreadingLibrary.dll" referenceName="SSharpThreadingLibrary" />
        </OriginalReferenceSources>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>
  <PropertyGroup>
    <PostBuildEvent>rem S# Pro preparation will execute after these operations</PostBuildEvent>
  </PropertyGroup>
</Project>
ï»¿#if !(_WINDOWS_CE) || SSHARP

using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

namespace Amib.SSharpThreading.Internal
	{
#if _WINDOWS ||  WINDOWS_PHONE || SSHARP
	internal static class STPEventWaitHandle
		{
		public const int WaitTimeout = Timeout.Infinite;

		internal static bool WaitAll (WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
			{
			return WaitHandle.WaitAll (waitHandles, millisecondsTimeout);
			}

		internal static int WaitAny (WaitHandle[] waitHandles)
			{
			return WaitHandle.WaitAny (waitHandles);
			}

		internal static int WaitAny (WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
			{
			return WaitHandle.WaitAny (waitHandles, millisecondsTimeout);
			}

		internal static bool WaitOne (WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
			{
			return waitHandle.WaitOne (millisecondsTimeout);
			}
		}
#else
    internal static class STPEventWaitHandle
    {
        public const int WaitTimeout = Timeout.Infinite;

        internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
        {
            return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext);
        }

        internal static int WaitAny(WaitHandle[] waitHandles)
        {
            return WaitHandle.WaitAny(waitHandles);
        }

        internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
        {
            return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext);
        }

        internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext)
        {
            return waitHandle.WaitOne(millisecondsTimeout, exitContext);
        }
    }
#endif

	}

#endif
using System;
using System.Diagnostics;
using System.Threading;

namespace Amib.SSharpThreading
{
    public interface ISTPPerformanceCountersReader
    {
        long InUseThreads { get; }
        long ActiveThreads { get; }
        long WorkItemsQueued { get; }
        long WorkItemsProcessed { get; }
    }
}

namespace Amib.SSharpThreading.Internal
{
    internal interface ISTPInstancePerformanceCounters : IDisposable
    {
        void Close();
        void SampleThreads(long activeThreads, long inUseThreads);
        void SampleWorkItems(long workItemsQueued, long workItemsProcessed);
        void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime);
        void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime);
    }
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)

    internal enum STPPerformanceCounterType
	{
		// Fields
		ActiveThreads				= 0,
		InUseThreads				= 1,
		OverheadThreads				= 2,
		OverheadThreadsPercent		= 3,
		OverheadThreadsPercentBase	= 4,

		WorkItems					= 5,
		WorkItemsInQueue			= 6,
		WorkItemsProcessed			= 7,

		WorkItemsQueuedPerSecond	= 8,
		WorkItemsProcessedPerSecond	= 9,

		AvgWorkItemWaitTime			= 10,
		AvgWorkItemWaitTimeBase		= 11,

		AvgWorkItemProcessTime		= 12,
		AvgWorkItemProcessTimeBase	= 13,

		WorkItemsGroups				= 14,

		LastCounter					= 14,
	}
 

	/// <summary>
	/// Summary description for STPPerformanceCounter.
	/// </summary>
	internal class STPPerformanceCounter
	{
		// Fields
		private readonly PerformanceCounterType _pcType;
		protected string _counterHelp;
		protected string _counterName;

		// Methods
		public STPPerformanceCounter(
			string counterName, 
			string counterHelp, 
			PerformanceCounterType pcType)
		{
			_counterName = counterName;
			_counterHelp = counterHelp;
			_pcType = pcType;
		}

		public void AddCounterToCollection(CounterCreationDataCollection counterData)
		{
			CounterCreationData counterCreationData = new CounterCreationData(
				_counterName, 
				_counterHelp, 
				_pcType);

			counterData.Add(counterCreationData);
		}
 
		// Properties
		public string Name
		{
			get
			{
				return _counterName;
			}
		}
	}

	internal class STPPerformanceCounters
	{
		// Fields
		internal STPPerformanceCounter[] _stpPerformanceCounters;
		private static readonly STPPerformanceCounters _instance;
		internal const string _stpCategoryHelp = "SmartThreadPool performance counters";
		internal const string _stpCategoryName = "SmartThreadPool";

		// Methods
		static STPPerformanceCounters()
		{
			_instance = new STPPerformanceCounters();
		}
 
		private STPPerformanceCounters()
		{
			STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] 
				{ 
					new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), 
					new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), 
					new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), 
					new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), 
					new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), 

					new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), 
					new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), 
					new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), 

					new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), 
					new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), 

					new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), 
					new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), 

					new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), 
					new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), 

					new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), 
				};

			_stpPerformanceCounters = stpPerformanceCounters;
			SetupCategory();
		}
 
		private void SetupCategory()
		{
			if (!PerformanceCounterCategory.Exists(_stpCategoryName))
			{
				CounterCreationDataCollection counters = new CounterCreationDataCollection();

				for (int i = 0; i < _stpPerformanceCounters.Length; i++)
				{
					_stpPerformanceCounters[i].AddCounterToCollection(counters);
				}

				PerformanceCounterCategory.Create(
					_stpCategoryName, 
					_stpCategoryHelp, 
                    PerformanceCounterCategoryType.MultiInstance,
					counters);
					
			}
		}
 
		// Properties
		public static STPPerformanceCounters Instance
		{
			get
			{
				return _instance;
			}
		}
 	}

	internal class STPInstancePerformanceCounter : IDisposable
	{
		// Fields
        private bool _isDisposed;
		private PerformanceCounter _pcs;

		// Methods
		protected STPInstancePerformanceCounter()
		{
            _isDisposed = false;
		}

		public STPInstancePerformanceCounter(
			string instance, 
			STPPerformanceCounterType spcType) : this()
		{
			STPPerformanceCounters counters = STPPerformanceCounters.Instance;
			_pcs = new PerformanceCounter(
				STPPerformanceCounters._stpCategoryName, 
				counters._stpPerformanceCounters[(int) spcType].Name, 
				instance, 
				false);
			_pcs.RawValue = _pcs.RawValue;
		}


		public void Close()
		{
			if (_pcs != null)
			{
				_pcs.RemoveInstance();
				_pcs.Close();
				_pcs = null;
			}
		}
 
		public void Dispose()
		{
            Dispose(true);
		}

        public virtual void Dispose(bool disposing)
        {
            if (!_isDisposed)
            {
                if (disposing)
                {
                    Close();
                }
            }
            _isDisposed = true;
        }
 
		public virtual void Increment()
		{
			_pcs.Increment();
		}
 
		public virtual void IncrementBy(long val)
		{
			_pcs.IncrementBy(val);
		}

		public virtual void Set(long val)
		{
			_pcs.RawValue = val;
		}
	}

	internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter
	{
		// Methods
		public override void Increment() {}
		public override void IncrementBy(long value) {}
		public override void Set(long val) {}
	}



	internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters
	{
        private bool _isDisposed;
		// Fields
		private STPInstancePerformanceCounter[] _pcs;
		private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter;

		// Methods
		static STPInstancePerformanceCounters()
		{
			_stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter();
		}
 
		public STPInstancePerformanceCounters(string instance)
		{
            _isDisposed = false;
			_pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter];

            // 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)
				{
					_pcs[i] = new STPInstancePerformanceCounter(
						instance, 
						(STPPerformanceCounterType) i);
				}
				else
				{
					_pcs[i] = _stpInstanceNullPerformanceCounter;
				}
			}
		}
 

		public void Close()
		{
			if (null != _pcs)
			{
				for (int i = 0; i < _pcs.Length; i++)
				{
                    if (null != _pcs[i])
                    {
                        _pcs[i].Dispose();
                    }
				}
				_pcs = null;
			}
		}

		public void Dispose()
		{
            Dispose(true);
		}

        public virtual void Dispose(bool disposing)
        {
            if (!_isDisposed)
            {
                if (disposing)
                {
                    Close();
                }
            }
            _isDisposed = true;
        }
 
		private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType)
		{
			return _pcs[(int) spcType];
		}

		public void SampleThreads(long activeThreads, long inUseThreads)
		{
			GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads);
			GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads);
			GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads);

			GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads);
			GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads);
		}

		public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
		{
			GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed);
			GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued);
			GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed);

			GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued);
			GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed);
		}

		public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
		{
			GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds);
			GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment();
		}

		public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
		{
			GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds);
			GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment();
		}
    }
#endif

    internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
	{
		private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters();

		public static NullSTPInstancePerformanceCounters Instance
		{
			get { return _instance; }
		}

 		public void Close() {}
		public void Dispose() {}
 
		public void SampleThreads(long activeThreads, long inUseThreads) {}
		public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {}
		public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {}
		public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {}
        public long InUseThreads
        {
            get { return 0; }
        }

        public long ActiveThreads
        {
            get { return 0; }
        }

        public long WorkItemsQueued
        {
            get { return 0; }
        }

        public long WorkItemsProcessed
        {
            get { return 0; }
        }
	}

    internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader
    {
        public void Close() { }
        public void Dispose() { }

        private long _activeThreads;
        private long _inUseThreads;
        private long _workItemsQueued;
        private long _workItemsProcessed;

        public long InUseThreads
        {
            get { return _inUseThreads; }
        }

        public long ActiveThreads
        {
            get { return _activeThreads; }
        }

        public long WorkItemsQueued
        {
            get { return _workItemsQueued; }
        }

        public long WorkItemsProcessed
        {
            get { return _workItemsProcessed; }
        }

        public void SampleThreads(long activeThreads, long inUseThreads)
        {
            _activeThreads = activeThreads;
            _inUseThreads = inUseThreads;
        }

        public void SampleWorkItems(long workItemsQueued, long workItemsProcessed)
        {
            _workItemsQueued = workItemsQueued;
            _workItemsProcessed = workItemsProcessed;
        }

        public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime)
        {
            // Not supported
        }

        public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime)
        {
            // Not supported
        }
    }
}
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;
using WaitHandle = Crestron.SimplSharp.CEventHandle;

namespace Amib.SSharpThreading
{
	/// <summary>
	/// Summary description for STPStartInfo.
	/// </summary>
    public class STPStartInfo : WIGStartInfo
    {
        private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
        private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
        private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
#if !(WINDOWS_PHONE)
        private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority;
#endif
        private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
        private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground;
        private bool _enableLocalPerformanceCounters;
        private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName;
        private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize;

        public STPStartInfo()
        {
            _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName;
#if !(WINDOWS_PHONE)
            _threadPriority = SmartThreadPool.DefaultThreadPriority;
#endif
            _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads;
            _idleTimeout = SmartThreadPool.DefaultIdleTimeout;
            _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads;
        }

	    public STPStartInfo(STPStartInfo stpStartInfo)
            : base(stpStartInfo)
        {
            _idleTimeout = stpStartInfo.IdleTimeout;
            _minWorkerThreads = stpStartInfo.MinWorkerThreads;
            _maxWorkerThreads = stpStartInfo.MaxWorkerThreads;
#if !(WINDOWS_PHONE)
            _threadPriority = stpStartInfo.ThreadPriority;
#endif
            _performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName;
            _enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters;
            _threadPoolName = stpStartInfo._threadPoolName;
            _areThreadsBackground = stpStartInfo.AreThreadsBackground;
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)
            _apartmentState = stpStartInfo._apartmentState;
#endif
        }
	  
	    /// <summary>
	    /// Get/Set the idle timeout in milliseconds.
	    /// If a thread is idle (starved) longer than IdleTimeout then it may quit.
	    /// </summary>
	    public virtual int IdleTimeout
	    {
	        get { return _idleTimeout; }
	        set 
            {
                ThrowIfReadOnly();
                _idleTimeout = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set the lower limit of threads in the pool.
	    /// </summary>
	    public virtual int MinWorkerThreads
	    {
	        get { return _minWorkerThreads; }
	        set 
            {
                ThrowIfReadOnly();
                _minWorkerThreads = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set the upper limit of threads in the pool.
	    /// </summary>
	    public virtual int MaxWorkerThreads
	    {
	        get { return _maxWorkerThreads; }
	        set 
            {
                ThrowIfReadOnly();
                _maxWorkerThreads = value; 
            }
	    }

#if !(WINDOWS_PHONE)
	    /// <summary>
	    /// Get/Set the scheduling priority of the threads in the pool.
	    /// The Os handles the scheduling.
	    /// </summary>
	    public virtual ThreadPriority ThreadPriority
	    {
	        get { return _threadPriority; }
	        set 
            {
                ThrowIfReadOnly();
                _threadPriority = value; 
            }
	    }
#endif
        /// <summary>
        /// Get/Set the thread pool name. Threads will get names depending on this.
        /// </summary>
        public virtual string ThreadPoolName {
            get { return _threadPoolName; }
            set
            {
                ThrowIfReadOnly ();
                _threadPoolName = value;
            }
        }

	    /// <summary>
	    /// Get/Set the performance counter instance name of this SmartThreadPool
	    /// The default is null which indicate not to use performance counters at all.
	    /// </summary>
	    public virtual string PerformanceCounterInstanceName
	    {
	        get { return _performanceCounterInstanceName; }
	        set 
            {
                ThrowIfReadOnly();
                _performanceCounterInstanceName = value; 
            }
	    }
        /// <summary>
        /// Enable/Disable the local performance counter.
        /// This enables the user to get some performance information about the SmartThreadPool 
        /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.)
        /// The default is false.
        /// </summary>
        public virtual bool EnableLocalPerformanceCounters
	    {
	        get { return _enableLocalPerformanceCounters; }
	        set 
			{ 
				ThrowIfReadOnly(); 
				_enableLocalPerformanceCounters = value; 
			}
	    }

        /// <summary>
        /// Get/Set backgroundness of thread in thread pool.
        /// </summary>
	    public virtual bool AreThreadsBackground
 	    {
 	        get { return _areThreadsBackground; }
 	        set
 	        {
 	            ThrowIfReadOnly ();
 	            _areThreadsBackground = value;
 	        }
 	    }

	    /// <summary>
        /// Get a readonly version of this STPStartInfo.
        /// </summary>
        /// <returns>Returns a readonly reference to this STPStartInfo</returns>
        public new STPStartInfo AsReadOnly()
        {
            return new STPStartInfo(this) { _readOnly = true };
        }

#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)

        private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState;

        /// <summary>
        /// Get/Set the apartment state of threads in the thread pool
        /// </summary>
        public ApartmentState ApartmentState
        {
            get { return _apartmentState; }
            set
            {
                ThrowIfReadOnly();
                _apartmentState = value;
            }
        } 

#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
        
        /// <summary>
        /// Get/Set the max stack size of threads in the thread pool
        /// </summary>
        public int? MaxStackSize
        {
            get { return _maxStackSize; }
            set
            {
                ThrowIfReadOnly();
                if (value.HasValue && value.Value < 0)
                {
                    throw new ArgumentOutOfRangeException("value", "Value must be greater than 0.");
                }
                _maxStackSize = value;
            }
        }
#endif

#endif
    }
}
ï»¿
using System;
using Amib.SSharpThreading.Internal;

namespace Amib.SSharpThreading
{
    public partial class SmartThreadPool
    {
        #region ThreadEntry class

        internal class ThreadEntry
        {
            /// <summary>
            /// The thread creation time
            /// The value is stored as UTC value.
            /// </summary>
            private readonly DateTime _creationTime;

            /// <summary>
            /// The last time this thread has been running
            /// It is updated by IAmAlive() method
            /// The value is stored as UTC value.
            /// </summary>
            private DateTime _lastAliveTime;

            /// <summary>
            /// A reference from each thread in the thread pool to its SmartThreadPool
            /// object container.
            /// With this variable a thread can know whatever it belongs to a 
            /// SmartThreadPool.
            /// </summary>
            private readonly SmartThreadPool _associatedSmartThreadPool;

            /// <summary>
            /// A reference to the current work item a thread from the thread pool 
            /// is executing.
            /// </summary>            
            public WorkItem CurrentWorkItem { get; set; }

            public ThreadEntry(SmartThreadPool stp)
            {
                _associatedSmartThreadPool = stp;
                _creationTime = DateTime.UtcNow;
                _lastAliveTime = DateTime.MinValue;
            }

            public SmartThreadPool AssociatedSmartThreadPool
            {
                get { return _associatedSmartThreadPool; }
            }

            public void IAmAlive()
            {
                _lastAliveTime = DateTime.UtcNow;
            }
        }

        #endregion
    }
}
#region Release History

// Smart Thread Pool
// 7 Aug 2004 - Initial release
//
// 14 Sep 2004 - Bug fixes 
//
// 15 Oct 2004 - Added new features
//		- Work items return result.
//		- Support 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.
//
// 26 Dec 2004 - Changes:
//		- Removed static constructors.
//      - Added finalizers.
//		- Changed Exceptions so they are serializable.
//		- Fixed the bug in one of the SmartThreadPool constructors.
//		- Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. 
//        The SmartThreadPool.WaitAny() is still limited by the .NET Framework.
//		- Added PostExecute with options on which cases to call it.
//      - Added option to dispose of the state objects.
//      - Added a WaitForIdle() method that waits until the work items queue is empty.
//      - Added an STPStartInfo class for the initialization of the thread pool.
//      - Changed exception handling so if a work item throws an exception it 
//        is rethrown at GetResult(), rather then firing an UnhandledException event.
//        Note that PostExecute exception are always ignored.
//
// 25 Mar 2005 - Changes:
//		- Fixed lost of work items bug
//
// 3 Jul 2005: Changes.
//      - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. 
//
// 16 Aug 2005: Changes.
//		- Fixed bug where the InUseThreads becomes negative when canceling work items. 
//
// 31 Jan 2006 - Changes:
//		- Added work items priority
//		- Removed support of chained delegates in callbacks and post executes (nobody really use this)
//		- Added work items groups
//		- Added work items groups idle event
//		- Changed SmartThreadPool.WaitAll() behavior so when it gets empty array
//		  it returns true rather then throwing an exception.
//		- Added option to start the STP and the WIG as suspended
//		- Exception behavior changed, 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
//
// 13 Feb 2006 - Changes:
//		- Added a call to the dispose of the Performance Counter so
//		  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<T> and Func<T>
//
// 07 April 2009 - Changes:
//      - Added support for Silverlight and Mono
//      - Added Join, Choice, and Pipe to SmartThreadPool.
//      - Added local performance counters (for Mono, Silverlight, and WindowsCE)
//      - Changed duration measures from DateTime.Now to Stopwatch.
//      - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList<T>.
//
// 21 December 2009 - Changes:
//      - Added work item timeout (passive)
//
// 20 August 2012 - Changes:
//      - Added set name to threads
//      - Fixed the WorkItemsQueue.Dequeue. 
//        Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... }
//      - Fixed SmartThreadPool.Pipe
//      - Added IsBackground option to threads
//      - Added ApartmentState to threads
//      - Fixed thread creation when queuing many work items at the same time.
//
// 24 August 2012 - Changes:
//      - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan
//      - Added option to set MaxStackSize of threads 

#endregion

using System;
using System.Security;
using Amib.SSharpThreading.Internal;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;
using SSharp.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
//using SSMono.Diagnostics;
using ThreadAbortException = System.Threading.ThreadAbortException;
using ThreadStateException = System.Threading.ThreadStateException;

namespace Amib.SSharpThreading
	{
	#region SmartThreadPool class
	/// <summary>
	/// Smart thread pool class.
	/// </summary>
	public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable
		{
		#region Public Default Constants

		/// <summary>
		/// Default minimum number of threads the thread pool contains. (0)
		/// </summary>
		public const int DefaultMinWorkerThreads = 0;

		/// <summary>
		/// Default maximum number of threads the thread pool contains. (25)
		/// </summary>
		public const int DefaultMaxWorkerThreads = 25;

		/// <summary>
		/// Default idle timeout in milliseconds. (One minute)
		/// </summary>
		public const int DefaultIdleTimeout = 60 * 1000; // One minute

		/// <summary>
		/// Indicate to copy the security context of the caller and then use it in the call. (false)
		/// </summary>
		public const bool DefaultUseCallerCallContext = false;

		/// <summary>
		/// Indicate to copy the HTTP context of the caller and then use it in the call. (false)
		/// </summary>
		public const bool DefaultUseCallerHttpContext = false;

		/// <summary>
		/// Indicate to dispose of the state objects if they support the IDispose interface. (false)
		/// </summary>
		public const bool DefaultDisposeOfStateObjects = false;

		/// <summary>
		/// The default option to run the post execute (CallToPostExecute.Always)
		/// </summary>
		public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always;

		/// <summary>
		/// The default post execute method to run. (None)
		/// When null it means not to call it.
		/// </summary>
		public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback;

		/// <summary>
		/// The default work item priority (WorkItemPriority.Normal)
		/// </summary>
		public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal;

		/// <summary>
		/// The default is to work on work items as soon as they arrive
		/// and not to wait for the start. (false)
		/// </summary>
		public const bool DefaultStartSuspended = false;

		/// <summary>
		/// The default name to use for the performance counters instance. (null)
		/// </summary>
		public static readonly string DefaultPerformanceCounterInstanceName;

#if !(WINDOWS_PHONE)

		/// <summary>
		/// The default thread priority (ThreadPriority.Normal)
		/// </summary>

		public const ThreadPriority DefaultThreadPriority = ThreadPriority.MediumPriority;
#endif
		/// <summary>
		/// The default thread pool name. (SmartThreadPool)
		/// </summary>
		public const string DefaultThreadPoolName = "SmartThreadPool";

		/// <summary>
		/// The default Max Stack Size. (SmartThreadPool)
		/// </summary>
		public static readonly int? DefaultMaxStackSize = null;

		/// <summary>
		/// The default fill state with params. (false)
		/// It is relevant only to QueueWorkItem of Action&lt;...&gt;/Func&lt;...&gt;
		/// </summary>
		public const bool DefaultFillStateWithArgs = false;

		/// <summary>
		/// The default thread backgroundness. (true)
		/// </summary>
		public const bool DefaultAreThreadsBackground = true;

#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)
        /// <summary>
        /// The default apartment state of a thread in the thread pool. 
        /// The default is ApartmentState.Unknown which means the STP will not 
        /// set the apartment of the thread. It will use the .NET default.
        /// </summary>
        public const ApartmentState DefaultApartmentState = ApartmentState.Unknown;
#endif

		#endregion

		#region Member Variables

		/// <summary>
		/// Dictionary of all the threads in the thread pool.
		/// </summary>
		private readonly SynchronizedDictionary<Thread, ThreadEntry> _workerThreads = new SynchronizedDictionary<Thread, ThreadEntry> ();

		/// <summary>
		/// Queue of work items.
		/// </summary>
		private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue ();

		/// <summary>
		/// Count the work items handled.
		/// Used by the performance counter.
		/// </summary>
		private int _workItemsProcessed;

		/// <summary>
		/// Number of threads that currently work (not idle).
		/// </summary>
		private int _inUseWorkerThreads;

		/// <summary>
		/// Stores a copy of the original STPStartInfo.
		/// It is used to change the MinThread and MaxThreads
		/// </summary>
		private STPStartInfo _stpStartInfo;

		/// <summary>
		/// 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.
		/// </summary>
		private int _currentWorkItemsCount;

		/// <summary>
		/// Signaled when the thread pool is idle, i.e. no thread is busy
		/// and the work items queue is empty
		/// </summary>
		//private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
		private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent (true);

		/// <summary>
		/// An event to signal all the threads to quit immediately.
		/// </summary>
		//private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false);
		private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent (false);

		/// <summary>
		/// A flag to indicate if the Smart Thread Pool is now suspended.
		/// </summary>
		private bool _isSuspended;

		/// <summary>
		/// A flag to indicate the threads to quit.
		/// </summary>
		private bool _shutdown;

		/// <summary>
		/// Counts the threads created in the pool.
		/// It is used to name the threads.
		/// </summary>
		private int _threadCounter;

		/// <summary>
		/// Indicate that the SmartThreadPool has been disposed
		/// </summary>
		private bool _isDisposed;

		/// <summary>
		/// Holds all the WorkItemsGroup instaces that have at least one 
		/// work item int the SmartThreadPool
		/// This variable is used in case of Shutdown
		/// </summary>
		private readonly SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup> _workItemsGroups = new SynchronizedDictionary<IWorkItemsGroup, IWorkItemsGroup> ();

		/// <summary>
		/// A common object for all the work items int the STP
		/// so we can mark them to cancel in O(1)
		/// </summary>
		private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup ();

		/// <summary>
		/// Windows STP performance counters
		/// </summary>
		private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance;

		/// <summary>
		/// Local STP performance counters
		/// </summary>
		private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance;

#if (WINDOWS_PHONE) 
        private static readonly Dictionary<int, ThreadEntry> _threadEntries = new Dictionary<int, ThreadEntry>();
#elif (_WINDOWS_CE)
		private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot ();
#else
        [ThreadStatic]
        private static ThreadEntry _threadEntry;

#endif

		/// <summary>
		/// An event to call after a thread is created, but before 
		/// it's first use.
		/// </summary>
		private event ThreadInitializationHandler _onThreadInitialization;

		/// <summary>
		/// An event to call when a thread is about to exit, after 
		/// it is no longer belong to the pool.
		/// </summary>
		private event ThreadTerminationHandler _onThreadTermination;

		#endregion

		#region Per thread properties

		/// <summary>
		/// A reference to the current work item a thread from the thread pool 
		/// is executing.
		/// </summary>
		internal static ThreadEntry CurrentThreadEntry
			{
#if (WINDOWS_PHONE)
            get
            {
                lock(_threadEntries)
                {
                    ThreadEntry threadEntry;
                    if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry))
                    {
                        return threadEntry;
                    }
                }
                return null;
            }
            set
            {
                lock(_threadEntries)
                {
                    _threadEntries[Thread.CurrentThread.ManagedThreadId] = value;
                }
            }
#elif (_WINDOWS_CE)
			get
				{
				//Thread.CurrentThread.ManagedThreadId
				return Thread.GetData (_threadEntrySlot) as ThreadEntry;
				}
			set
				{
				Thread.SetData (_threadEntrySlot, value);
				}
#else
            get
            {
                return _threadEntry;
            }
            set
            {
                _threadEntry = value;
            }
#endif
			}
		#endregion

		#region Construction and Finalization

		/// <summary>
		/// Constructor
		/// </summary>
		public SmartThreadPool ()
			{
			_stpStartInfo = new STPStartInfo ();
			Initialize ();
			}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="idleTimeout">Idle timeout in milliseconds</param>
		public SmartThreadPool (int idleTimeout)
			{
			_stpStartInfo = new STPStartInfo
			{
				IdleTimeout = idleTimeout,
			};
			Initialize ();
			}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="idleTimeout">Idle timeout in milliseconds</param>
		/// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
		public SmartThreadPool (
			int idleTimeout,
			int maxWorkerThreads)
			{
			_stpStartInfo = new STPStartInfo
			{
				IdleTimeout = idleTimeout,
				MaxWorkerThreads = maxWorkerThreads,
			};
			Initialize ();
			}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="idleTimeout">Idle timeout in milliseconds</param>
		/// <param name="maxWorkerThreads">Upper limit of threads in the pool</param>
		/// <param name="minWorkerThreads">Lower limit of threads in the pool</param>
		public SmartThreadPool (
			int idleTimeout,
			int maxWorkerThreads,
			int minWorkerThreads)
			{
			_stpStartInfo = new STPStartInfo
			{
				IdleTimeout = idleTimeout,
				MaxWorkerThreads = maxWorkerThreads,
				MinWorkerThreads = minWorkerThreads,
			};
			Initialize ();
			}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="stpStartInfo">A SmartThreadPool configuration that overrides the default behavior</param>
		public SmartThreadPool (STPStartInfo stpStartInfo)
			{
			_stpStartInfo = new STPStartInfo (stpStartInfo);
			Initialize ();
			}

		private void Initialize ()
			{
			Name = _stpStartInfo.ThreadPoolName;
			ValidateSTPStartInfo ();

			// _stpStartInfoRW stores a read/write copy of the STPStartInfo.
			// Actually only MaxWorkerThreads and MinWorkerThreads are overwritten

			_isSuspended = _stpStartInfo.StartSuspended;

#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE)
			if (null != _stpStartInfo.PerformanceCounterInstanceName)
				{
				throw new NotSupportedException ("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters");
				}
#else
            if (null != _stpStartInfo.PerformanceCounterInstanceName)
            {
                try
                {
                    _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName);
                }
                catch (Exception e)
                {
                    //Debug.WriteLine("Unable to create Performance Counters: " + e);
                    _windowsPCs = NullSTPInstancePerformanceCounters.Instance;
                }
            }
#endif

			if (_stpStartInfo.EnableLocalPerformanceCounters)
				{
				_localPCs = new LocalSTPInstancePerformanceCounters ();
				}

			// 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);
			threadsCount -= _workerThreads.Count;
			if (threadsCount > 0)
				{
				StartThreads (threadsCount);
				}
			}

		private void ValidateSTPStartInfo ()
			{
			if (_stpStartInfo.MinWorkerThreads < 0)
				{
				throw new ArgumentOutOfRangeException (
					"MinWorkerThreads", "MinWorkerThreads cannot be negative");
				}

			if (_stpStartInfo.MaxWorkerThreads <= 0)
				{
				throw new ArgumentOutOfRangeException (
					"MaxWorkerThreads", "MaxWorkerThreads must be greater than zero");
				}

			if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads)
				{
				throw new ArgumentOutOfRangeException (
					"MinWorkerThreads, maxWorkerThreads",
					"MaxWorkerThreads must be greater or equal to MinWorkerThreads");
				}
			}

		private static void ValidateCallback (Delegate callback)
			{
			if (callback.GetInvocationList ().Length > 1)
				{
				throw new NotSupportedException ("SmartThreadPool doesn't support delegates chains");
				}
			}

		#endregion

		#region Thread Processing

		/// <summary>
		/// Waits on the queue for a work item, shutdown, or timeout.
		/// </summary>
		/// <returns>
		/// Returns the WaitingCallback or null in case of timeout or shutdown.
		/// </returns>
		private WorkItem Dequeue ()
			{
			WorkItem workItem =
				_workItemsQueue.DequeueWorkItem (_stpStartInfo.IdleTimeout, _shuttingDownEvent);

			return workItem;
			}

		/// <summary>
		/// Put a new work item in the queue
		/// </summary>
		/// <param name="workItem">A work item to queue</param>
		internal override void Enqueue (WorkItem workItem)
			{
			// Make sure the workItem is not null
			//Debug.Assert (null != workItem);

			IncrementWorkItemsCount ();

			workItem.CanceledSmartThreadPool = _canceledSmartThreadPool;
			_workItemsQueue.EnqueueWorkItem (workItem);
			workItem.WorkItemIsQueued ();

			// If all the threads are busy then try to create a new one
			if (_currentWorkItemsCount > _workerThreads.Count)
				{
				StartThreads (1);
				}
			}

		private void IncrementWorkItemsCount ()
			{
			_windowsPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
			_localPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
			int count = Interlocked.Increment (ref _currentWorkItemsCount);
			//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
			if (count == 1)
				{
				IsIdle = false;
				_isIdleWaitHandle.Reset ();
				}
			}

		private void DecrementWorkItemsCount ()
			{
			int count = Interlocked.Decrement (ref _currentWorkItemsCount);
			//Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString());
			if (count == 0)
				{
				IsIdle = true;
				_isIdleWaitHandle.Set ();
				}

			Interlocked.Increment (ref _workItemsProcessed);

			if (!_shutdown)
				{
				// The counter counts even if the work item was cancelled
				_windowsPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
				_localPCs.SampleWorkItems (_workItemsQueue.Count, _workItemsProcessed);
				}

			}

		internal void RegisterWorkItemsGroup (IWorkItemsGroup workItemsGroup)
			{
			_workItemsGroups[workItemsGroup] = workItemsGroup;
			}

		internal void UnregisterWorkItemsGroup (IWorkItemsGroup workItemsGroup)
			{
			if (_workItemsGroups.Contains (workItemsGroup))
				{
				_workItemsGroups.Remove (workItemsGroup);
				}
			}

		/// <summary>
		/// Inform that the current thread is about to quit or quiting.
		/// The same thread may call this method more than once.
		/// </summary>
		private void InformCompleted ()
			{
			// There is no need to lock the two methods together 
			// since only the current thread removes itself
			// and the _workerThreads is a synchronized dictionary
			if (_workerThreads.Contains (Thread.CurrentThread))
				{
				_workerThreads.Remove (Thread.CurrentThread);
				_windowsPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
				_localPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
				}
			}

		/// <summary>
		/// Starts new threads
		/// </summary>
		/// <param name="threadsCount">The number of threads to start</param>
		private void StartThreads (int threadsCount)
			{
			if (_isSuspended)
				{
				return;
				}

			lock (_workerThreads.SyncRoot)
				{
				// Don't start threads on shut down
				if (_shutdown)
					{
					return;
					}

				for (int i = 0; i < threadsCount; ++i)
					{
					// Don't create more threads then the upper limit
					if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads)
						{
						return;
						}

					// Create a new thread

#if (_SILVERLIGHT) || (WINDOWS_PHONE) || (_WINDOWS_CE)
#if SSHARP
					var te = new ThreadEntry (this);

					Thread workerThread = new Thread (ProcessQueuedItems, te, Thread.eThreadStartOptions.CreateSuspended);
#else
					Thread workerThread = new Thread (ProcessQueuedItems, null);
#endif
#else
                    Thread workerThread =
                        _stpStartInfo.MaxStackSize.HasValue
                        ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value)
                        : new Thread(ProcessQueuedItems);
#endif
					// Configure the new thread and start it
					workerThread.Name = "STP " + Name + " Thread #" + _threadCounter;
#if !SSHARP
					workerThread.IsBackground = _stpStartInfo.AreThreadsBackground;
#endif

#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE)
                    if (_stpStartInfo.ApartmentState != ApartmentState.Unknown)
                    {
                        workerThread.SetApartmentState(_stpStartInfo.ApartmentState);
                    }
#endif

#if !(_SILVERLIGHT) && !(WINDOWS_PHONE)
					workerThread.Priority = _stpStartInfo.ThreadPriority;
#endif
					workerThread.Start ();
					++_threadCounter;

#if SSHARP
					_workerThreads.AddPendingEntry ();
#else
					// Add it to the dictionary and update its creation time.
					_workerThreads[workerThread] = new ThreadEntry (this);
#endif

					_windowsPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
					_localPCs.SampleThreads (_workerThreads.Count, _inUseWorkerThreads);
					}
				}
			}

		/// <summary>
		/// A worker thread method that processes work items from the work items queue.
		/// </summary>
		private object ProcessQueuedItems (object state)
			{
#if SSHARP
			var te = (ThreadEntry)state;
			_workerThreads[Thread.CurrentThread] = te;
			_workerThreads.RemovePendingEntry ();
#endif

			// Keep the entry of the dictionary as thread's variable to avoid the synchronization locks
			// of the dictionary.
			CurrentThreadEntry = _workerThreads[Thread.CurrentThread];

			FireOnThreadInitialization ();

			try
				{
				bool bInUseWorkerThreadsWasIncremented = false;

				// Process until shutdown.
				while (!_shutdown)
					{
					// Update the last time this thread was seen alive.
					// It's good for debugging.
					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.
					CurrentThreadEntry.IAmAlive ();

					// On timeout or shut down.
					if (null == workItem)
						{
						// Double lock for quit.
						if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
							{
							lock (_workerThreads.SyncRoot)
								{
								if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads)
									{
									// 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;
									}
								}
							}
						}

					// If we didn't quit then skip to the next iteration.
					if (null == workItem)
						{
						continue;
						}

					try
						{
						// 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.
						// The cancel mechanism doesn't delete items from the queue,  
						// it marks the work item as canceled, and when the work item
						// is dequeued, we just skip it.
						// If the post execute of work item is set to always or to
						// call when the work item is canceled then the StartingWorkItem()
						// will return true, so the post execute can run.
						if (!workItem.StartingWorkItem ())
							{
							continue;
							}

						// Execute the callback.  Make sure to accurately
						// record how many callbacks are currently executing.
						int inUseWorkerThreads = Interlocked.Increment (ref _inUseWorkerThreads);
						_windowsPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);
						_localPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);

						// Mark that the _inUseWorkerThreads incremented, so in the finally{}
						// statement we will decrement it correctly.
						bInUseWorkerThreadsWasIncremented = true;

						workItem.FireWorkItemStarted ();

						ExecuteWorkItem (workItem);
						}
					catch (Exception ex)
						{
						ex.GetHashCode ();
						// Do nothing
						}
					finally
						{
						workItem.DisposeOfState ();

						// Set the CurrentWorkItem to null, since we 
						// no longer run user's code.
						CurrentThreadEntry.CurrentWorkItem = null;

						// Decrement the _inUseWorkerThreads only if we had 
						// incremented it. Note the cancelled work items don't
						// increment _inUseWorkerThreads.
						if (bInUseWorkerThreadsWasIncremented)
							{
							int inUseWorkerThreads = Interlocked.Decrement (ref _inUseWorkerThreads);
							_windowsPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);
							_localPCs.SampleThreads (_workerThreads.Count, inUseWorkerThreads);
							}

						// Notify that the work item has been completed.
						// WorkItemsGroup may enqueue their next work item.
						workItem.FireWorkItemCompleted ();

						// Decrement the number of work items here so the idle 
						// ManualResetEvent won't fluctuate.
						DecrementWorkItemsCount ();
						}
					}
				}
#if SSHARP
			catch (Exception tae)
#else
			catch (ThreadAbortException tae)
#endif
				{
#if SSHARP
				Exception e = tae;

				if (tae is ThreadAbortException)
					{
#endif
				tae.GetHashCode ();
				// Handle the abort exception gracfully.
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
				Thread.ResetAbort();
#endif

				}
#if !SSHARP
			catch (Exception e)
				{
#endif
				//Debug.Assert (null != e);
				}
			finally
				{
				InformCompleted ();
				FireOnThreadTermination ();
				}

			return null;
			}

		private void ExecuteWorkItem (WorkItem workItem)
			{
			_windowsPCs.SampleWorkItemsWaitTime (workItem.WaitingTime);
			_localPCs.SampleWorkItemsWaitTime (workItem.WaitingTime);
			try
				{
				workItem.Execute ();
				}
			finally
				{
				_windowsPCs.SampleWorkItemsProcessTime (workItem.ProcessTime);
				_localPCs.SampleWorkItemsProcessTime (workItem.ProcessTime);
				}
			}


		#endregion

		#region Public Methods

		private void ValidateWaitForIdle ()
			{
			if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this)
				{
				throw new NotSupportedException (
					"WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock");
				}
			}

		internal static void ValidateWorkItemsGroupWaitForIdle (IWorkItemsGroup 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 causes a deadlock");
				}
			}

		[MethodImpl (MethodImplOptions.NoInlining)]
		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 causes a deadlock");
				}
			}

		/// <summary>
		/// Force the SmartThreadPool to shutdown
		/// </summary>
		public void Shutdown ()
			{
			Shutdown (true, 0);
			}

		/// <summary>
		/// Force the SmartThreadPool to shutdown with timeout
		/// </summary>
		public void Shutdown (bool forceAbort, TimeSpan timeout)
			{
			Shutdown (forceAbort, (int)timeout.TotalMilliseconds);
			}

		/// <summary>
		/// Empties the queue of work items and abort the threads in the pool.
		/// </summary>
		public void Shutdown (bool forceAbort, int millisecondsTimeout)
			{
			ValidateNotDisposed ();

			ISTPInstancePerformanceCounters pcs = _windowsPCs;

			if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs)
				{
				// Set the _pcs to "null" to stop updating the performance
				// counters
				_windowsPCs = NullSTPInstancePerformanceCounters.Instance;

				pcs.Dispose ();
				}

			Thread[] threads;
			lock (_workerThreads.SyncRoot)
				{
				// Shutdown the work items queue
				_workItemsQueue.Dispose ();

				// Signal the threads to exit
				_shutdown = true;
				_shuttingDownEvent.Set ();

				// Make a copy of the threads' references in the pool
#if SSHARP
				threads = new Thread[_workerThreads.RealCount];
#else
				threads = new Thread[_workerThreads.Count];
#endif
				_workerThreads.Keys.CopyTo (threads, 0);
				}

			int millisecondsLeft = millisecondsTimeout;
			Stopwatch stopwatch = Stopwatch.StartNew ();
			//DateTime start = DateTime.UtcNow;
			bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
			bool timeout = false;

			// Each iteration we update the time left for the timeout.
			foreach (Thread thread in threads)
				{
				// Join don't work with negative numbers
				if (!waitInfinitely && (millisecondsLeft < 0))
					{
					timeout = true;
					break;
					}

				// Wait for the thread to terminate
#if SSHARP
				bool success;
				try
					{
					success = thread.Join (millisecondsLeft);
					}
				catch (NullReferenceException)
					{
					success = true;
					}
#else
				bool success = thread.Join (millisecondsLeft);
#endif
				if (!success)
					{
					timeout = true;
					break;
					}

				if (!waitInfinitely)
					{
					// Update the time left to wait
					//TimeSpan ts = DateTime.UtcNow - start;
					millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
					}
				}

			if (timeout && forceAbort)
				{
				// Abort the threads in the pool
				foreach (Thread thread in threads)
					{

#if SSHARP
					bool running = false;
					try
						{
						running = thread != null && !thread.Join (0);
						}
					catch (NullReferenceException)
						{
						
						}

					if (running)
#else
					if ((thread != null)
#if (_WINDOWS_CE)
						&& !thread.Join (0)
#else
                        && thread.IsAlive
#endif
)
#endif
						{
						try
							{
							thread.Abort (); // Shutdown
							}
#if SSHARP
						catch (Exception e)
							{
							if (e.GetType () != typeof (System.Threading.ThreadAbortException) && e.GetType () != typeof (SecurityException))
								throw;
							e.GetHashCode ();
							}
#else
						catch (SecurityException e)
							{
							e.GetHashCode ();
							}
						catch (ThreadStateException ex)
							{
							ex.GetHashCode ();
							// In case the thread has been terminated 
							// after the check if it is alive.
							}
#endif
						}
					}
				}
			}

		/// <summary>
		/// Wait for all work items to complete
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <returns>
		/// true when every work item in workItemResults has completed; otherwise false.
		/// </returns>
		public static bool WaitAll (
			IWaitableResult[] waitableResults)
			{
			return WaitAll (waitableResults, Timeout.Infinite, true);
			}

		/// <summary>
		/// Wait for all work items to complete
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <returns>
		/// true when every work item in workItemResults has completed; otherwise false.
		/// </returns>
		public static bool WaitAll (
			IWaitableResult[] waitableResults,
			TimeSpan timeout,
			bool exitContext)
			{
			return WaitAll (waitableResults, (int)timeout.TotalMilliseconds, exitContext);
			}

		/// <summary>
		/// Wait for all work items to complete
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
		/// <returns>
		/// true when every work item in workItemResults has completed; otherwise false.
		/// </returns>
		public static bool WaitAll (
			IWaitableResult[] waitableResults,
			TimeSpan timeout,
			bool exitContext,
			WaitHandle cancelWaitHandle)
			{
			return WaitAll (waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
			}

		/// <summary>
		/// Wait for all work items to complete
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <returns>
		/// true when every work item in workItemResults has completed; otherwise false.
		/// </returns>
		public static bool WaitAll (
			IWaitableResult[] waitableResults,
			int millisecondsTimeout,
			bool exitContext)
			{
			return WorkItem.WaitAll (waitableResults, millisecondsTimeout, exitContext, null);
			}

		/// <summary>
		/// Wait for all work items to complete
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
		/// <returns>
		/// true when every work item in workItemResults has completed; otherwise false.
		/// </returns>
		public static bool WaitAll (
			IWaitableResult[] waitableResults,
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle)
			{
			return WorkItem.WaitAll (waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
			}


		/// <summary>
		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <returns>
		/// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled.
		/// </returns>
		public static int WaitAny (
			IWaitableResult[] waitableResults)
			{
			return WaitAny (waitableResults, Timeout.Infinite, true);
			}

		/// <summary>
		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <returns>
		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
		/// </returns>
		public static int WaitAny (
			IWaitableResult[] waitableResults,
			TimeSpan timeout,
			bool exitContext)
			{
			return WaitAny (waitableResults, (int)timeout.TotalMilliseconds, exitContext);
			}

		/// <summary>
		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="timeout">The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. </param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
		/// <returns>
		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
		/// </returns>
		public static int WaitAny (
			IWaitableResult[] waitableResults,
			TimeSpan timeout,
			bool exitContext,
			WaitHandle cancelWaitHandle)
			{
			return WaitAny (waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
			}

		/// <summary>
		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <returns>
		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
		/// </returns>
		public static int WaitAny (
			IWaitableResult[] waitableResults,
			int millisecondsTimeout,
			bool exitContext)
			{
			return WorkItem.WaitAny (waitableResults, millisecondsTimeout, exitContext, null);
			}

		/// <summary>
		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
		/// <returns>
		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
		/// </returns>
		public static int WaitAny (
			IWaitableResult[] waitableResults,
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle)
			{
			return WorkItem.WaitAny (waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle);
			}

		/// <summary>
		/// Creates a new WorkItemsGroup.
		/// </summary>
		/// <param name="concurrency">The number of work items that can be run concurrently</param>
		/// <returns>A reference to the WorkItemsGroup</returns>
		public IWorkItemsGroup CreateWorkItemsGroup (int concurrency)
			{
			IWorkItemsGroup workItemsGroup = new WorkItemsGroup (this, concurrency, _stpStartInfo);
			return workItemsGroup;
			}

		/// <summary>
		/// Creates a new WorkItemsGroup.
		/// </summary>
		/// <param name="concurrency">The number of work items that can be run concurrently</param>
		/// <param name="wigStartInfo">A WorkItemsGroup configuration that overrides the default behavior</param>
		/// <returns>A reference to the WorkItemsGroup</returns>
		public IWorkItemsGroup CreateWorkItemsGroup (int concurrency, WIGStartInfo wigStartInfo)
			{
			IWorkItemsGroup workItemsGroup = new WorkItemsGroup (this, concurrency, wigStartInfo);
			return workItemsGroup;
			}

		#region Fire Thread's Events

		private void FireOnThreadInitialization ()
			{
			if (null != _onThreadInitialization)
				{
				foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList ())
					{
					try
						{
						tih ();
						}
					catch (Exception e)
						{
						e.GetHashCode ();
						//Debug.Assert (false);
						throw;
						}
					}
				}
			}

		private void FireOnThreadTermination ()
			{
			if (null != _onThreadTermination)
				{
				foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList ())
					{
					try
						{
						tth ();
						}
					catch (Exception e)
						{
						e.GetHashCode ();
						//Debug.Assert (false);
						throw;
						}
					}
				}
			}

		#endregion

		/// <summary>
		/// This event is fired when a thread is created.
		/// Use it to initialize a thread before the work items use it.
		/// </summary>
		public event ThreadInitializationHandler OnThreadInitialization
			{
			add { _onThreadInitialization += value; }
			remove { _onThreadInitialization -= value; }
			}

		/// <summary>
		/// This event is fired when a thread is terminating.
		/// Use it for cleanup.
		/// </summary>
		public event ThreadTerminationHandler OnThreadTermination
			{
			add { _onThreadTermination += value; }
			remove { _onThreadTermination -= value; }
			}


		internal void CancelAbortWorkItemsGroup (WorkItemsGroup wig)
			{
			ThreadEntry[] threadEntries;
			lock (_workerThreads.SyncRoot)
				{
				threadEntries = new ThreadEntry[_workerThreads.RealCount];
				_workerThreads.Values.CopyTo (threadEntries, 0);
				}

			foreach (ThreadEntry threadEntry in threadEntries)
				{
				WorkItem workItem = threadEntry.CurrentWorkItem;
				if (null != workItem &&
					workItem.WasQueuedBy (wig) &&
					!workItem.IsCanceled)
					{
					threadEntry.CurrentWorkItem.GetWorkItemResult ().Cancel (true);
					}
				}
			}

		#endregion

		#region Properties

		/// <summary>
		/// Get/Set the lower limit of threads in the pool.
		/// </summary>
		public int MinThreads
			{
			get
				{
				ValidateNotDisposed ();
				return _stpStartInfo.MinWorkerThreads;
				}
			set
				{
				//Debug.Assert (value >= 0);
				//Debug.Assert (value <= _stpStartInfo.MaxWorkerThreads);
				if (_stpStartInfo.MaxWorkerThreads < value)
					{
					_stpStartInfo.MaxWorkerThreads = value;
					}
				_stpStartInfo.MinWorkerThreads = value;
				StartOptimalNumberOfThreads ();
				}
			}

		/// <summary>
		/// Get/Set the upper limit of threads in the pool.
		/// </summary>
		public int MaxThreads
			{
			get
				{
				ValidateNotDisposed ();
				return _stpStartInfo.MaxWorkerThreads;
				}

			set
				{
				//Debug.Assert (value > 0);
				//Debug.Assert (value >= _stpStartInfo.MinWorkerThreads);
				if (_stpStartInfo.MinWorkerThreads > value)
					{
					_stpStartInfo.MinWorkerThreads = value;
					}
				_stpStartInfo.MaxWorkerThreads = value;
				StartOptimalNumberOfThreads ();
				}
			}
		/// <summary>
		/// Get the number of threads in the thread pool.
		/// Should be between the lower and the upper limits.
		/// </summary>
		public int ActiveThreads
			{
			get
				{
				ValidateNotDisposed ();
				return _workerThreads.Count;
				}
			}

		/// <summary>
		/// Get the number of busy (not idle) threads in the thread pool.
		/// </summary>
		public int InUseThreads
			{
			get
				{
				ValidateNotDisposed ();
				return _inUseWorkerThreads;
				}
			}

		/// <summary>
		/// 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.
		/// </summary>
		public static bool IsWorkItemCanceled
			{
			get
				{
				return CurrentThreadEntry.CurrentWorkItem.IsCanceled;
				}
			}

		/// <summary>
		/// Checks if the work item has been cancelled, and if yes then abort the thread.
		/// Can be used with Cancel and timeout
		/// </summary>
		public static void AbortOnWorkItemCancel ()
			{
			if (IsWorkItemCanceled)
				{
				Thread.CurrentThread.Abort ();
				}
			}

		/// <summary>
		/// Thread Pool start information (readonly)
		/// </summary>
		public STPStartInfo STPStartInfo
			{
			get
				{
				return _stpStartInfo.AsReadOnly ();
				}
			}

		public bool IsShuttingdown
			{
			get { return _shutdown; }
			}

		/// <summary>
		/// Return the local calculated performance counters
		/// Available only if STPStartInfo.EnableLocalPerformanceCounters is true.
		/// </summary>
		public ISTPPerformanceCountersReader PerformanceCountersReader
			{
			get { return (ISTPPerformanceCountersReader)_localPCs; }
			}
		#endregion

		#region IDisposable Members

		public void Dispose ()
			{
			if (!_isDisposed)
				{
				if (!_shutdown)
					{
					Shutdown ();
					}

				if (null != _shuttingDownEvent)
					{
					_shuttingDownEvent.Close ();
					_shuttingDownEvent = null;
					}
				_workerThreads.Clear ();

				if (null != _isIdleWaitHandle)
					{
					_isIdleWaitHandle.Close ();
					_isIdleWaitHandle = null;
					}

				_isDisposed = true;
				}
			}

		private void ValidateNotDisposed ()
			{
			if (_isDisposed)
				{
				throw new ObjectDisposedException (GetType ().ToString (), "The SmartThreadPool has been shutdown");
				}
			}
		#endregion

		#region WorkItemsGroupBase Overrides

		/// <summary>
		/// Get/Set the maximum number of work items that execute cocurrency on the thread pool
		/// </summary>
		public override int Concurrency
			{
			get { return MaxThreads; }
			set { MaxThreads = value; }
			}

		/// <summary>
		/// Get the number of work items in the queue.
		/// </summary>
		public override int WaitingCallbacks
			{
			get
				{
				ValidateNotDisposed ();
				return _workItemsQueue.Count;
				}
			}

		/// <summary>
		/// Get an array with all the state objects of the currently running items.
		/// The array represents a snap shot and impact performance.
		/// </summary>
		public override object[] GetStates ()
			{
			object[] states = _workItemsQueue.GetStates ();
			return states;
			}

		/// <summary>
		/// WorkItemsGroup start information (readonly)
		/// </summary>
		public override WIGStartInfo WIGStartInfo
			{
			get { return _stpStartInfo.AsReadOnly (); }
			}

		/// <summary>
		/// Start the thread pool if it was started suspended.
		/// If it is already running, this method is ignored.
		/// </summary>
		public override void Start ()
			{
			if (!_isSuspended)
				{
				return;
				}
			_isSuspended = false;

			ICollection workItemsGroups = _workItemsGroups.Values;
			foreach (WorkItemsGroup workItemsGroup in workItemsGroups)
				{
				workItemsGroup.OnSTPIsStarting ();
				}

			StartOptimalNumberOfThreads ();
			}

		/// <summary>
		/// Cancel all work items using thread abortion
		/// </summary>
		/// <param name="abortExecution">True to stop work items by raising ThreadAbortException</param>
		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)
				{
				ThreadEntry[] threadEntries;
				lock (_workerThreads.SyncRoot)
					{
					threadEntries = new ThreadEntry[_workerThreads.RealCount];
					_workerThreads.Values.CopyTo (threadEntries, 0);
					}

				foreach (ThreadEntry threadEntry in threadEntries)
					{
					WorkItem workItem = threadEntry.CurrentWorkItem;
					if (null != workItem &&
						threadEntry.AssociatedSmartThreadPool == this &&
						!workItem.IsCanceled)
						{
						threadEntry.CurrentWorkItem.GetWorkItemResult ().Cancel (true);
						}
					}
				}
			}

		/// <summary>
		/// Wait for the thread pool to be idle
		/// </summary>
		public override bool WaitForIdle (int millisecondsTimeout)
			{
			ValidateWaitForIdle ();
			return STPEventWaitHandle.WaitOne (_isIdleWaitHandle, millisecondsTimeout, false);
			}

		/// <summary>
		/// This event is fired when all work items are completed.
		/// (When IsIdle changes to true)
		/// This event only work on WorkItemsGroup. On SmartThreadPool
		/// it throws the NotImplementedException.
		/// </summary>
		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

		#region Join, Choice, Pipe, etc.

		/// <summary>
		/// Executes all actions in parallel.
		/// Returns when they all finish.
		/// </summary>
		/// <param name="actions">Actions to execute</param>
		public void Join (IEnumerable<Action> actions)
			{
			WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
			IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup (int.MaxValue, wigStartInfo);
			foreach (Action action in actions)
				{
				workItemsGroup.QueueWorkItem (action);
				}
			workItemsGroup.Start ();
			workItemsGroup.WaitForIdle ();
			}

		/// <summary>
		/// Executes all actions in parallel.
		/// Returns when they all finish.
		/// </summary>
		/// <param name="actions">Actions to execute</param>
		public void Join (params Action[] actions)
			{
			Join ((IEnumerable<Action>)actions);
			}

		private class ChoiceIndex
			{
			public int _index = -1;
			}

		/// <summary>
		/// Executes all actions in parallel
		/// Returns when the first one completes
		/// </summary>
		/// <param name="actions">Actions to execute</param>
		public int Choice (IEnumerable<Action> actions)
			{
			WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
			IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup (int.MaxValue, wigStartInfo);

			ManualResetEvent anActionCompleted = new ManualResetEvent (false);

			ChoiceIndex choiceIndex = new ChoiceIndex ();

			int i = 0;
			foreach (Action action in actions)
				{
				Action act = action;
				int value = i;
				workItemsGroup.QueueWorkItem (() => { act (); Interlocked.CompareExchange (ref choiceIndex._index, value, -1); anActionCompleted.Set (); });
				++i;
				}
			workItemsGroup.Start ();
			anActionCompleted.WaitOne ();

			return choiceIndex._index;
			}

		/// <summary>
		/// Executes all actions in parallel
		/// Returns when the first one completes
		/// </summary>
		/// <param name="actions">Actions to execute</param>
		public int Choice (params Action[] actions)
			{
			return Choice ((IEnumerable<Action>)actions);
			}

		/// <summary>
		/// Executes actions in sequence asynchronously.
		/// Returns immediately.
		/// </summary>
		/// <param name="pipeState">A state context that passes </param>
		/// <param name="actions">Actions to execute in the order they should run</param>
		public void Pipe<T> (T pipeState, IEnumerable<Action<T>> actions)
			{
			WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true };
			IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup (1, wigStartInfo);
			foreach (Action<T> action in actions)
				{
				Action<T> act = action;
				workItemsGroup.QueueWorkItem (() => act (pipeState));
				}
			workItemsGroup.Start ();
			workItemsGroup.WaitForIdle ();
			}

		/// <summary>
		/// Executes actions in sequence asynchronously.
		/// Returns immediately.
		/// </summary>
		/// <param name="pipeState"></param>
		/// <param name="actions">Actions to execute in the order they should run</param>
		public void Pipe<T> (T pipeState, params Action<T>[] actions)
			{
			Pipe (pipeState, (IEnumerable<Action<T>>)actions);
			}
		#endregion
		}
	#endregion
	}
ï»¿using System;

namespace Amib.Threading.Internal
{
    /// <summary>
    /// Stopwatch class
    /// Used with WindowsCE and Silverlight which don't have Stopwatch
    /// </summary>
    internal class Stopwatch
    {
        private long _elapsed;
        private bool _isRunning;
        private long _startTimeStamp;

        public Stopwatch()
        {
            Reset();
        }

        private long GetElapsedDateTimeTicks()
        {
            long rawElapsedTicks = GetRawElapsedTicks();
            return rawElapsedTicks;
        }

        private long GetRawElapsedTicks()
        {
            long elapsed = _elapsed;
            if (_isRunning)
            {
                long ticks = GetTimestamp() - _startTimeStamp;
                elapsed += ticks;
            }
            return elapsed;
        }

        public static long GetTimestamp()
        {
            return DateTime.UtcNow.Ticks;
        }

        public void Reset()
        {
           _elapsed = 0L;
           _isRunning = false;
           _startTimeStamp = 0L;
        }

        public void Start()
        {
            if (!_isRunning)
            {
                _startTimeStamp = GetTimestamp();
                _isRunning = true;
            }
        }

        public static Stopwatch StartNew()
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            return stopwatch;
        }

        public void Stop()
        {
            if (_isRunning)
            {
                long ticks = GetTimestamp() - _startTimeStamp;
                _elapsed += ticks;
                _isRunning = false;
            }
        }

        // Properties
        public TimeSpan Elapsed
        {
            get
            {
                return new TimeSpan(GetElapsedDateTimeTicks());
            }
        }

        public long ElapsedMilliseconds
        {
            get
            {
                return (GetElapsedDateTimeTicks() / 0x2710L);
            }
        }

        public long ElapsedTicks
        {
            get
            {
                return GetRawElapsedTicks();
            }
        }

        public bool IsRunning
        {
            get
            {
                return _isRunning;
            }
        }
    }
}
using System.Collections.Generic;
using Crestron.SimplSharp;

namespace Amib.SSharpThreading.Internal
	{
	internal class SynchronizedDictionary<TKey, TValue>
		{
		private readonly Dictionary<TKey, TValue> _dictionary;
		private readonly object _lock;
#if SSHARP
		private int _pendingEntries;
#endif

		public SynchronizedDictionary ()
			{
			_lock = new object ();
			_dictionary = new Dictionary<TKey, TValue> ();
			}

		public int Count
			{
#if SSHARP
			get { return _dictionary.Count + _pendingEntries; }
#else
			get { return _dictionary.Count; }
#endif
			}

#if SSHARP
		public int RealCount
			{
			get { return _dictionary.Count; }
			}
#endif

		public bool Contains (TKey key)
			{
			lock (_lock)
				{
				return _dictionary.ContainsKey (key);
				}
			}

		public void Remove (TKey key)
			{
			lock (_lock)
				{
				_dictionary.Remove (key);
				}
			}

		public object SyncRoot
			{
			get { return _lock; }
			}

		public TValue this[TKey key]
			{
			get
				{
				lock (_lock)
					{
					return _dictionary[key];
					}
				}
			set
				{
				lock (_lock)
					{
					_dictionary[key] = value;
					}
				}
			}

		public Dictionary<TKey, TValue>.KeyCollection Keys
			{
			get
				{
				lock (_lock)
					{
					return _dictionary.Keys;
					}
				}
			}

		public Dictionary<TKey, TValue>.ValueCollection Values
			{
			get
				{
				lock (_lock)
					{
					return _dictionary.Values;
					}
				}
			}
		public void Clear ()
			{
			lock (_lock)
				{
				_dictionary.Clear ();
				_pendingEntries = 0;
				}
			}

#if SSHARP
		public void AddPendingEntry ()
			{
			Interlocked.Increment (ref _pendingEntries);
			}

		public void RemovePendingEntry ()
			{
			Interlocked.Decrement (ref _pendingEntries);
			}
#endif
		}
	}
ï»¿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;

namespace Crestron.SimplSharpPro.CrestronThread
	{
	public class ThreadEx : Thread
		{
		private static int _lastExIdHigh;
		private static int _lastExId;

		private readonly long _threadExId;

		public ThreadEx (Crestron.SimplSharpPro.CrestronThread.ThreadCallbackFunction threadCallbackFunc, object userDefinedObject)
			: base (threadCallbackFunc, userDefinedObject)
			{
			_threadExId = Interlocked.Increment (ref _lastExId) | (long)(_threadExId == 0 ? Interlocked.Increment (ref _lastExIdHigh) : _lastExIdHigh) << 32;
			}

		public ThreadEx (Crestron.SimplSharpPro.CrestronThread.ThreadCallbackFunction threadCallbackFunc, object userDefinedObject,
		                 Crestron.SimplSharpPro.CrestronThread.Thread.eThreadStartOptions threadStartOption)
			: base (threadCallbackFunc, userDefinedObject, threadStartOption)
			{
			_threadExId = Interlocked.Increment (ref _lastExId) | (long)(_threadExId == 0 ? Interlocked.Increment (ref _lastExIdHigh) : _lastExIdHigh) << 32;
			}

		public long ThreadExId
			{
			get
				{
				return _threadExId;
				}
			}

		public override int GetHashCode ()
			{
			return (int)_threadExId ^ ((int)(_threadExId >> 32));
			}

		public override bool Equals (object obj)
			{
			return obj is long && _threadExId == (long)obj;
			}

		public new void Join ()
			{
			Join (Timeout.Infinite);
			}

		public new bool Join (int millisecondTimeout)
			{
			try
				{
				return base.Join (millisecondTimeout);
				}
			catch (NullReferenceException)
				{
				return true;
				}
			}

		public bool Join (TimeSpan timeout)
			{
			long lmsec = (long)timeout.TotalMilliseconds;
			if (lmsec < 0 || lmsec > Int32.MaxValue)
				throw new ArgumentOutOfRangeException ("timeout", "timeout exceed maximum milliseconds");

			return Join ((int)lmsec);
			}

		public static ThreadEx CurrentThread
			{
			get
				{
				
				}
			}
		}
	}
using System;

namespace Amib.SSharpThreading
{
	/// <summary>
	/// Summary description for WIGStartInfo.
	/// </summary>
	public class WIGStartInfo
	{
        private bool _useCallerCallContext;
        private bool _useCallerHttpContext;
        private bool _disposeOfStateObjects;
        private CallToPostExecute _callToPostExecute;
        private PostExecuteWorkItemCallback _postExecuteWorkItemCallback;
        private bool _startSuspended;
        private WorkItemPriority _workItemPriority;
        private bool _fillStateWithArgs;

        protected bool _readOnly;

	    public WIGStartInfo()
        {
	        _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs;
	        _workItemPriority = SmartThreadPool.DefaultWorkItemPriority;
            _startSuspended = SmartThreadPool.DefaultStartSuspended;
            _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
            _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
            _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
            _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
            _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
        }

	    public WIGStartInfo(WIGStartInfo wigStartInfo)
        {
            _useCallerCallContext = wigStartInfo.UseCallerCallContext;
            _useCallerHttpContext = wigStartInfo.UseCallerHttpContext;
            _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
            _callToPostExecute = wigStartInfo.CallToPostExecute;
            _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback;
            _workItemPriority = wigStartInfo.WorkItemPriority;
            _startSuspended = wigStartInfo.StartSuspended;
            _fillStateWithArgs = wigStartInfo.FillStateWithArgs;
        }

        protected void ThrowIfReadOnly()
        {
            if (_readOnly)
            {
                throw new NotSupportedException("This is a readonly instance and set is not supported");
            }
        }

	    /// <summary>
	    /// Get/Set if to use the caller's security context
	    /// </summary>
	    public virtual bool UseCallerCallContext
	    {
	        get { return _useCallerCallContext; }
            set 
            { 
                ThrowIfReadOnly();  
                _useCallerCallContext = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set if to use the caller's HTTP context
	    /// </summary>
	    public virtual bool UseCallerHttpContext
	    {
	        get { return _useCallerHttpContext; }
            set 
            { 
                ThrowIfReadOnly();  
                _useCallerHttpContext = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set if to dispose of the state object of a work item
	    /// </summary>
	    public virtual bool DisposeOfStateObjects
	    {
	        get { return _disposeOfStateObjects; }
            set 
            { 
                ThrowIfReadOnly();  
                _disposeOfStateObjects = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set the run the post execute options
	    /// </summary>
	    public virtual CallToPostExecute CallToPostExecute
	    {
	        get { return _callToPostExecute; }
            set 
            { 
                ThrowIfReadOnly();  
                _callToPostExecute = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set the default post execute callback
	    /// </summary>
	    public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback
	    {
	        get { return _postExecuteWorkItemCallback; }
            set 
            { 
                ThrowIfReadOnly();  
                _postExecuteWorkItemCallback = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set if the work items execution should be suspended until the Start()
	    /// method is called.
	    /// </summary>
	    public virtual bool StartSuspended
	    {
	        get { return _startSuspended; }
            set 
            { 
                ThrowIfReadOnly();  
                _startSuspended = value; 
            }
	    }


	    /// <summary>
	    /// Get/Set the default priority that a work item gets when it is enqueued
	    /// </summary>
	    public virtual WorkItemPriority WorkItemPriority
	    {
	        get { return _workItemPriority; }
	        set { _workItemPriority = value; }
	    }

	    /// <summary>
        /// Get/Set the if QueueWorkItem of Action&lt;...&gt;/Func&lt;...&gt; fill the
	    /// arguments as an object array into the state of the work item.
	    /// The arguments can be access later by IWorkItemResult.State.
	    /// </summary>
	    public virtual bool FillStateWithArgs
	    {
	        get { return _fillStateWithArgs; }
            set 
            { 
                ThrowIfReadOnly();  
                _fillStateWithArgs = value; 
            }
	    }

	    /// <summary>
        /// Get a readonly version of this WIGStartInfo
        /// </summary>
        /// <returns>Returns a readonly reference to this WIGStartInfoRO</returns>
        public WIGStartInfo AsReadOnly()
	    {
            return new WIGStartInfo(this) { _readOnly = true };
	    }
    }
}
ï»¿using System;
using System.Collections.Generic;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

namespace Amib.SSharpThreading.Internal
{
    public partial class WorkItem
    {
        #region WorkItemResult class

        private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult
        {
            /// <summary>
            /// A back reference to the work item
            /// </summary>
            private readonly WorkItem _workItem;

            public WorkItemResult(WorkItem workItem)
            {
                _workItem = workItem;
            }

            internal WorkItem GetWorkItem()
            {
                return _workItem;
            }

            #region IWorkItemResult Members

            public bool IsCompleted
            {
                get
                {
                    return _workItem.IsCompleted;
                }
            }

            public bool IsCanceled
            {
                get
                {
                    return _workItem.IsCanceled;
                }
            }

            public object GetResult()
            {
                return _workItem.GetResult(Timeout.Infinite, true, null);
            }

            public object GetResult(int millisecondsTimeout, bool exitContext)
            {
                return _workItem.GetResult(millisecondsTimeout, exitContext, null);
            }

            public object GetResult(TimeSpan timeout, bool exitContext)
            {
                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null);
            }

            public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
            {
                return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
            }

            public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
            {
                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle);
            }

            public object GetResult(out Exception e)
            {
                return _workItem.GetResult(Timeout.Infinite, true, null, out e);
            }

            public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
            {
                return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e);
            }

            public object GetResult(TimeSpan timeout, bool exitContext, out Exception e)
            {
                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e);
            }

            public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
            {
                return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
            }

            public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
            {
                return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e);
            }

            public bool Cancel()
            {
                return Cancel(false);
            }

            public bool Cancel(bool abortExecution)
            {
                return _workItem.Cancel(abortExecution);
            }

            public object State
            {
                get
                {
                    return _workItem._state;
                }
            }

            public WorkItemPriority WorkItemPriority
            {
                get
                {
                    return _workItem._workItemInfo.WorkItemPriority;
                }
            }

            /// <summary>
            /// Return the result, same as GetResult()
            /// </summary>
            public object Result
            {
                get { return GetResult(); }
            }

            /// <summary>
            /// Returns the exception if occured otherwise returns null.
            /// This value is valid only after the work item completed,
            /// before that it is always null.
            /// </summary>
            public object Exception
            {
                get { return _workItem._exception; }
            }

            #endregion

            #region IInternalWorkItemResult Members

            public event WorkItemStateCallback OnWorkItemStarted
            {
                add
                {
                    _workItem.OnWorkItemStarted += value;
                }
                remove
                {
                    _workItem.OnWorkItemStarted -= value;
                }
            }


            public event WorkItemStateCallback OnWorkItemCompleted
            {
                add
                {
                    _workItem.OnWorkItemCompleted += value;
                }
                remove
                {
                    _workItem.OnWorkItemCompleted -= value;
                }
            }

            #endregion

            #region IInternalWorkItemResult Members

            public IWorkItemResult GetWorkItemResult()
            {
                return this;
            }

            public IWorkItemResult<TResult> GetWorkItemResultT<TResult>()
            {
                return new WorkItemResultTWrapper<TResult>(this);
            }

            #endregion
        }

        #endregion

    }
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
//using SSMono.Diagnostics;
using Crestron.SimplSharp;
using SSharp.Threading;

namespace Amib.SSharpThreading.Internal
	{
	/// <summary>
	/// Holds a callback delegate and the state for that delegate.
	/// </summary>
	public partial class WorkItem : IHasWorkItemPriority
		{
		#region WorkItemState enum

		/// <summary>
		/// Indicates the state of the work item in the thread pool
		/// </summary>
		private enum WorkItemState
			{
			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 Fields

		/// <summary>
		/// Callback delegate for the callback.
		/// </summary>
		private readonly WorkItemCallback _callback;

		/// <summary>
		/// State with which to call the callback delegate.
		/// </summary>
		private object _state;

#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
        /// <summary>
        /// Stores the caller's context
        /// </summary>
        private readonly CallerThreadContext _callerContext;
#endif
		/// <summary>
		/// Holds the result of the mehtod
		/// </summary>
		private object _result;

		/// <summary>
		/// Hold the exception if the method threw it
		/// </summary>
		private Exception _exception;

		/// <summary>
		/// Hold the state of the work item
		/// </summary>
		private WorkItemState _workItemState;

		/// <summary>
		/// A ManualResetEvent to indicate that the result is ready
		/// </summary>
		private ManualResetEvent _workItemCompleted;

		/// <summary>
		/// A reference count to the _workItemCompleted. 
		/// When it reaches to zero _workItemCompleted is Closed
		/// </summary>
		private int _workItemCompletedRefCount;

		/// <summary>
		/// Represents the result state of the work item
		/// </summary>
		private readonly WorkItemResult _workItemResult;

		/// <summary>
		/// Work item info
		/// </summary>
		private readonly WorkItemInfo _workItemInfo;

		/// <summary>
		/// Called when the WorkItem starts
		/// </summary>
		private event WorkItemStateCallback _workItemStartedEvent;

		/// <summary>
		/// Called when the WorkItem completes
		/// </summary>
		private event WorkItemStateCallback _workItemCompletedEvent;

		/// <summary>
		/// A reference to an object that indicates whatever the 
		/// WorkItemsGroup has been canceled
		/// </summary>
		private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;

		/// <summary>
		/// A reference to an object that indicates whatever the 
		/// SmartThreadPool has been canceled
		/// </summary>
		private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup;

		/// <summary>
		/// The work item group this work item belong to.
		/// </summary>
		private readonly IWorkItemsGroup _workItemsGroup;

		/// <summary>
		/// The thread that executes this workitem.
		/// This field is available for the period when the work item is executed, before and after it is null.
		/// </summary>
		private Thread _executingThread;

		/// <summary>
		/// The absulote time when the work item will be timeout
		/// </summary>
		private long _expirationTime;

		#region Performance Counter fields




		/// <summary>
		/// Stores how long the work item waited on the stp queue
		/// </summary>
		private Stopwatch _waitingOnQueueStopwatch;

		/// <summary>
		/// Stores how much time it took the work item to execute after it went out of the queue
		/// </summary>
		private Stopwatch _processingStopwatch;

		#endregion

		#endregion

		#region Properties

		public TimeSpan WaitingTime
			{
			get
				{
				return _waitingOnQueueStopwatch.Elapsed;
				}
			}

		public TimeSpan ProcessTime
			{
			get
				{
				return _processingStopwatch.Elapsed;
				}
			}

		internal WorkItemInfo WorkItemInfo
			{
			get
				{
				return _workItemInfo;
				}
			}

		#endregion

		#region Construction

		/// <summary>
		/// Initialize the callback holding object.
		/// </summary>
		/// <param name="workItemsGroup">The workItemGroup of the workitem</param>
		/// <param name="workItemInfo">The WorkItemInfo of te workitem</param>
		/// <param name="callback">Callback delegate for the callback.</param>
		/// <param name="state">State with which to call the callback delegate.</param>
		/// 
		/// We assume that the WorkItem object is created within the thread
		/// that meant to run the callback
		public WorkItem (
			IWorkItemsGroup workItemsGroup,
			WorkItemInfo workItemInfo,
			WorkItemCallback callback,
			object state)
			{
			_workItemsGroup = workItemsGroup;
			_workItemInfo = workItemInfo;

#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
            if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext)
            {
                _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext);
            }
#endif

			_callback = callback;
			_state = state;
			_workItemResult = new WorkItemResult (this);
			Initialize ();
			}

		internal void Initialize ()
			{
			// The _workItemState is changed directly instead of using the SetWorkItemState
			// method since we don't want to go throught IsValidStateTransition.
			_workItemState = WorkItemState.InQueue;

			_workItemCompleted = null;
			_workItemCompletedRefCount = 0;
			_waitingOnQueueStopwatch = new Stopwatch ();
			_processingStopwatch = new Stopwatch ();
			_expirationTime =
				_workItemInfo.Timeout > 0 ?
				DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond :
				long.MaxValue;
			}

		internal bool WasQueuedBy (IWorkItemsGroup workItemsGroup)
			{
			return (workItemsGroup == _workItemsGroup);
			}


		#endregion

		#region Methods

		internal CanceledWorkItemsGroup CanceledWorkItemsGroup
			{
			get { return _canceledWorkItemsGroup; }
			set { _canceledWorkItemsGroup = value; }
			}

		internal CanceledWorkItemsGroup CanceledSmartThreadPool
			{
			get { return _canceledSmartThreadPool; }
			set { _canceledSmartThreadPool = value; }
			}

		/// <summary>
		/// Change the state of the work item to in progress if it wasn't canceled.
		/// </summary>
		/// <returns>
		/// Return true on success or false in case the work item was canceled.
		/// If the work item needs to run a post execute then the method will return true.
		/// </returns>
		public bool StartingWorkItem ()
			{
			_waitingOnQueueStopwatch.Stop ();
			_processingStopwatch.Start ();

			lock (this)
				{
				if (IsCanceled)
					{
					bool result = false;
					if ((_workItemInfo.PostExecuteWorkItemCallback != null) &&
						((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled))
						{
						result = true;
						}

					return result;
					}

				//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);
				}

			return true;
			}

		/// <summary>
		/// Execute the work item and the post execute
		/// </summary>
		public void Execute ()
			{
			CallToPostExecute currentCallToPostExecute = 0;

			// Execute the work item if we are in the correct state
			switch (GetWorkItemState ())
				{
				case WorkItemState.InProgress:
					currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled;
					ExecuteWorkItem ();
					break;
				case WorkItemState.Canceled:
					currentCallToPostExecute |= CallToPostExecute.WhenWorkItemCanceled;
					break;
				default:
					//Debug.Assert (false);
					throw new NotSupportedException ();
				}

			// Run the post execute as needed
			if ((currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0)
				{
				PostExecute ();
				}

			_processingStopwatch.Stop ();
			}

		internal void FireWorkItemCompleted ()
			{
			try
				{
				if (null != _workItemCompletedEvent)
					{
					_workItemCompletedEvent (this);
					}
				}
			catch // Suppress exceptions
			{ }
			}

		internal void FireWorkItemStarted ()
			{
			try
				{
				if (null != _workItemStartedEvent)
					{
					_workItemStartedEvent (this);
					}
				}
			catch // Suppress exceptions
			{ }
			}

		/// <summary>
		/// Execute the work item
		/// </summary>
		private void ExecuteWorkItem ()
			{

#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
            CallerThreadContext ctc = null;
            if (null != _callerContext)
            {
                ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext);
                CallerThreadContext.Apply(_callerContext);
            }
#endif

			Exception exception = null;
			object result = null;

			try
				{
				try
					{
					result = _callback (_state);
					}
				catch (Exception e)
					{
					// Save the exception so we can rethrow it later
					exception = e;
					}

				// Remove the value of the execution thread, so it will be impossible to cancel the work item,
				// since it is already completed.
				// Cancelling a work item that already completed may cause the abortion of the next work item!!!
				Thread executionThread = Interlocked.CompareExchange (ref _executingThread, null, _executingThread);

				if (null == executionThread)
					{
					// Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException
					Thread.Sleep (60 * 1000);

					// If after 1 minute this thread was not aborted then let it continue working.
					}
				}
			// We must treat the ThreadAbortException or else it will be stored in the exception variable
#if SSHARP
			catch (Exception tae)
#else
			catch (ThreadAbortException tae)
#endif
				{
#if SSHARP
				if (!(tae is System.Threading.ThreadAbortException))
					throw;
#endif
				tae.GetHashCode ();
				// Check if the work item was cancelled
				// If we got a ThreadAbortException and the STP is not shutting down, it means the 
				// work items was cancelled.
				if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown)
					{
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
                    Thread.ResetAbort();
#endif
					}
				}

#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
            if (null != _callerContext)
            {
                CallerThreadContext.Apply(ctc);
            }
#endif

			if (!SmartThreadPool.IsWorkItemCanceled)
				{
				SetResult (result, exception);
				}
			}

		/// <summary>
		/// Runs the post execute callback
		/// </summary>
		private void PostExecute ()
			{
			if (null != _workItemInfo.PostExecuteWorkItemCallback)
				{
				try
					{
					_workItemInfo.PostExecuteWorkItemCallback (_workItemResult);
					}
				catch (Exception e)
					{
					//Debug.Assert (null != e);
					}
				}
			}

		/// <summary>
		/// Set the result of the work item to return
		/// </summary>
		/// <param name="result">The result of the work item</param>
		/// <param name="exception">The exception that was throw while the workitem executed, null
		/// if there was no exception.</param>
		internal void SetResult (object result, Exception exception)
			{
			_result = result;
			_exception = exception;
			SignalComplete (false);
			}

		/// <summary>
		/// Returns the work item result
		/// </summary>
		/// <returns>The work item result</returns>
		internal IWorkItemResult GetWorkItemResult ()
			{
			return _workItemResult;
			}

		/// <summary>
		/// Wait for all work items to complete
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
		/// <returns>
		/// true when every work item in waitableResults has completed; otherwise false.
		/// </returns>
		internal static bool WaitAll (
			IWaitableResult[] waitableResults,
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle)
			{
			if (0 == waitableResults.Length)
				{
				return true;
				}

			bool success;
			WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length];
			GetWaitHandles (waitableResults, waitHandles);

			if ((null == cancelWaitHandle) && (waitHandles.Length <= 64))
				{
				success = STPEventWaitHandle.WaitAll (waitHandles, millisecondsTimeout, exitContext);
				}
			else
				{
				success = true;
				int millisecondsLeft = millisecondsTimeout;
				Stopwatch stopwatch = Stopwatch.StartNew ();

				WaitHandle[] whs;
				if (null != cancelWaitHandle)
					{
					whs = new WaitHandle[] { null, cancelWaitHandle };
					}
				else
					{
					whs = new WaitHandle[] { null };
					}

				bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout);
				// Iterate over the wait handles and wait for each one to complete.
				// 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 < waitableResults.Length; ++i)
					{
					// WaitAny don't work with negative numbers
					if (!waitInfinitely && (millisecondsLeft < 0))
						{
						success = false;
						break;
						}

					whs[0] = waitHandles[i];
					int result = STPEventWaitHandle.WaitAny (whs, millisecondsLeft, exitContext);
					if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result))
						{
						success = false;
						break;
						}

					if (!waitInfinitely)
						{
						// Update the time left to wait
						millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds;
						}
					}
				}
			// Release the wait handles
			ReleaseWaitHandles (waitableResults);

			return success;
			}

		/// <summary>
		/// Waits for any of the work items in the specified array to complete, cancel, or timeout
		/// </summary>
		/// <param name="waitableResults">Array of work item result objects</param>
		/// <param name="millisecondsTimeout">The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely.</param>
		/// <param name="exitContext">
		/// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. 
		/// </param>
		/// <param name="cancelWaitHandle">A cancel wait handle to interrupt the wait if needed</param>
		/// <returns>
		/// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled.
		/// </returns>
		internal static int WaitAny (
			IWaitableResult[] waitableResults,
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle)
			{
			WaitHandle[] waitHandles;

			if (null != cancelWaitHandle)
				{
				waitHandles = new WaitHandle[waitableResults.Length + 1];
				GetWaitHandles (waitableResults, waitHandles);
				waitHandles[waitableResults.Length] = cancelWaitHandle;
				}
			else
				{
				waitHandles = new WaitHandle[waitableResults.Length];
				GetWaitHandles (waitableResults, waitHandles);
				}

			int result = STPEventWaitHandle.WaitAny (waitHandles, millisecondsTimeout, exitContext);

			// Treat cancel as timeout
			if (null != cancelWaitHandle)
				{
				if (result == waitableResults.Length)
					{
					result = STPEventWaitHandle.WaitTimeout;
					}
				}

			ReleaseWaitHandles (waitableResults);

			return result;
			}

		/// <summary>
		/// Fill an array of wait handles with the work items wait handles.
		/// </summary>
		/// <param name="waitableResults">An array of work item results</param>
		/// <param name="waitHandles">An array of wait handles to fill</param>
		private static void GetWaitHandles (
			IWaitableResult[] waitableResults,
			WaitHandle[] waitHandles)
			{
			for (int i = 0; i < waitableResults.Length; ++i)
				{
				WorkItemResult wir = waitableResults[i].GetWorkItemResult () as WorkItemResult;
				//Debug.Assert (null != wir, "All waitableResults must be WorkItemResult objects");

				waitHandles[i] = wir.GetWorkItem ().GetWaitHandle ();
				}
			}

		/// <summary>
		/// Release the work items' wait handles
		/// </summary>
		/// <param name="waitableResults">An array of work item results</param>
		private static void ReleaseWaitHandles (IWaitableResult[] waitableResults)
			{
			for (int i = 0; i < waitableResults.Length; ++i)
				{
				WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult ();

				wir.GetWorkItem ().ReleaseWaitHandle ();
				}
			}

		#endregion

		#region Private Members

		private WorkItemState GetWorkItemState ()
			{
			lock (this)
				{
				if (WorkItemState.Completed == _workItemState)
					{
					return _workItemState;
					}

				long nowTicks = DateTime.UtcNow.Ticks;

				if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime)
					{
					_workItemState = WorkItemState.Canceled;
					}

				if (WorkItemState.InProgress == _workItemState)
					{
					return _workItemState;
					}

				if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled)
					{
					return WorkItemState.Canceled;
					}

				return _workItemState;
				}
			}


		/// <summary>
		/// Sets the work item's state
		/// </summary>
		/// <param name="workItemState">The state to set the work item to</param>
		private void SetWorkItemState (WorkItemState workItemState)
			{
			lock (this)
				{
				if (IsValidStatesTransition (_workItemState, workItemState))
					{
					_workItemState = workItemState;
					}
				}
			}

		/// <summary>
		/// Signals that work item has been completed or canceled
		/// </summary>
		/// <param name="canceled">Indicates that the work item has been canceled</param>
		private void SignalComplete (bool canceled)
			{
			SetWorkItemState (canceled ? WorkItemState.Canceled : WorkItemState.Completed);
			lock (this)
				{
				// If someone is waiting then signal.
				if (null != _workItemCompleted)
					{
					_workItemCompleted.Set ();
					}
				}
			}

		internal void WorkItemIsQueued ()
			{
			_waitingOnQueueStopwatch.Start ();
			}

		#endregion

		#region Members exposed by WorkItemResult

		/// <summary>
		/// Cancel the work item if it didn't start running yet.
		/// </summary>
		/// <returns>Returns true on success or false if the work item is in progress or already completed</returns>
		private bool Cancel (bool abortExecution)
			{
#if false && (_WINDOWS_CE)
			if (abortExecution)
				{
				throw new ArgumentOutOfRangeException ("abortExecution", "WindowsCE doesn't support this feature");
				}
#endif
			bool success = false;
			bool signalComplete = false;

			lock (this)
				{
				switch (GetWorkItemState ())
					{
					case WorkItemState.Canceled:
						////Debug.WriteLine("Work item already canceled");
						if (abortExecution)
							{
							Thread executionThread = Interlocked.CompareExchange (ref _executingThread, null, _executingThread);
							if (null != executionThread)
								{
								executionThread.Abort (); // "Cancel"
								// No need to signalComplete, because we already cancelled this work item
								// so it already signaled its completion.
								//signalComplete = true;
								}
							}
						success = true;
						break;
					case WorkItemState.Completed:
						////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;
						////Debug.WriteLine("Work item canceled");
						success = true;
						break;
					}

				if (signalComplete)
					{
					SignalComplete (true);
					}
				}
			return success;
			}

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
		/// In case of error the method throws and exception
		/// </summary>
		/// <returns>The result of the work item</returns>
		private object GetResult (
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle)
			{
			Exception e;
			object result = GetResult (millisecondsTimeout, exitContext, cancelWaitHandle, out e);
			if (null != e)
				{
				throw new WorkItemResultException ("The work item caused an excpetion, see the inner exception for details", e);
				}
			return result;
			}

		/// <summary>
		/// Get the result of the work item.
		/// If the work item didn't run yet then the caller waits for the result, timeout, or cancel.
		/// In case of error the e argument is filled with the exception
		/// </summary>
		/// <returns>The result of the work item</returns>
		private object GetResult (
			int millisecondsTimeout,
			bool exitContext,
			WaitHandle cancelWaitHandle,
			out Exception e)
			{
			e = null;

			// Check for cancel
			if (WorkItemState.Canceled == GetWorkItemState ())
				{
				throw new WorkItemCancelException ("Work item canceled");
				}

			// Check for completion
			if (IsCompleted)
				{
				e = _exception;
				return _result;
				}

			// If no cancelWaitHandle is provided
			if (null == cancelWaitHandle)
				{
				WaitHandle wh = GetWaitHandle ();

				bool timeout = !STPEventWaitHandle.WaitOne (wh, millisecondsTimeout, exitContext);

				ReleaseWaitHandle ();

				if (timeout)
					{
					throw new WorkItemTimeoutException ("Work item timeout");
					}
				}
			else
				{
				WaitHandle wh = GetWaitHandle ();
				int result = STPEventWaitHandle.WaitAny (new WaitHandle[] { wh, cancelWaitHandle });
				ReleaseWaitHandle ();

				switch (result)
					{
					case 0:
						// The work item signaled
						// Note that the signal could be also as a result of canceling the 
						// work item (not the get result)
						break;
					case 1:
					case STPEventWaitHandle.WaitTimeout:
						throw new WorkItemTimeoutException ("Work item timeout");
					default:
						//Debug.Assert (false);
						break;

					}
				}

			// Check for cancel
			if (WorkItemState.Canceled == GetWorkItemState ())
				{
				throw new WorkItemCancelException ("Work item canceled");
				}

			//Debug.Assert (IsCompleted);

			e = _exception;

			// Return the result
			return _result;
			}

		/// <summary>
		/// A wait handle to wait for completion, cancel, or timeout 
		/// </summary>
		private WaitHandle GetWaitHandle ()
			{
			lock (this)
				{
				if (null == _workItemCompleted)
					{
					_workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent (IsCompleted);
					}
				++_workItemCompletedRefCount;
				}
			return _workItemCompleted;
			}

		private void ReleaseWaitHandle ()
			{
			lock (this)
				{
				if (null != _workItemCompleted)
					{
					--_workItemCompletedRefCount;
					if (0 == _workItemCompletedRefCount)
						{
						_workItemCompleted.Close ();
						_workItemCompleted = null;
						}
					}
				}
			}

		/// <summary>
		/// Returns true when the work item has completed or canceled
		/// </summary>
		private bool IsCompleted
			{
			get
				{
				lock (this)
					{
					WorkItemState workItemState = GetWorkItemState ();
					return ((workItemState == WorkItemState.Completed) ||
							(workItemState == WorkItemState.Canceled));
					}
				}
			}

		/// <summary>
		/// Returns true when the work item has canceled
		/// </summary>
		public bool IsCanceled
			{
			get
				{
				lock (this)
					{
					return (GetWorkItemState () == WorkItemState.Canceled);
					}
				}
			}

		#endregion

		#region IHasWorkItemPriority Members

		/// <summary>
		/// Returns the priority of the work item
		/// </summary>
		public WorkItemPriority WorkItemPriority
			{
			get
				{
				return _workItemInfo.WorkItemPriority;
				}
			}

		#endregion

		internal event WorkItemStateCallback OnWorkItemStarted
			{
			add
				{
				_workItemStartedEvent += value;
				}
			remove
				{
				_workItemStartedEvent -= value;
				}
			}

		internal event WorkItemStateCallback OnWorkItemCompleted
			{
			add
				{
				_workItemCompletedEvent += value;
				}
			remove
				{
				_workItemCompletedEvent -= value;
				}
			}

		public void DisposeOfState ()
			{
			if (_workItemInfo.DisposeOfStateObjects)
				{
				IDisposable disp = _state as IDisposable;
				if (null != disp)
					{
					disp.Dispose ();
					_state = null;
					}
				}
			}
		}
	}
using System;

namespace Amib.SSharpThreading.Internal
{
	#region WorkItemFactory class 

	public class WorkItemFactory
	{
		/// <summary>
		/// Create a new work item
		/// </summary>
		/// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
		/// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback)
		{
			return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null);
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
        /// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="workItemPriority">The priority of the work item</param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback, 
			WorkItemPriority workItemPriority)
		{
			return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority);
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
        /// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="workItemInfo">Work item info</param>
		/// <param name="callback">A callback to execute</param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemInfo workItemInfo, 
			WorkItemCallback callback)
		{
			return CreateWorkItem(
				workItemsGroup,
				wigStartInfo,
				workItemInfo, 
				callback, 
				null);
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The WorkItemsGroup of this workitem</param>
        /// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback, 
			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,
				workItemInfo,
				callback, 
				state);
			return workItem;
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
		/// <param name="workItemsGroup">The work items group</param>
		/// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback, 
			object state, 
			WorkItemPriority workItemPriority)
		{
			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 = workItemPriority;

			WorkItem workItem = new WorkItem(
				workItemsGroup,
				workItemInfo,
				callback, 
				state);

			return workItem;
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The work items group</param>
		/// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="workItemInfo">Work item information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <returns>Returns a work item</returns>
        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);

            return workItem;
        }

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The work items group</param>
		/// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback, 
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback)
		{
			ValidateCallback(callback);
			ValidateCallback(postExecuteWorkItemCallback);

			WorkItemInfo workItemInfo = new WorkItemInfo();
			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
			workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
            workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;

			WorkItem workItem = new WorkItem(
				workItemsGroup,
				workItemInfo,
				callback, 
				state);

			return workItem;
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The work items group</param>
		/// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback, 
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
			WorkItemPriority workItemPriority)
		{
			ValidateCallback(callback);
			ValidateCallback(postExecuteWorkItemCallback);

			WorkItemInfo workItemInfo = new WorkItemInfo();
			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
			workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute;
			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
			workItemInfo.WorkItemPriority = workItemPriority;

			WorkItem workItem = new WorkItem(
				workItemsGroup,
				workItemInfo,
				callback, 
				state);

			return workItem;
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The work items group</param>
		/// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback, 
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
			CallToPostExecute callToPostExecute)
		{
			ValidateCallback(callback);
			ValidateCallback(postExecuteWorkItemCallback);

			WorkItemInfo workItemInfo = new WorkItemInfo();
			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
			workItemInfo.CallToPostExecute = callToPostExecute;
			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;
            workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority;

			WorkItem workItem = new WorkItem(
				workItemsGroup,
				workItemInfo,
				callback, 
				state);

			return workItem;
		}

		/// <summary>
		/// Create a new work item
		/// </summary>
        /// <param name="workItemsGroup">The work items group</param>
		/// <param name="wigStartInfo">Work item group start information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item</returns>
		public static WorkItem CreateWorkItem(
			IWorkItemsGroup workItemsGroup,
			WIGStartInfo wigStartInfo,
			WorkItemCallback callback, 
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
			CallToPostExecute callToPostExecute,
			WorkItemPriority workItemPriority)
		{

			ValidateCallback(callback);
			ValidateCallback(postExecuteWorkItemCallback);

			WorkItemInfo workItemInfo = new WorkItemInfo();
			workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext;
			workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext;
			workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback;
			workItemInfo.CallToPostExecute = callToPostExecute;
			workItemInfo.WorkItemPriority = workItemPriority;
			workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects;

			WorkItem workItem = new WorkItem(
				workItemsGroup,
				workItemInfo,
				callback, 
				state);
			
			return workItem;
		}

		private static void ValidateCallback(Delegate callback)
		{
            if (callback != null && callback.GetInvocationList().Length > 1)
			{
				throw new NotSupportedException("SmartThreadPool doesn't support delegates chains");
			}
		}
	}

	#endregion
}
namespace Amib.SSharpThreading
{
	#region WorkItemInfo class

	/// <summary>
	/// Summary description for WorkItemInfo.
	/// </summary>
	public class WorkItemInfo
	{
	    public WorkItemInfo()
		{
			UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext;
			UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext;
			DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects;
			CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute;
			PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback;
			WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority;
		}

		public WorkItemInfo(WorkItemInfo workItemInfo)
		{
			UseCallerCallContext = workItemInfo.UseCallerCallContext;
			UseCallerHttpContext = workItemInfo.UseCallerHttpContext;
			DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects;
			CallToPostExecute = workItemInfo.CallToPostExecute;
			PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback;
			WorkItemPriority = workItemInfo.WorkItemPriority;
            Timeout = workItemInfo.Timeout;
		}

	    /// <summary>
	    /// Get/Set if to use the caller's security context
	    /// </summary>
	    public bool UseCallerCallContext { get; set; }

	    /// <summary>
	    /// Get/Set if to use the caller's HTTP context
	    /// </summary>
	    public bool UseCallerHttpContext { get; set; }

	    /// <summary>
	    /// Get/Set if to dispose of the state object of a work item
	    /// </summary>
	    public bool DisposeOfStateObjects { get; set; }

	    /// <summary>
	    /// Get/Set the run the post execute options
	    /// </summary>
        public CallToPostExecute CallToPostExecute { get; set; }

	    /// <summary>
	    /// Get/Set the post execute callback
	    /// </summary>
        public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; }

	    /// <summary>
	    /// Get/Set the work item's priority
	    /// </summary>
	    public WorkItemPriority WorkItemPriority { get; set; }

	    /// <summary>
	    /// Get/Set the work item's timout in milliseconds.
        /// This is a passive timout. When the timout expires the work item won't be actively aborted!
	    /// </summary>
	    public long Timeout { get; set; }
	}

	#endregion
}
using System;

using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

namespace Amib.SSharpThreading.Internal
{
    #region WorkItemResultTWrapper class

    internal class WorkItemResultTWrapper<TResult> : IWorkItemResult<TResult>, IInternalWaitableResult
    {
        private readonly IWorkItemResult _workItemResult;

        public WorkItemResultTWrapper(IWorkItemResult workItemResult)
        {
            _workItemResult = workItemResult;
        }

        #region IWorkItemResult<TResult> Members

        public TResult GetResult()
        {
            return (TResult)_workItemResult.GetResult();
        }

        public TResult GetResult(int millisecondsTimeout, bool exitContext)
        {
            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext);
        }

        public TResult GetResult(TimeSpan timeout, bool exitContext)
        {
            return (TResult)_workItemResult.GetResult(timeout, exitContext);
        }

        public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle)
        {
            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle);
        }

        public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle)
        {
            return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle);
        }

        public TResult GetResult(out Exception e)
        {
            return (TResult)_workItemResult.GetResult(out e);
        }

        public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e)
        {
            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e);
        }

        public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e)
        {
            return (TResult)_workItemResult.GetResult(timeout, exitContext, out e);
        }

        public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
        {
            return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e);
        }

        public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e)
        {
            return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e);
        }

        public bool IsCompleted
        {
            get { return _workItemResult.IsCompleted; }
        }

        public bool IsCanceled
        {
            get { return _workItemResult.IsCanceled; }
        }

        public object State
        {
            get { return _workItemResult.State; }
        }

        public bool Cancel()
        {
            return _workItemResult.Cancel();
        }

        public bool Cancel(bool abortExecution)
        {
            return _workItemResult.Cancel(abortExecution);
        }

        public WorkItemPriority WorkItemPriority
        {
            get { return _workItemResult.WorkItemPriority; }
        }

        public TResult Result
        {
            get { return (TResult)_workItemResult.Result; }
        }

        public object Exception
        {
            get { return (TResult)_workItemResult.Exception; }
        }

        #region IInternalWorkItemResult Members

        public IWorkItemResult GetWorkItemResult()
        {
            return _workItemResult.GetWorkItemResult();
        }

        public IWorkItemResult<TRes> GetWorkItemResultT<TRes>()
        {
            return (IWorkItemResult<TRes>)this;
        }

        #endregion

        #endregion
    }

    #endregion

}
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;
using System.Runtime.CompilerServices;
//using SSMono.Diagnostics;

namespace Amib.SSharpThreading.Internal
{

	#region WorkItemsGroup class 

	/// <summary>
	/// Summary description for WorkItemsGroup.
	/// </summary>
	public class WorkItemsGroup : WorkItemsGroupBase
	{
		#region Private members

		private readonly object _lock = new object();

		/// <summary>
		/// A reference to the SmartThreadPool instance that created this 
		/// WorkItemsGroup.
		/// </summary>
		private readonly SmartThreadPool _stp;

		/// <summary>
		/// The OnIdle event
		/// </summary>
		private event WorkItemsGroupIdleHandler _onIdle;

        /// <summary>
        /// A flag to indicate if the Work Items Group is now suspended.
        /// </summary>
        private bool _isSuspended;

		/// <summary>
		/// Defines how many work items of this WorkItemsGroup can run at once.
		/// </summary>
		private int _concurrency;

		/// <summary>
		/// Priority queue to hold work items before they are passed 
		/// to the SmartThreadPool.
		/// </summary>
		private readonly PriorityQueue _workItemsQueue;

		/// <summary>
		/// Indicate how many work items are waiting in the SmartThreadPool
		/// queue.
		/// This value is used to apply the concurrency.
		/// </summary>
		private int _workItemsInStpQueue;

		/// <summary>
		/// Indicate how many work items are currently running in the SmartThreadPool.
		/// This value is used with the Cancel, to calculate if we can send new 
		/// work items to the STP.
		/// </summary>
		private int _workItemsExecutingInStp = 0;

		/// <summary>
		/// WorkItemsGroup start information
		/// </summary>
		private readonly WIGStartInfo _workItemsGroupStartInfo;

		/// <summary>
		/// Signaled when all of the WorkItemsGroup's work item completed.
		/// </summary>
        //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true);
        private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true);

		/// <summary>
		/// A common object for all the work items that this work items group
		/// generate so we can mark them to cancel in O(1)
		/// </summary>
		private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup();

		#endregion 

		#region Construction

	    public WorkItemsGroup(
			SmartThreadPool stp, 
			int concurrency, 
			WIGStartInfo wigStartInfo)
		{
			if (concurrency <= 0)
			{
				throw new ArgumentOutOfRangeException(
                    "concurrency",
#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE)
                    concurrency,
#endif
 "concurrency must be greater than zero");
			}
			_stp = stp;
			_concurrency = concurrency;
			_workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).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 WorkItemsGroupBase Overrides

        public override int Concurrency
        {
            get { return _concurrency; }
            set
            {
                //Debug.Assert(value > 0);

                int diff = value - _concurrency;
                _concurrency = value;
                if (diff > 0)
                {
                    EnqueueToSTPNextNWorkItem(diff);
                }
            }
        }

        public override int WaitingCallbacks
        {
            get { return _workItemsQueue.Count; }
        }

        public override object[] GetStates()
        {
            lock (_lock)
            {
                object[] states = new object[_workItemsQueue.Count];
                int i = 0;
                foreach (WorkItem workItem in _workItemsQueue)
                {
                    states[i] = workItem.GetWorkItemResult().State;
                    ++i;
                }
                return states;
            }
        }

	    /// <summary>
        /// WorkItemsGroup start information
        /// </summary>
        public override WIGStartInfo WIGStartInfo
        {
            get { return _workItemsGroupStartInfo; }
        }

	    /// <summary>
	    /// Start the Work Items Group if it was started suspended
	    /// </summary>
	    public override void Start()
	    {
	        // If the Work Items Group already started then quit
	        if (!_isSuspended)
	        {
	            return;
	        }
	        _isSuspended = false;
            
	        EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency));
	    }

	    public override void Cancel(bool abortExecution)
	    {
	        lock (_lock)
	        {
	            _canceledWorkItemsGroup.IsCanceled = true;
	            _workItemsQueue.Clear();
	            _workItemsInStpQueue = 0;
	            _canceledWorkItemsGroup = new CanceledWorkItemsGroup();
	        }

	        if (abortExecution)
	        {
	            _stp.CancelAbortWorkItemsGroup(this);
	        }
	    }

	    /// <summary>
        /// Wait for the thread pool to be idle
        /// </summary>
        public override bool WaitForIdle(int millisecondsTimeout)
        {
            SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this);
            return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false);
        }

	    public override event WorkItemsGroupIdleHandler OnIdle
		{
			add { _onIdle += value; }
			remove { _onIdle -= value; }
		}

	    #endregion 

		#region Private methods

	    private void RegisterToWorkItemCompletion(IWorkItemResult wir)
		{
			IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir;
			iwir.OnWorkItemStarted += OnWorkItemStartedCallback;
			iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback;
		}

	    public void OnSTPIsStarting()
		{
            if (_isSuspended)
            {
                return;
            }
			
            EnqueueToSTPNextNWorkItem(_concurrency);
		}

	    public void EnqueueToSTPNextNWorkItem(int count)
        {
            for (int i = 0; i < count; ++i)
            {
                EnqueueToSTPNextWorkItem(null, false);
            }
        }

		private object FireOnIdle(object state)
		{
			FireOnIdleImpl(_onIdle);
			return null;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle)
		{
			if(null == onIdle)
			{
				return;
			}

			Delegate[] delegates = onIdle.GetInvocationList();
			foreach(WorkItemsGroupIdleHandler eh in delegates)
			{
				try
				{
					eh(this);
				}
                catch { }  // Suppress exceptions
			}
		}

		private void OnWorkItemStartedCallback(WorkItem workItem)
		{
			lock(_lock)
			{
				++_workItemsExecutingInStp;
			}
		}

		private void OnWorkItemCompletedCallback(WorkItem workItem)
		{
			EnqueueToSTPNextWorkItem(null, true);
		}

        internal override void Enqueue(WorkItem workItem)
        {
            EnqueueToSTPNextWorkItem(workItem);
        }

	    private void EnqueueToSTPNextWorkItem(WorkItem workItem)
		{
			EnqueueToSTPNextWorkItem(workItem, false);
		}

		private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue)
		{
			lock(_lock)
			{
				// Got here from OnWorkItemCompletedCallback()
				if (decrementWorkItemsInStpQueue)
				{
					--_workItemsInStpQueue;

					if(_workItemsInStpQueue < 0)
					{
						_workItemsInStpQueue = 0;
					}

					--_workItemsExecutingInStp;

					if(_workItemsExecutingInStp < 0)
					{
						_workItemsExecutingInStp = 0;
					}
				}

				// If the work item is not null then enqueue it
				if (null != workItem)
				{
					workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup;

					RegisterToWorkItemCompletion(workItem.GetWorkItemResult());
					_workItemsQueue.Enqueue(workItem);
					//_stp.IncrementWorkItemsCount();

					if ((1 == _workItemsQueue.Count) && 
						(0 == _workItemsInStpQueue))
					{
						_stp.RegisterWorkItemsGroup(this);
                        IsIdle = false;
                        _isIdleWaitHandle.Reset();
					}
				}

				// If the work items queue of the group is empty than quit
				if (0 == _workItemsQueue.Count)
				{
					if (0 == _workItemsInStpQueue)
					{
						_stp.UnregisterWorkItemsGroup(this);
                        IsIdle = true;
                        _isIdleWaitHandle.Set();
                        if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0)
                        {
                            _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle));
                        }
					}
					return;
				}

                if (!_isSuspended)
				{
					if (_workItemsInStpQueue < _concurrency)
					{
						WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem;
                        try
                        {
                            _stp.Enqueue(nextWorkItem);
                        }
                        catch (ObjectDisposedException e)
                        {
                            e.GetHashCode();
                            // The STP has been shutdown
                        }

						++_workItemsInStpQueue;
					}
				}
			}
		}

		#endregion
    }

	#endregion
}
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;

namespace Amib.SSharpThreading.Internal
	{
	public abstract class WorkItemsGroupBase : IWorkItemsGroup
		{
		#region Private Fields

		/// <summary>
		/// Contains the name of this instance of SmartThreadPool.
		/// Can be changed by the user.
		/// </summary>
		private string _name = "WorkItemsGroupBase";

		public WorkItemsGroupBase ()
			{
			IsIdle = true;
			}

		#endregion

		#region IWorkItemsGroup Members

		#region Public Methods

		/// <summary>
		/// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance
		/// </summary>
		public string Name
			{
			get { return _name; }
			set { _name = value; }
			}

		#endregion

		#region Abstract Methods

		public abstract int Concurrency { get; set; }
		public abstract int WaitingCallbacks { get; }
		public abstract object[] GetStates ();
		public abstract WIGStartInfo WIGStartInfo { get; }
		public abstract void Start ();
		public abstract void Cancel (bool abortExecution);
		public abstract bool WaitForIdle (int millisecondsTimeout);
		public abstract event WorkItemsGroupIdleHandler OnIdle;

		internal abstract void Enqueue (WorkItem workItem);
		internal virtual void PreQueueWorkItem () { }

		#endregion

		#region Common Base Methods

		/// <summary>
		/// Cancel all the work items.
		/// Same as Cancel(false)
		/// </summary>
		public virtual void Cancel ()
			{
			Cancel (false);
			}

		/// <summary>
		/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
		/// </summary>
		public void WaitForIdle ()
			{
			WaitForIdle (Timeout.Infinite);
			}

		/// <summary>
		/// Wait for the SmartThreadPool/WorkItemsGroup to be idle
		/// </summary>
		public bool WaitForIdle (TimeSpan timeout)
			{
			return WaitForIdle ((int)timeout.TotalMilliseconds);
			}

		/// <summary>
		/// IsIdle is true when there are no work items running or queued.
		/// </summary>
		public bool IsIdle { get; protected set; }

		#endregion

		#region QueueWorkItem

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (WorkItemCallback callback)
			{
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="workItemPriority">The priority of the work item</param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (WorkItemCallback callback, WorkItemPriority workItemPriority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, workItemPriority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="workItemInfo">Work item info</param>
		/// <param name="callback">A callback to execute</param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, workItemInfo, callback);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state)
			{
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (WorkItemCallback callback, object state, WorkItemPriority workItemPriority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, workItemPriority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="workItemInfo">Work item information</param>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (WorkItemInfo workItemInfo, WorkItemCallback callback, object state)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, workItemInfo, callback, state);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (
			WorkItemCallback callback,
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (
			WorkItemCallback callback,
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
			WorkItemPriority workItemPriority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (
			WorkItemCallback callback,
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
			CallToPostExecute callToPostExecute)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		/// <summary>
		/// Queue a work item
		/// </summary>
		/// <param name="callback">A callback to execute</param>
		/// <param name="state">
		/// The context object of the work item. Used for passing arguments to the work item. 
		/// </param>
		/// <param name="postExecuteWorkItemCallback">
		/// A delegate to call after the callback completion
		/// </param>
		/// <param name="callToPostExecute">Indicates on which cases to call to the post execute callback</param>
		/// <param name="workItemPriority">The work item priority</param>
		/// <returns>Returns a work item result</returns>
		public IWorkItemResult QueueWorkItem (
			WorkItemCallback callback,
			object state,
			PostExecuteWorkItemCallback postExecuteWorkItemCallback,
			CallToPostExecute callToPostExecute,
			WorkItemPriority workItemPriority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		#endregion

		#region QueueWorkItem(Action<...>)

		public IWorkItemResult QueueWorkItem (Action action)
			{
			return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
				this,
				WIGStartInfo,
				delegate
					{
					action.Invoke ();
					return null;
					}, priority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		public IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg)
			{
			return QueueWorkItem (action, arg, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult QueueWorkItem<T> (Action<T> action, T arg, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
				this,
				WIGStartInfo,
				state =>
					{
					action.Invoke (arg);
					return null;
					},
				WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		public IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2)
			{
			return QueueWorkItem (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult QueueWorkItem<T1, T2> (Action<T1, T2> action, T1 arg1, T2 arg2, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
				this,
				WIGStartInfo,
				state =>
					{
					action.Invoke (arg1, arg2);
					return null;
					},
				WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		public IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
			{
			return QueueWorkItem (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult QueueWorkItem<T1, T2, T3> (Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
				this,
				WIGStartInfo,
				state =>
					{
					action.Invoke (arg1, arg2, arg3);
					return null;
					},
				WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		public IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
			{
			return QueueWorkItem (action, arg1, arg2, arg3, arg4, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult QueueWorkItem<T1, T2, T3, T4> (
			Action<T1, T2, T3, T4> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
						   this,
						   WIGStartInfo,
						   state =>
							   {
							   action.Invoke (arg1, arg2, arg3, arg4);
							   return null;
							   },
						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority);
			Enqueue (workItem);
			return workItem.GetWorkItemResult ();
			}

		#endregion

		#region QueueWorkItem(Func<...>)

		public IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func)
			{
			return QueueWorkItem (func, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult<TResult> QueueWorkItem<TResult> (Func<TResult> func, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
							this,
							WIGStartInfo,
							state =>
								{
								return func.Invoke ();
								}, priority);
			Enqueue (workItem);
			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
			}

		public IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg)
			{
			return QueueWorkItem (func, arg, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult<TResult> QueueWorkItem<T, TResult> (Func<T, TResult> func, T arg, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
				this,
				WIGStartInfo,
				state =>
					{
					return func.Invoke (arg);
					},
				WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null,
				priority);
			Enqueue (workItem);
			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
			}

		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2)
			{
			return QueueWorkItem (func, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, TResult> (Func<T1, T2, TResult> func, T1 arg1, T2 arg2, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
							this,
							WIGStartInfo,
							state =>
								{
								return func.Invoke (arg1, arg2);
								},
						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null,
						   priority);
			Enqueue (workItem);
			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
			}

		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3)
			{
			return QueueWorkItem (func, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, TResult> (
			Func<T1, T2, T3, TResult> func, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
							this,
							WIGStartInfo,
							state =>
								{
								return func.Invoke (arg1, arg2, arg3);
								},
						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null,
						   priority);
			Enqueue (workItem);
			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
			}

		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
			{
			return QueueWorkItem (func, arg1, arg2, arg3, arg4, SmartThreadPool.DefaultWorkItemPriority);
			}

		public IWorkItemResult<TResult> QueueWorkItem<T1, T2, T3, T4, TResult> (
			Func<T1, T2, T3, T4, TResult> func, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority)
			{
			PreQueueWorkItem ();
			WorkItem workItem = WorkItemFactory.CreateWorkItem (
							this,
							WIGStartInfo,
							state =>
								{
								return func.Invoke (arg1, arg2, arg3, arg4);
								},
						   WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null,
						   priority);
			Enqueue (workItem);
			return new WorkItemResultTWrapper<TResult> (workItem.GetWorkItemResult ());
			}

		#endregion

		#endregion
		}
	}
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

namespace Amib.SSharpThreading.Internal
{
	#region WorkItemsQueue class

	/// <summary>
	/// WorkItemsQueue class.
	/// </summary>
	public class WorkItemsQueue : IDisposable
	{
		#region Member variables

		/// <summary>
		/// Waiters queue (implemented as stack).
		/// </summary>
		private readonly WaiterEntry _headWaiterEntry = new WaiterEntry();

		/// <summary>
		/// Waiters count
		/// </summary>
		private int _waitersCount = 0;

		/// <summary>
		/// Work items queue
		/// </summary>
		private readonly PriorityQueue _workItems = new PriorityQueue();

        /// <summary>
        /// Indicate that work items are allowed to be queued
        /// </summary>
        private bool _isWorkItemsQueueActive = true;


#if (WINDOWS_PHONE) 
        private static readonly Dictionary<int, WaiterEntry> _waiterEntries = new Dictionary<int, WaiterEntry>();
#elif (_WINDOWS_CE)
        private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot();
#else

        [ThreadStatic]
        private static WaiterEntry _waiterEntry;
#endif


        /// <summary>
        /// Each thread in the thread pool keeps its own waiter entry.
        /// </summary>
        private static WaiterEntry CurrentWaiterEntry
        {
#if (WINDOWS_PHONE) 
            get
            {
                lock (_waiterEntries)
                {
                    WaiterEntry waiterEntry;
                    if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry))
                    {
                        return waiterEntry;
                    }
                }
                return null;
            }
            set
            {
                lock (_waiterEntries)
                {
                    _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value;
                }
            }
#elif (_WINDOWS_CE)
            get
            {
                return Thread.GetData(_waiterEntrySlot) as WaiterEntry;
            }
            set
            {
                Thread.SetData(_waiterEntrySlot, value);
            }
#else
            get
            {
                return _waiterEntry;
            }
            set
            {
                _waiterEntry = value;
            }
#endif
        }

        /// <summary>
		/// A flag that indicates if the WorkItemsQueue has been disposed.
		/// </summary>
        private bool _isDisposed = false;

		#endregion

		#region Public properties

        /// <summary>
        /// Returns the current number of work items in the queue
        /// </summary>
		public int Count
		{
			get
			{
                return _workItems.Count;
			}
		}

		/// <summary>
		/// Returns the current number of waiters
		/// </summary>
		public int WaitersCount
		{
			get
			{
				return _waitersCount;
			}
		}


        #endregion

        #region Public methods

		/// <summary>
		/// Enqueue a work item to the queue.
		/// </summary>
		public bool EnqueueWorkItem(WorkItem workItem)
		{
			// A work item cannot be null, since null is used in the
			// WaitForWorkItem() method to indicate timeout or cancel
			if (null == workItem)
			{
				throw new ArgumentNullException("workItem" , "workItem cannot be null");
			}

			bool enqueue = true;

			// First check if there is a waiter waiting for work item. During 
			// the check, timed out waiters are ignored. If there is no 
			// waiter then the work item is queued.
			lock(this)
			{
                ValidateNotDisposed();

                if (!_isWorkItemsQueueActive)
                {
                    return false;
                }

				while(_waitersCount > 0)
				{
					// Dequeue a waiter.
					WaiterEntry waiterEntry = PopWaiter();

					// Signal the waiter. On success break the loop
					if (waiterEntry.Signal(workItem))
					{
						enqueue = false;
						break;
					}
				}

				if (enqueue)
				{
					// Enqueue the work item
					_workItems.Enqueue(workItem);
				}
			}
            return true;
		}


		/// <summary>
		/// Waits for a work item or exits on timeout or cancel
		/// </summary>
		/// <param name="millisecondsTimeout">Timeout in milliseconds</param>
		/// <param name="cancelEvent">Cancel wait handle</param>
		/// <returns>Returns true if the resource was granted</returns>
		public WorkItem DequeueWorkItem(
			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 it.
			// 
			// If there are no waiting work items then the caller 
			// is queued between other waiters for a work item to arrive.
			// 
			// If a work item didn't come within millisecondsTimeout or 
			// the user canceled the wait by signaling the cancelEvent 
			// then the method returns null to indicate that the caller 
			// didn't get a work item.

			WaiterEntry waiterEntry;
			WorkItem workItem = null;
		    lock (this)
		    {
                ValidateNotDisposed();

                // If there are waiting work items then take one and return.
                if (_workItems.Count > 0)
                {
                    workItem = _workItems.Dequeue() as WorkItem;
                    return workItem;
                }

                // No waiting work items ...

                // Get the waiter entry for the waiters queue
                waiterEntry = GetThreadWaiterEntry();

                // Put the waiter with the other waiters
                PushWaiter(waiterEntry);
		    }

		    // Prepare array of wait handle for the WaitHandle.WaitAny()
            WaitHandle [] waitHandles = new WaitHandle[] { 
																waiterEntry.WaitHandle, 
																cancelEvent };

			// Wait for an available resource, cancel event, or timeout.

			// 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 two lock(this) 
			// statments instead of one.

            int index = STPEventWaitHandle.WaitAny(
				waitHandles,
				millisecondsTimeout, 
				true);

			lock(this)
			{
				// success is true if it got a work item.
				bool success = (0 == index);

				// The timeout variable is used only for readability.
				// (We treat cancel as timeout)
				bool timeout = !success;

				// On timeout update the waiterEntry that it is timed out
				if (timeout)
				{
					// The Timeout() fails if the waiter has already been signaled
					timeout = waiterEntry.Timeout();

					// On timeout remove the waiter from the queue.
					// Note that the complexity is O(1).
					if(timeout)
					{
						RemoveWaiter(waiterEntry, false);
					}

					// Again readability
					success = !timeout;
				}

				// On success return the work item
				if (success)
				{
					workItem = waiterEntry.WorkItem;

					if (null == workItem)
					{
						workItem = _workItems.Dequeue() as WorkItem;
					}
				}
			}
			// On failure return null.
			return workItem;
		}

        /// <summary>
        /// Cleanup the work items queue, hence no more work 
        /// items are allowed to be queue
        /// </summary>
        private void Cleanup()
        {
            lock(this)
            {
                // Deactivate only once
                if (!_isWorkItemsQueueActive)
                {
                    return;
                }

                // Don't queue more work items
                _isWorkItemsQueueActive = false;

                foreach(WorkItem workItem in _workItems)
                {
                    workItem.DisposeOfState();
                }

                // Clear the work items that are already queued
                _workItems.Clear();

                // Note: 
                // I don't iterate over the queue and dispose of work items's states, 
                // since if a work item has a state object that is still in use in the 
                // application then I must not dispose it.

                // Tell the waiters that they were timed out.
                // It won't signal them to exit, but to ignore their
                // next work item.
				while(_waitersCount > 0)
				{
					WaiterEntry waiterEntry = PopWaiter();
					waiterEntry.Timeout();
				}
            }
        }

        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

		/// <summary>
		/// Returns the WaiterEntry of the current thread
		/// </summary>
		/// <returns></returns>
		/// In order to avoid creation and destuction of WaiterEntry
		/// objects each thread has its own WaiterEntry object.
		private static WaiterEntry GetThreadWaiterEntry()
		{
			if (null == CurrentWaiterEntry)
			{
				CurrentWaiterEntry = new WaiterEntry();
			}
			CurrentWaiterEntry.Reset();
			return CurrentWaiterEntry;
		}

		#region Waiters stack methods

		/// <summary>
		/// Push a new waiter into the waiter's stack
		/// </summary>
		/// <param name="newWaiterEntry">A waiter to put in the stack</param>
		public void PushWaiter(WaiterEntry newWaiterEntry)
		{
			// Remove the waiter if it is already in the stack and 
			// update waiter's count as needed
			RemoveWaiter(newWaiterEntry, false);

			// If the stack is empty then newWaiterEntry is the new head of the stack 
			if (null == _headWaiterEntry._nextWaiterEntry)
			{
				_headWaiterEntry._nextWaiterEntry = newWaiterEntry;
				newWaiterEntry._prevWaiterEntry = _headWaiterEntry;

			}
			// If the stack is not empty then put newWaiterEntry as the new head 
			// of the stack.
			else
			{
				// Save the old first waiter entry
				WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;

                // Update the links
				_headWaiterEntry._nextWaiterEntry = newWaiterEntry;
				newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry;
				newWaiterEntry._prevWaiterEntry = _headWaiterEntry;
				oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry;
			}

			// Increment the number of waiters
			++_waitersCount;
		}

		/// <summary>
		/// Pop a waiter from the waiter's stack
		/// </summary>
		/// <returns>Returns the first waiter in the stack</returns>
		private WaiterEntry PopWaiter()
		{
			// Store the current stack head
			WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry;

			// Store the new stack head
			WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry;

			// Update the old stack head list links and decrement the number
			// waiters.
			RemoveWaiter(oldFirstWaiterEntry, true);

			// Update the new stack head
			_headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry;
			if (null != newHeadWaiterEntry)
			{
				newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry;
			}

			// Return the old stack head
			return oldFirstWaiterEntry;
		}

		/// <summary>
		/// Remove a waiter from the stack
		/// </summary>
		/// <param name="waiterEntry">A waiter entry to remove</param>
		/// <param name="popDecrement">If true the waiter count is always decremented</param>
		private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement)
		{
			// Store the prev entry in the list
			WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry;

			// Store the next entry in the list
			WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry;

			// A flag to indicate if we need to decrement the waiters count.
			// If we got here from PopWaiter then we must decrement.
			// If we got here from PushWaiter then we decrement only if
			// the waiter was already in the stack.
			bool decrementCounter = popDecrement;

			// Null the waiter's entry links
			waiterEntry._prevWaiterEntry = null;
			waiterEntry._nextWaiterEntry = null;

			// If the waiter entry had a prev link then update it.
			// It also means that the waiter is already in the list and we
			// need to decrement the waiters count.
			if (null != prevWaiterEntry)
			{
				prevWaiterEntry._nextWaiterEntry = nextWaiterEntry;
				decrementCounter = true;
			}

			// If the waiter entry had a next link then update it.
			// It also means that the waiter is already in the list and we
			// need to decrement the waiters count.
			if (null != nextWaiterEntry)
			{
				nextWaiterEntry._prevWaiterEntry = prevWaiterEntry;
				decrementCounter = true;
			}

			// Decrement the waiters count if needed
			if (decrementCounter)
			{
				--_waitersCount;
			}
		}

		#endregion

		#endregion

		#region WaiterEntry class 

		// A waiter entry in the _waiters queue.
		public sealed class WaiterEntry : IDisposable
		{
			#region Member variables

			/// <summary>
			/// Event to signal the waiter that it got the work item.
			/// </summary>
			//private AutoResetEvent _waitHandle = new AutoResetEvent(false);
            private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent();

			/// <summary>
			/// Flag to know if this waiter already quited from the queue 
			/// because of a timeout.
			/// </summary>
			private bool _isTimedout = false;

			/// <summary>
			/// Flag to know if the waiter was signaled and got a work item. 
			/// </summary>
			private bool _isSignaled = false;

            /// <summary>
            /// A work item that passed directly to the waiter withou going 
            /// through the queue
            /// </summary>
			private WorkItem _workItem = null;

            private bool _isDisposed = false;

			// Linked list members
			internal WaiterEntry _nextWaiterEntry = null;
			internal WaiterEntry _prevWaiterEntry = null;

			#endregion

			#region Construction

			public WaiterEntry()
			{
				Reset();
			}
						
			#endregion

			#region Public methods

			public WaitHandle WaitHandle
			{
				get { return _waitHandle; }
			}

			public WorkItem WorkItem
			{
				get
				{
					return _workItem;
				}
			}

			/// <summary>
			/// Signal the waiter that it got a work item.
			/// </summary>
			/// <returns>Return true on success</returns>
			/// The method fails if Timeout() preceded its call
			public bool Signal(WorkItem workItem)
			{
				lock(this)
				{
					if (!_isTimedout)
					{
						_workItem = workItem;
						_isSignaled = true;
						_waitHandle.Set();
						return true;
					}
				}
				return false;
			}

			/// <summary>
			/// Mark the wait entry that it has been timed out
			/// </summary>
			/// <returns>Return true on success</returns>
			/// The method fails if Signal() preceded its call
			public bool Timeout()
			{
				lock(this)
				{
					// Time out can happen only if the waiter wasn't marked as
					// signaled
					if (!_isSignaled)
					{
						// We don't remove the waiter from the queue, the DequeueWorkItem 
                        // method skips _waiters that were timed out.
						_isTimedout = true;
						return true;
					}
				}
				return false;
			}

			/// <summary>
			/// Reset the wait entry so it can be used again
			/// </summary>
			public void Reset()
			{
				_workItem = null;
				_isTimedout = false;
				_isSignaled = false;
				_waitHandle.Reset();
			}

			/// <summary>
			/// Free resources
			/// </summary>
			public void Close()
			{
				if (null != _waitHandle)
				{
					_waitHandle.Close();
					_waitHandle = null;
				}
			}

			#endregion

			#region IDisposable Members

			public void Dispose()
			{
                lock (this)
                {
                    if (!_isDisposed)
                    {
                        Close();
                    }
                    _isDisposed = true;
                }
            }

			#endregion
		}

		#endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (!_isDisposed)
            {
                Cleanup();
            }
            _isDisposed = true;
        }

        private void ValidateNotDisposed()
        {
            if(_isDisposed)
            {
                throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown");
            }
        }

        #endregion
    }

	#endregion
}

using System;
using System.Collections;

/*
 * The code below generates permutations.
 * 
 * The original code was written by Michael Gilleland,
 * and can be found in the following site 
 * http://www.merriampark.com/perm.htm
 * 
 * I translated it to C# from Java.
 */
namespace SmartThreadPoolTests
{
	//--------------------------------------
	// Systematically generate permutations. 
	//--------------------------------------

	public class PermutationGenerator : IEnumerable
	{
		private object [] _objects;

		public PermutationGenerator(object [] objects)
		{
			_objects = (object [])objects.Clone();
		}

		#region IEnumerable Members

		public IEnumerator GetEnumerator()
		{
			return new PermutationGeneratorEnumerator(_objects);
		}

		#endregion

		private class PermutationGeneratorEnumerator : IEnumerator
		{
			private object [] _objects;
			private object [] _currentPermutation;
			private PermutationGeneratorHelper _permutationGeneratorHelper;

			public PermutationGeneratorEnumerator(object [] objects)
			{
				_objects = objects;
				Reset();
			}

			#region IEnumerator Members

			public void Reset()
			{
				_permutationGeneratorHelper = new PermutationGeneratorHelper(_objects.Length);
			}

			public object Current
			{
				get
				{
					return _currentPermutation;
				}
			}

			public bool MoveNext()
			{
				if (_permutationGeneratorHelper.hasMore())
				{
					_currentPermutation = new object[_objects.Length];
					int [] indices = _permutationGeneratorHelper.getNext();
					for (int i = 0; i < indices.Length; i++) 
					{
						_currentPermutation[i] = _objects[indices[i]];
					}
					return true;
				}
				_currentPermutation = null;
				return false;
			}

			#endregion
		}


		private class PermutationGeneratorHelper
		{

			private int[] a;
			private long numLeft;
			private long total;

			//-----------------------------------------------------------
			// Constructor. WARNING: Don't make n too large.
			// Recall that the number of permutations is n!
			// which can be very large, even when n is as small as 20 --
			// 20! = 2,432,902,008,176,640,000 and
			// 21! is too big to fit into a Java long, which is
			// why we use long instead.
			//----------------------------------------------------------

			public PermutationGeneratorHelper (int n) 
			{
				if (n < 1) 
				{
					throw new ArgumentOutOfRangeException("n", "Min 1");
				}
				a = new int[n];
				total = getFactorial (n);
				reset();
			}

			//------
			// Reset
			//------

			public void reset () 
			{
				for (int i = 0; i < a.Length; i++) 
				{
					a[i] = i;
				}
				numLeft = total;
			}

			//------------------------------------------------
			// Return number of permutations not yet generated
			//------------------------------------------------

			public long getNumLeft () 
			{
				return numLeft;
			}

			//------------------------------------
			// Return total number of permutations
			//------------------------------------

			public long getTotal () 
			{
				return total;
			}

			//-----------------------------
			// Are there more permutations?
			//-----------------------------

			public bool hasMore () 
			{
				return (numLeft > 0);
			}

			//------------------
			// Compute factorial
			//------------------

			private static long getFactorial (int n) 
			{
				long fact = 1;
				for (int i = n; i > 1; i--) 
				{
					fact = fact * i;
				}
				return fact;
			}

			//--------------------------------------------------------
			// Generate next permutation (algorithm from Rosen p. 284)
			//--------------------------------------------------------

			public int[] getNext () 
			{

				if (numLeft == total) 
				{
					--numLeft;
					return a;
				}

				int temp;

				// Find largest index j with a[j] < a[j+1]

				int j = a.Length - 2;
				while (a[j] > a[j+1]) 
				{
					j--;
				}

				// Find index k such that a[k] is smallest integer
				// greater than a[j] to the right of a[j]

				int k = a.Length - 1;
				while (a[j] > a[k]) 
				{
					k--;
				}

				// Interchange a[j] and a[k]

				temp = a[k];
				a[k] = a[j];
				a[j] = temp;

				// Put tail end of permutation after jth position in increasing order

				int r = a.Length - 1;
				int s = j + 1;

				while (r > s) 
				{
					temp = a[s];
					a[s] = a[r];
					a[r] = temp;
					r--;
					s++;
				}

				--numLeft;
				return a;
			}
		}
	}
}
ï»¿using System.Reflection;

[assembly: AssemblyTitle ("SSharpSmartThreadPoolTests")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("SSharpSmartThreadPoolTests")]
[assembly: AssemblyCopyright ("Copyright Â©  2015")]
[assembly: AssemblyVersion ("1.0.0.*")]

using System;
using Crestron.SimplSharpPro.CrestronThread;
using Amib.SSharpThreading.Internal;
using NUnit.Framework;

using Amib.SSharpThreading;
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((WorkItemCallback) 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 class WorkItemInfoComparer
        {
            private WorkItemInfo _neededWorkItemInfo;
            private object _state;

            public int SleepTime { get; set; }

            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 = SmartThreadPool.CurrentThreadEntry.CurrentWorkItem.WorkItemInfo;
                    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;
            }
        }
    }
}
ï»¿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>9.0.30729</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{43AEC3F3-0D6D-406C-B4B1-45FCEAA8817A}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>SSharpSmartThreadPoolTests</RootNamespace>
    <AssemblyName>SSharpSmartThreadPoolTests</AssemblyName>
    <ProjectTypeGuids>{0B4745B0-194B-4BB6-8E21-E9057CA92300};{4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <PlatformFamilyName>WindowsCE</PlatformFamilyName>
    <PlatformID>E2BECB1F-8C8C-41ba-B736-9BE7D946A398</PlatformID>
    <OSVersion>5.0</OSVersion>
    <DeployDirSuffix>SmartDeviceProject1</DeployDirSuffix>
    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
    <NativePlatformName>Windows CE</NativePlatformName>
    <FormFactorID>
    </FormFactorID>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE;_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <FileAlignment>512</FileAlignment>
    <NoStdLib>true</NoStdLib>
    <NoConfig>true</NoConfig>
    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
    <DebugType>none</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <FileAlignment>512</FileAlignment>
    <NoStdLib>true</NoStdLib>
    <NoConfig>true</NoConfig>
    <GenerateSerializationAssemblies>off</GenerateSerializationAssemblies>
    <DefineConstants>_WINDOWS_CE;_WINDOWS;SSHARP</DefineConstants>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="mscorlib" />
    <Reference Include="SimplSharpCustomAttributesInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="SimplSharpHelperInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="SimplSharpPro, Version=1.4.0.14, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="SSharpCrestronExtensionsLibrary, Version=1.0.0.13603, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\Simpl# Libraries\SSharpCrestronExtensionsLibrary\SSharpCrestronExtensionsLibrary\bin\Release\SSharpCrestronExtensionsLibrary.dll</HintPath>
    </Reference>
    <Reference Include="SSharpNunitLite, Version=3.0.5543.22955, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\SSharpNUnitLite3\SSharpNunitLite\bin\Release\SSharpNunitLite.dll</HintPath>
    </Reference>
    <Reference Include="SSharpThreadingLibrary, Version=1.0.0.15966, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\Simpl# Libraries\SSharpThreadingLibrary\SSharpThreadingLibrary\bin\Release\SSharpThreadingLibrary.dll</HintPath>
    </Reference>
    <Reference Include="SSMonoDiagnosticsLibrary, Version=1.0.0.23683, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\Simpl# Libraries\SSMonoDiagnosticsLibrary\SSMonoDiagnosticsLibrary\bin\Release\SSMonoDiagnosticsLibrary.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Data" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="PermutationGenerator.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="QueueWorkItemHelper.cs" />
    <Compile Include="TestActionT.cs" />
    <Compile Include="TestCancel.cs" />
    <Compile Include="TestChainedDelegates.cs" />
    <Compile Include="TestConcurrencyChanges.cs" />
    <Compile Include="TestExceptions.cs" />
    <Compile Include="TestFalseFillStateWithParams.cs" />
    <Compile Include="TestFillStateWithParams.cs" />
    <Compile Include="TestFunc.cs" />
    <Compile Include="TestFuncT.cs" />
    <Compile Include="TestGetResult.cs" />
    <Compile Include="TestMultipleWorkItems.cs" />
    <Compile Include="TestParallelMethods.cs" />
    <Compile Include="TestPostExecute.cs" />
    <Compile Include="TestPriorityQueue.cs" />
    <Compile Include="TestQueueWorkItem.cs" />
    <Compile Include="TestStartSuspended.cs" />
    <Compile Include="TestStateDispose.cs" />
    <Compile Include="TestThreadPriority.cs" />
    <Compile Include="TestThreadsCreate.cs" />
    <Compile Include="TestWaitForIdle.cs" />
    <Compile Include="TestWIGActionT.cs" />
    <Compile Include="TestWIGChainedDelegates.cs" />
    <Compile Include="TestWIGConcurrency.cs" />
    <Compile Include="TestWIGConcurrencyChanges.cs" />
    <Compile Include="TestWIGExceptions.cs" />
    <Compile Include="TestWIGFillStateWithParams.cs" />
    <Compile Include="TestWIGFuncT.cs" />
    <Compile Include="TestWIGGetResult.cs" />
    <Compile Include="TestWIGMultipleWorkItems.cs" />
    <Compile Include="TestWIGPostExecute.cs" />
    <Compile Include="TestWIGQueueWorkItem.cs" />
    <Compile Include="TestWIGStateDispose.cs" />
    <Compile Include="TestWIGWaitForIdle.cs" />
    <Compile Include="TestWorkItemsGroups.cs" />
    <Compile Include="TestWorkItemsQueue.cs" />
    <Compile Include="TestWorkItemTimeout.cs" />
    <None Include="Properties\ControlSystem.cfg" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\SSharpSmartThreadPool\SSharpSmartThreadPool.csproj">
      <Project>{06299ED2-056A-427C-8C84-D716197080EB}</Project>
      <Name>SSharpSmartThreadPool</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{0B4745B0-194B-4BB6-8E21-E9057CA92300}">
        <ProgramIdTag>SSharpSmartThreadPoolTests</ProgramIdTag>
        <SystemName>SSharpSmartThreadPoolTests</SystemName>
        <Programmer />
        <ArchiveFilename>C:\Projects\Crestron\Simpl# Pro Libraries\SSharpSmartThreadPool\SSharpSmartThreadPoolTests\bin\Debug\SSharpSmartThreadPoolTests.cplz</ArchiveFilename>
        <MinFirmwareVersion>1.009.0029</MinFirmwareVersion>
        <CompiledOn>3/14/2015 11:00:21 AM</CompiledOn>
        <AdditionalInfo />
        <EmbedSourceArchive>False</EmbedSourceArchive>
        <CopyTo />
        <OriginalReferenceSources>
          <Reference sourceFilename="../../SSharpNUnitLite3/SSharpNunitLite/bin/Release/SSharpNunitLite.dll" referenceName="SSharpNunitLite" />
          <Reference sourceFilename="../../../Simpl%23%20Libraries/SSharpCrestronExtensionsLibrary/SSharpCrestronExtensionsLibrary/bin/Release/SSharpCrestronExtensionsLibrary.dll" referenceName="SSharpCrestronExtensionsLibrary" />
        </OriginalReferenceSources>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>
  <PropertyGroup>
    <PostBuildEvent>rem S# Pro preparation will execute after these operations</PostBuildEvent>
  </PropertyGroup>
</Project>
using Amib.SSharpThreading;
using NUnit.Framework;

namespace SmartThreadPoolTests
{
    /// <summary>
    /// Summary description for TestCancel.
    /// </summary>
    [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;
            }
    }
}
using Crestron.SimplSharpPro.CrestronThread;
using Crestron.SimplSharp;
using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
	{
	/// <summary>
	/// Summary description for TestCancel.
	/// </summary>
	[TestFixture]
	[Category ("TestCancel")]
	public class TestCancel
		{
		/// <summary>
		/// 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
		/// </summary>        
		[Test]
		public void CancelInQueueWorkItem ()
			{
			Assert.Throws<WorkItemCancelException> (() =>
				{
					STPStartInfo stpStartInfo = new STPStartInfo ();
					stpStartInfo.StartSuspended = true;

					SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
					IWorkItemResult wir = stp.QueueWorkItem (arg => null);

					wir.Cancel ();

					Assert.IsTrue (wir.IsCanceled);

					try
						{
						wir.GetResult ();
						}
					finally
						{
						stp.Shutdown ();
						}
				});
			}

		/// <summary>
		/// 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
		/// </summary>        
		[Test]
		public void CancelInProgressWorkItemSoft ()
			{
			Assert.Throws<WorkItemCancelException> (() =>
				{
					ManualResetEvent waitToStart = new ManualResetEvent (false);

					SmartThreadPool stp = new SmartThreadPool ();
					IWorkItemResult wir = stp.QueueWorkItem (state =>
						{
							waitToStart.Set ();
							Thread.Sleep (100);
							return null;
						});

					waitToStart.WaitOne ();

					wir.Cancel (false);

					Assert.IsTrue (wir.IsCanceled);

					try
						{
						wir.GetResult ();
						}
					finally
						{
						stp.Shutdown ();
						}
				});
			}

		/// <summary>
		/// 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
		/// </summary>        
		[Test]
		public void CancelCancelledWorkItemAbort ()
			{
			Assert.Throws<WorkItemCancelException> (() =>
				{
					ManualResetEvent waitToStart = new ManualResetEvent (false);

					SmartThreadPool stp = new SmartThreadPool ();
					IWorkItemResult wir = stp.QueueWorkItem (state =>
						{
							waitToStart.Set ();
							while (true)
								{
								Thread.Sleep (1000);
								}
							return null;
						});

					waitToStart.WaitOne ();

					wir.Cancel (false);

					Assert.IsTrue (wir.IsCanceled);

					bool completed = stp.WaitForIdle (1000);

					Assert.IsFalse (completed);

					wir.Cancel (true);

					try
						{
						wir.GetResult ();
						}
					finally
						{
						stp.Shutdown ();
						}
				});
			}


		/// <summary>
		/// 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
		/// </summary>        
		[Test]
		public void CancelInProgressWorkItemAbort ()
			{
			Assert.Throws<WorkItemCancelException> (() =>
				{
					ManualResetEvent waitToStart = new ManualResetEvent (false);
					int counter = 0;

					SmartThreadPool stp = new SmartThreadPool ();
					IWorkItemResult wir = stp.QueueWorkItem (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 ();
						}
				});
			}

		/// <summary>
		/// 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
		/// </summary>        
		[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 (
				state =>
					{
					waitToStart.Set ();
					waitToComplete.WaitOne ();
					cancelled = SmartThreadPool.IsWorkItemCanceled;
					return null;
					}
				);

			waitToStart.WaitOne ();

			wir.Cancel (false);

			waitToComplete.Set ();

			stp.WaitForIdle ();

			Assert.IsTrue (cancelled);

			stp.Shutdown ();
			}

		/// <summary>
		/// 1. Create STP
		/// 2. Queue work item that takes some time
		/// 3. Wait for it to start
		/// 4. Cancel the work item (soft)
		/// 5. Don't call to SmartThreadPool.IsWorkItemCanceled
		/// 6. Wait for the STP to get idle
		/// 7. Work item's GetResult should throw WorkItemCancelException
		/// </summary>        
		[Test]
		public void CancelInProgressWorkItemSoftWithIgnoreSample ()
			{
			Assert.Throws<WorkItemCancelException> (() =>
				{
					ManualResetEvent waitToStart = new ManualResetEvent (false);
					ManualResetEvent waitToComplete = new ManualResetEvent (false);

					SmartThreadPool stp = new SmartThreadPool ();
					IWorkItemResult wir = stp.QueueWorkItem (state =>
						{
							waitToStart.Set ();
							Thread.Sleep (100);
							waitToComplete.WaitOne ();
							return null;
						});

					waitToStart.WaitOne ();

					wir.Cancel (false);

					waitToComplete.Set ();

					stp.WaitForIdle ();

					// Throws WorkItemCancelException
					wir.GetResult ();

					stp.Shutdown ();
				});
			}

		/// <summary>
		/// 1. Create STP
		/// 2. Queue work item that takes some time
		/// 3. Wait for it to start
		/// 4. Cancel the work item (soft)
		/// 5. Call to AbortOnWorkItemCancel
		/// 5. Wait for the STP to get idle
		/// 6. Make sure nothing ran in the work item after the AbortOnWorkItemCancel
		/// </summary>        
		[Test]
		public void CancelInProgressWorkItemSoftWithAbortOnWorkItemCancel ()
			{
			bool abortFailed = false;
			ManualResetEvent waitToStart = new ManualResetEvent (false);
			ManualResetEvent waitToCancel = new ManualResetEvent (false);

			SmartThreadPool stp = new SmartThreadPool ();
			IWorkItemResult wir = stp.QueueWorkItem (
				state =>
					{
					waitToStart.Set ();
					waitToCancel.WaitOne ();
					SmartThreadPool.AbortOnWorkItemCancel ();
					abortFailed = true;
					return null;
					});

			waitToStart.WaitOne ();

			wir.Cancel (false);

			waitToCancel.Set ();

			stp.WaitForIdle ();

			Assert.IsTrue (wir.IsCanceled);
			Assert.IsFalse (abortFailed);

			stp.Shutdown ();
			}

		/// <summary>
		/// 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
		/// </summary>        
		[Test]
		public void CancelCanceledWorkItem ()
			{
			Assert.Throws<WorkItemCancelException> (() =>
				{
					STPStartInfo stpStartInfo = new STPStartInfo ();
					stpStartInfo.StartSuspended = true;

					SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
					IWorkItemResult wir = stp.QueueWorkItem (state => 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 ();
						}
				});
			}

		/// <summary>
		/// 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
		/// </summary>        
		[Test]
		public void CancelCompletedWorkItem ()
			{
			SmartThreadPool stp = new SmartThreadPool ();
			IWorkItemResult wir = stp.QueueWorkItem (
				state => 1
				);

			stp.WaitForIdle ();

			Assert.AreEqual (wir.GetResult (), 1);

			wir.Cancel ();

			Assert.AreEqual (wir.GetResult (), 1);

			stp.Shutdown ();
			}

		/// <summary>
		/// 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
		/// </summary>        
		[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 (
					state => { Thread.Sleep (500); ++counter; return null; }
					);
				}

			Thread.Sleep (100);

			stp.Cancel (true);

			Assert.AreEqual (counter, 0);

			stp.Shutdown ();
			}

		/// <summary>
		/// 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
		/// </summary>        
		[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 (
					state => { Thread.Sleep (500); ++counter; return null; }
					);
				}

			Thread.Sleep (100);

			wig.Cancel (true);

			Assert.AreEqual (counter, 0);

			stp.Shutdown ();
			}

		/// <summary>
		/// 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
		/// </summary>                
		[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 (
					state => { Interlocked.Increment (ref counter1); Thread.Sleep (500); Interlocked.Increment (ref counter1); return null; }
					);
				}

			for (int i = 0; i < 3; i++)
				{
				wig2.QueueWorkItem (
					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 ();
			}
		}
	}
using System;
using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
	{
	/// <summary>
	/// Summary description for TestChainedDelegates.
	/// </summary>
	[TestFixture]
	[Category ("TestChainedDelegates")]
	public class TestChainedDelegates
		{
		[Test]
		public void GoodCallback ()
			{
			SmartThreadPool stp = new SmartThreadPool ();

			stp.QueueWorkItem (new WorkItemCallback (DoWork));

			stp.WaitForIdle ();

			stp.Shutdown ();
			}

		[Test]
		public void ChainedDelegatesCallback ()
			{
			Assert.Throws<NotSupportedException> (() =>
				{
					SmartThreadPool stp = new SmartThreadPool ();

					WorkItemCallback workItemCallback = new WorkItemCallback (DoWork);
					workItemCallback += new WorkItemCallback (DoWork);

					stp.QueueWorkItem (workItemCallback);

					stp.WaitForIdle ();

					stp.Shutdown ();
				});
			}

		[Test]
		public void GoodPostExecute ()
			{
			SmartThreadPool stp = new SmartThreadPool ();

			stp.QueueWorkItem (
				new WorkItemCallback (DoWork),
				null,
				new PostExecuteWorkItemCallback (DoPostExecute));

			stp.WaitForIdle ();

			stp.Shutdown ();
			}

		[Test]
		public void ChainedDelegatesPostExecute ()
			{
			Assert.Throws<NotSupportedException> (() =>
				{
					SmartThreadPool stp = new SmartThreadPool ();

					PostExecuteWorkItemCallback postExecuteWorkItemCallback = DoPostExecute;
					postExecuteWorkItemCallback += DoPostExecute;

					stp.QueueWorkItem (new WorkItemCallback (DoWork), null, postExecuteWorkItemCallback);

					stp.WaitForIdle ();

					stp.Shutdown ();
				});
			}


		private object DoWork (object state)
			{
			return null;
			}

		private void DoPostExecute (IWorkItemResult wir)
			{
			}


		}
	}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSMono.Diagnostics;
using Interlocked = Crestron.SimplSharp.Interlocked;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
	/// Summary description for TestConcurrencyChanges.
	/// </summary>
	[TestFixture]
	[Category("TestConcurrencyChanges")]
	public class TestConcurrencyChanges
	{
	    /// <summary>
        /// Example of waiting for idle
        /// </summary>
        [Test]
        public void TestMaxThreadsChange()
        {
            SmartThreadPool smartThreadPool = new SmartThreadPool(1 * 1000, 1, 0);

            for (int i = 0; i < 100; ++i)
            {
                smartThreadPool.QueueWorkItem(
                    new WorkItemCallback(this.DoSomeWork),
                    null);
            }

            bool 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 = 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();
        }

        /// <summary>
        /// Example of waiting for idle
        /// </summary>
        [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;
        }
	}
}
using System;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
	/// Summary description for TestExceptions.
	/// </summary>
	[TestFixture]
	[Category("TestExceptions")]
	public class TestExceptions
	{
		private class DivArgs
		{
			public int x;
			public int y;
		}

		[Test]
		public void ExceptionThrowing() 
		{ 
			SmartThreadPool _smartThreadPool = new SmartThreadPool();

			DivArgs divArgs = new DivArgs();
			divArgs.x = 10;
			divArgs.y = 0;

			IWorkItemResult wir = 
				_smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);

			try
			{
				wir.GetResult();
			}
			catch(WorkItemResultException wire)
			{
				Assert.IsTrue(wire.InnerException is DivideByZeroException);
				return;
			}
			catch(Exception e)
			{
                e.GetHashCode();
				Assert.Fail();
			}
			Assert.Fail();
		} 

		[Test]
		public void ExceptionReturning() 
		{ 
			bool success = true;

			SmartThreadPool _smartThreadPool = new SmartThreadPool();

			DivArgs divArgs = new DivArgs();
			divArgs.x = 10;
			divArgs.y = 0;

			IWorkItemResult wir = 
				_smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);

			Exception e = null;
			try
			{
				wir.GetResult(out e);
			}
			catch (Exception ex)
			{
                ex.GetHashCode();
				success = false;
			}

			Assert.IsTrue(success);
			Assert.IsTrue(e is DivideByZeroException);
		} 

		private object DoDiv(object state)
		{ 
			DivArgs divArgs = (DivArgs)state;
			return (divArgs.x / divArgs.y);
		}

	}
}
using System;
using Crestron.SimplSharp;
using Amib.SSharpThreading;
using NUnit.Framework;

namespace STPTests
{
    /// <summary>
    /// Summary description for TestFalseFillStateWithArgs.
    /// </summary>
    [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<int> wir = _stp.QueueWorkItem(new Func<int>(Func0));
            Assert.IsNull(wir.State);
        }

        [Test]
        public void STPFuncT1()
        {
            IWorkItemResult<bool> wir = _stp.QueueWorkItem(new Func<int, bool>(Func1), 17);
            Assert.IsNull(wir.State);
        }

        [Test]
        public void STPFuncT2()
        {
            IWorkItemResult<string> wir = _stp.QueueWorkItem(new Func<char, string, string>(Func2), 'a', "bla bla");
            Assert.IsNull(wir.State);
        }

        [Test]
        public void STPFuncT3()
        {
            char[] chars = new char[] { 'a', 'b' };
            object obj = new object();

            IWorkItemResult<char> wir = _stp.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _stp.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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<int> wir = _wig.QueueWorkItem(new Func<int>(Func0));
            Assert.IsNull(wir.State);
        }

        [Test]
        public void WIGFuncT1()
        {
            IWorkItemResult<bool> wir = _wig.QueueWorkItem(new Func<int, bool>(Func1), 17);
            Assert.IsNull(wir.State);
        }

        [Test]
        public void WIGFuncT2()
        {
            IWorkItemResult<string> wir = _wig.QueueWorkItem(new Func<char, string, string>(Func2), 'a', "bla bla");
            Assert.IsNull(wir.State);
        }

        [Test]
        public void WIGFuncT3()
        {
            char[] chars = new char[] { 'a', 'b' };
            object obj = new object();

            IWorkItemResult<char> wir = _wig.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _wig.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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;
        }
    }
}
using System;
using Crestron.SimplSharp;
using Amib.SSharpThreading;
using NUnit.Framework;

namespace STPTests
{
    /// <summary>
    /// Summary description for TestFillStateWithArgs.
    /// </summary>
    [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<int> wir = _stp.QueueWorkItem(new Func<int>(Func0));
            Assert.AreEqual(wir.State, null);
        }

        [Test]
        public void FuncT1()
        {
            IWorkItemResult<bool> wir = _stp.QueueWorkItem(new Func<int, bool>(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<string> wir = _stp.QueueWorkItem(new Func<char, string, string>(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<char> wir = _stp.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _stp.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;

using Amib.SSharpThreading;
using NUnit.Framework;

namespace STPTests
{
    /// <summary>
    /// Summary description for TestCancel.
    /// </summary>
    [TestFixture]
    [Category("TestFuncT")]
    public class TestFuncT
    {
        [Test]
        public void FuncT()
        {
            SmartThreadPool stp = new SmartThreadPool();
            IWorkItemResult<int> wir = 
                stp.QueueWorkItem(new Func<int, int>(f), 1);

            int y = wir.GetResult();

            Assert.AreEqual(y, 2);

            try
            {
                wir.GetResult();
            }
            finally
            {
                stp.Shutdown();
            }
        }

        private int f(int x)
        {
            return x + 1;
        }
    }
}
using System;
using Amib.SSharpThreading;
using NUnit.Framework;

namespace SmartThreadPoolTests
{
    /// <summary>
    /// Summary description for TestCancel.
    /// </summary>
    [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<int> wir = _stp.QueueWorkItem(new Func<int>(MaxInt));

            int result = wir.GetResult();

            Assert.AreEqual(result, int.MaxValue);
        }

        [Test]
        public void FuncT1()
        {
            IWorkItemResult<bool> wir = _stp.QueueWorkItem(new Func<bool, bool>(Not), true);

            bool result = wir.Result;

            Assert.AreEqual(result, false);
        }

        [Test]
        public void FuncT2()
        {
            IWorkItemResult<string> wir = _stp.QueueWorkItem(new Func<string, string, string>(string.Concat), "ABC", "xyz");

            string result = wir.Result;

            Assert.AreEqual(result, "ABCxyz");
        }

        [Test]
        public void FuncT3()
        {
            IWorkItemResult<string> wir = _stp.QueueWorkItem(new Func<string, int, int, string>(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<int[]> wir = _stp.QueueWorkItem(new Func<int[], int, int, int, int[]>(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;
        }
    }
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
	{
	/// <summary>
	/// Summary description for GetResultExample.
	/// </summary>
	[TestFixture]
	[Category ("TestGetResult")]
	public class TestGetResult
		{
		/// <summary>
		/// Example of how to queue a work item and then wait infinitely for the result.
		/// </summary>
		[Test]
		public void BlockingCall ()
			{
			SmartThreadPool smartThreadPool = new SmartThreadPool ();

			bool success = false;

			IWorkItemResult wir =
				smartThreadPool.QueueWorkItem (new WorkItemCallback (this.DoSomeWork), null);

			if (!wir.IsCompleted)
				{
				int result = (int)wir.GetResult ();
				success = (1 == result);
				}

			smartThreadPool.Shutdown ();

			Assert.IsTrue (success);
			}

		/// <summary>
		/// Example of how to queue a work item and then wait on a timeout for the result.
		/// </summary>
		[Test]
		public void Timeout ()
			{
			Assert.Throws<WorkItemTimeoutException> (() =>
				{
					SmartThreadPool smartThreadPool = new SmartThreadPool ();

					IWorkItemResult wir = smartThreadPool.QueueWorkItem (new WorkItemCallback (this.DoSomeWork), null);

					try
						{
						wir.GetResult (500, true);
						}
					finally
						{
						smartThreadPool.Shutdown ();
						}
				});
			}

		/// <summary>
		/// Example of how to interrupt the waiting for a work item to complete.
		/// </summary>
		[Test]
		public void WorkItemWaitCanceling ()
			{
			Assert.Throws<WorkItemTimeoutException> (() =>
				{
			SmartThreadPool smartThreadPool = new SmartThreadPool ();

			ManualResetEvent cancelWaitHandle = new ManualResetEvent (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 (Crestron.SimplSharp.Timeout.Infinite, true, cancelWaitHandle);
					}
				finally
					{
					smartThreadPool.Shutdown ();
					}
				});
			}

		private object DoSomeWork (object state)
			{
			Thread.Sleep (1000);
			return 1;
			}

		private object SignalCancel (object state)
			{
			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
			Thread.Sleep (250);
			cancelWaitHandle.Set ();
			return null;
			}

		}
	}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
	/// Summary description for MultipleWorkItemsExample.
	/// </summary>
	[TestFixture]
	[Category("TestMultipleWorkItems")]
	public class TestMultipleWorkItems
	{
		/// <summary>
		/// Example of how to queue several work items and then wait infinitely for 
		/// all of them to complete.
		/// </summary>
		[Test]
		public void WaitAll() 
		{ 
			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 WorkItemCallback(this.DoSomeWork), null);
			}

			SmartThreadPool.WaitAll(wirs);

			for(int i = 0; i < wirs.Length; ++i)
			{
				if (!wirs[i].IsCompleted)
				{
					success = false;
					break;
				}
				else
				{
					int result = (int)wirs[i].GetResult();
					if (1 != result)
					{
						success = false;
						break;
					}
				}
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait infinitely for 
		/// one of them to complete.
		/// 
		/// You can use this technique if you have several work items that return the same 
		/// infomration, but use different method to aquire it. Just execute all of them at
		/// once and wait for the first work item to complete.
		/// 
		/// For example: You need an information about a person and you can query several 
		/// information sites (FBI, CIA, etc.). Query all of them at once and use the first
		/// answer to arrive.
		/// </summary>
		[Test]
		public void WaitAny() 
		{ 
			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 WorkItemCallback(this.DoSomeWork), null);
			}

			int index = SmartThreadPool.WaitAny(wirs);

			if (wirs[index].IsCompleted)
			{
				int result = (int)wirs[index].GetResult();
				if (1 == result)
				{
					success = true;
				}
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for all
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAllWithTimeoutSuccess()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();

		    IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			bool timeout = !SmartThreadPool.WaitAll(wirs, 1500, true);
			bool success = !timeout;

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for all
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAllWithTimeoutFailure()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();

		    IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			bool timeout = !SmartThreadPool.WaitAll(wirs, 10, true);
			bool success = timeout;

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for any
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAnyWithTimeoutSuccess()
		{ 
			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 WorkItemCallback(this.DoSomeWork), null);
			}

			int index = SmartThreadPool.WaitAny(wirs, 1500, true);

			success = (index != WaitHandle.WaitTimeout);

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		}

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for any
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAnyWithTimeoutFailure()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();

			bool success;

			IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					smartThreadPool.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			int index = SmartThreadPool.WaitAny(wirs, 10, true);

			success = (index == WaitHandle.WaitTimeout);

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		}

		[Test]
		public void WaitAllWithEmptyArray()
		{ 

			IWorkItemResult [] wirs = new IWorkItemResult[0];

			bool success = SmartThreadPool.WaitAll(wirs);;

			Assert.IsTrue(success);
		}

		private object DoSomeWork(object state)
		{ 
			Thread.Sleep(1000);
			return 1;
		}

        [Test]
        public void WaitAllT()
        {
            SmartThreadPool smartThreadPool = new SmartThreadPool();

            bool success = true;

            IWorkItemResult<int>[] wirs = new IWorkItemResult<int>[5];

            for (int i = 0; i < wirs.Length; ++i)
            {
                wirs[i] = smartThreadPool.QueueWorkItem(new Func<int, int, int>(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<int>[] wirs = new IWorkItemResult<int>[5];

            for (int i = 0; i < wirs.Length; ++i)
            {
                wirs[i] = smartThreadPool.QueueWorkItem(new Func<int, int, int>(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);
        } 

	}
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using Interlocked = Crestron.SimplSharp.Interlocked;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
    /// Summary description for TestParallelMethods.
	/// </summary>
	[TestFixture]
	[Category("TestParallelMethods")]
    public class TestParallelMethods
	{
		[Test]
		public void TestJoin() 
		{ 
			SmartThreadPool stp = new SmartThreadPool();

		    SafeCounter sc = new SafeCounter();

            stp.Join(
                sc.Increment,
                sc.Increment,
                sc.Increment);

            Assert.AreEqual(3, sc.Counter);

		    for (int j = 0; j < 10; j++)
		    {
                sc.Reset();

                Action[] actions = new Action[1000];
                for (int i = 0; i < actions.Length; i++)
                {
                    actions[i] = sc.Increment;
                }

                stp.Join(actions);

                Assert.AreEqual(actions.Length, sc.Counter);
		    }

            stp.Shutdown();
		}

	    private class SafeCounter
        {
            private int _counter;

            public void Increment()
            {
                Interlocked.Increment(ref _counter);
            }

            public int Counter
            {
                get { return _counter; }
            }

            public void Reset()
            {
                _counter = 0;
            }
        }

	    [Test]
	    public void TestChoice() 
	    { 
	        SmartThreadPool stp = new SmartThreadPool();

	        int index = stp.Choice(
	            () => Thread.Sleep(1000),
	            () => Thread.Sleep(1500),
	            () => Thread.Sleep(500));

	        Assert.AreEqual(2, index);  
            
	        index = stp.Choice(
	            () => Thread.Sleep(300),
	            () => Thread.Sleep(100),
	            () => Thread.Sleep(200));

	        Assert.AreEqual(1, index);

	        stp.Shutdown();
	    } 
        
        [Test]
	    public void TestPipe() 
	    { 
            SafeCounter sc = new SafeCounter();
	        SmartThreadPool stp = new SmartThreadPool();

            stp.Pipe(
                sc,
                sc1 => { if (sc.Counter == 0) { sc1.Increment(); }}, 
                sc1 => { if (sc.Counter == 1) { sc1.Increment(); }}, 
                sc1 => { if (sc.Counter == 2) { sc1.Increment(); }} 
                );

            Assert.AreEqual(3, sc.Counter);

	        stp.Shutdown();
	    }
	}
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
	/// Summary description for DoTestPostExecute.
	/// </summary>
	[TestFixture]
	[Category("DoTestPostExecute")]
	public class TestPostExecution
	{
		/// <summary>
		/// 
		/// </summary>
		[Test]
		public void DefaultPostExecute_AlwaysCall()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Always, true));
		}

		[Test]
		public void DefaultPostExecute_NeverCall()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Never, false));
		}

		[Test]
		public void DefaultPostExecute_CallWhenCanceled()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
		}

		[Test]
		public void DefaultPostExecute_CallWhenNotCanceled()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
		}

		/// <summary>
		/// 
		/// </summary>
		[Test]
		public void PostExecute_AlwaysCall()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Always, true));
		}

		[Test]
		public void PostExecute_NeverCall()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Never, false));
		}

		[Test]
		public void PostExecute_CallWhenCanceled()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
		}

		[Test]
		public void PostExecute_CallWhenNotCanceled()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
		}

		/// <summary>
		/// 
		/// </summary>
		[Test]
		public void PostExecuteWithCancel_AlwaysCall()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Always, true));
		}

		[Test]
		public void PostExecuteWithCancel_NeverCall()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Never, false));
		}

		[Test]
		public void PostExecuteWithCancel_CallWhenCanceled()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemCanceled, true));
		}

		[Test]
		public void PostExecuteWithCancel_CallWhenNotCanceled()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemNotCanceled, false));
		}


		private class PostExecuteResult
		{
			public ManualResetEvent wh = new ManualResetEvent(false);
		}

		/// <summary>
		/// Example of how to use the post execute callback
		/// </summary>
		private bool DoTestDefaultPostExecute(CallToPostExecute callToPostExecute, bool answer)
		{ 
			STPStartInfo stpStartInfo = new STPStartInfo();
			stpStartInfo.CallToPostExecute = callToPostExecute;
			stpStartInfo.PostExecuteWorkItemCallback = new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork);

			SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo);

			bool success = false;

			PostExecuteResult postExecuteResult = new PostExecuteResult();

			IWorkItemResult wir = 
				smartThreadPool.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				postExecuteResult);

			if (!wir.IsCompleted)
			{
				int result = (int)wir.GetResult();
				success = (1 == result);
				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
			}

			smartThreadPool.Shutdown();

			return success;
		}


		/// <summary>
		/// Example of how to use the post execute callback
		/// </summary>
		private bool DoTestPostExecute(CallToPostExecute callToPostExecute, bool answer)
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();

			bool success = false;

			PostExecuteResult postExecuteResult = new PostExecuteResult();

			IWorkItemResult wir = 
				smartThreadPool.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				postExecuteResult,
				new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
				callToPostExecute);

			if (!wir.IsCompleted)
			{
				int result = (int)wir.GetResult();
				success = (1 == result);
				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
			}

			smartThreadPool.Shutdown();

			return success;
		}

		/// <summary>
		/// Example of how to queue a work item and then cancel it while it is in the queue.
		/// </summary>
		private bool DoTestPostExecuteWithCancel(CallToPostExecute callToPostExecute, bool answer) 
		{ 
			// 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;
			PostExecuteResult postExecuteResult = new PostExecuteResult();

			// Queue a work item that will occupy the thread in the pool
			smartThreadPool.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				null);

			// Queue another work item that will wait for the first to complete
			IWorkItemResult wir = 
				smartThreadPool.QueueWorkItem(
					new WorkItemCallback(this.DoSomeWork), 
					postExecuteResult,
					new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
					callToPostExecute);


			// Wait a while for the thread pool to start executing the first work item
			Thread.Sleep(100);

			// Cancel the second work item while it still in the queue
			if (wir.Cancel())
			{
				success = (postExecuteResult.wh.WaitOne(1000, true) == answer);
			}

			smartThreadPool.Shutdown();

			return success;
		} 

		private object DoSomeWork(object state)
		{ 
			Thread.Sleep(1000);
			return 1;
		}

		private void DoSomePostExecuteWork(IWorkItemResult wir)
		{ 
			PostExecuteResult postExecuteResult = wir.State as PostExecuteResult;
			postExecuteResult.wh.Set();
		}

		private object SignalCancel(object state)
		{ 
			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
			Thread.Sleep(250);
			cancelWaitHandle.Set();
			return null;
		}
	}
}
using System;
using System.Linq;
using OpenNETCF;
using NUnit.Framework;

using Amib.SSharpThreading;
using Amib.SSharpThreading.Internal;
using SmartThreadPoolTests;

namespace PriorityQueueTests
	{
	/// <summary>
	/// Summary description for TestPriorityQueue.
	/// </summary>
	[TestFixture]
	[Category ("TestPriorityQueue")]
	public class TestPriorityQueue
		{
		[Test]
		public void Init ()
			{
			PriorityQueue pq = new PriorityQueue ();

			Assert.AreEqual (0, pq.Count);

			Assert.IsNull (pq.Dequeue ());

			Assert.AreEqual (0, pq.Count);
			}

		[Test]
		public void OneWorkItem ()
			{
			WorkItemPriority[] priorities = Enum2.GetValues (typeof (WorkItemPriority)).Cast<WorkItemPriority> ().ToArray ();
			foreach (WorkItemPriority wip in priorities)
				{
				PriorityQueue pq = new PriorityQueue ();

				PriorityItem pi = new PriorityItem (wip);

				pq.Enqueue (pi);

				Assert.AreEqual (1, pq.Count, "Failed for priority {0}", wip);

				PriorityItem pi2 = pq.Dequeue () as PriorityItem;

				Assert.IsNotNull (pi2, "Failed for priority {0}", wip);

				Assert.AreSame (pi, pi2, "Failed for priority {0}", wip);

				Assert.AreEqual (0, pq.Count, "Failed for priority {0}", wip);
				}
			}

		[Test]
		public void MultipleWorkItemsOnePriority ()
			{
			WorkItemPriority[] priorities = Enum2.GetValues (typeof (WorkItemPriority)).Cast<WorkItemPriority> ().ToArray ();
			foreach (WorkItemPriority wip in priorities)
				{
				PriorityQueue pq = new PriorityQueue ();

				PriorityItem[] priorityItems = new PriorityItem[10];

				for (int i = 0; i < priorityItems.Length; ++i)
					{
					priorityItems[i] = new PriorityItem (wip);

					pq.Enqueue (priorityItems[i]);

					Assert.AreEqual (i + 1, pq.Count, "Failed for priority {0} item count {1}", wip, i + 1);
					}

				for (int i = 0; i < priorityItems.Length; ++i)
					{
					PriorityItem pi = pq.Dequeue () as PriorityItem;

					Assert.AreEqual (priorityItems.Length - (i + 1), pq.Count, "Failed for priority {0} item count {1}", wip, i + 1);

					Assert.IsNotNull (pi, "Failed for priority {0} item count {1}", wip, i + 1);

					Assert.AreSame (pi, priorityItems[i], "Failed for priority {0} item count {1}", wip, i + 1);
					}

				Assert.AreEqual (0, pq.Count, "Failed for priority {0}", wip);

				Assert.IsNull (pq.Dequeue ());

				Assert.AreEqual (0, pq.Count);
				}
			}

		[Test]
		public void MultipleWorkItemsMultiplePriorities ()
			{
			// Get all the available priorities
			WorkItemPriority[] priorities = Enum2.GetValues (typeof (WorkItemPriority)).Cast<WorkItemPriority> ().ToArray ();

			// Create an array of priority items
			PriorityItem[] priorityItems = new PriorityItem[priorities.Length];

			// Create a priority item for each priority 
			int i = priorities.Length;
			foreach (WorkItemPriority workItemPriority in priorities)
				{
				--i;
				priorityItems[i] = new PriorityItem (workItemPriority);
				}

			// Create a PermutationGenerator for the priority items
			PermutationGenerator permutations = new PermutationGenerator (priorityItems);

			int count = 0;
			// Iterate over the permutations
			foreach (object[] permutation in permutations)
				{
				++count;
				//Console.Write ("Permutation #" + count + " : ");
				for (int j = 0; j < permutation.Length; ++j)
					{
					PriorityItem pi = permutation[j] as PriorityItem;
					//Console.Write (pi.WorkItemPriority + ", ");
					}
				//Console.WriteLine ();
				// Create a priority queue
				PriorityQueue pq = new PriorityQueue ();

				// Enqueue each priority item according to the permutation
				for (i = 0; i < permutation.Length; ++i)
					{
					PriorityItem priorityItem = permutation[i] as PriorityItem;
					pq.Enqueue (priorityItem);
					}

				// Make sure all the priority items are in the queue
				Assert.AreEqual (priorityItems.Length, pq.Count);

				// Compare the order of the priority items
				for (i = 0; i < priorityItems.Length; ++i)
					{
					PriorityItem priorityItem = pq.Dequeue () as PriorityItem;
					Assert.AreSame (priorityItems[i], priorityItem);
					}
				}
			}

		private class PriorityItem : IHasWorkItemPriority
			{
			public PriorityItem (WorkItemPriority workItemPriority)
				{
				WorkItemPriority = workItemPriority;
				}

			public WorkItemPriority WorkItemPriority { get; private set; }
			}

		}
	}

using NUnit.Framework;
using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
    /// Tests for QueueWorkItem.
	/// </summary>
	[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);
        }
	}
}
using Crestron.SimplSharpPro.CrestronThread;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
	/// Summary description for TestStartSuspended.
	/// </summary>
	[TestFixture]
	[Category("TestStartSuspended")]
	public class TestStartSuspended
	{
	    [Test]
		public void StartSuspended()
		{
			STPStartInfo stpStartInfo = new STPStartInfo();
			stpStartInfo.StartSuspended = true;

			SmartThreadPool stp = new SmartThreadPool(stpStartInfo);

			stp.QueueWorkItem(new WorkItemCallback(this.DoWork));

			Assert.IsFalse(stp.WaitForIdle(200));

			stp.Start();

			Assert.IsTrue(stp.WaitForIdle(200));
		}

		[Test]
		public void WIGStartSuspended()
		{
			SmartThreadPool stp = new SmartThreadPool();

			WIGStartInfo wigStartInfo = new WIGStartInfo();
			wigStartInfo.StartSuspended = true;

			IWorkItemsGroup wig = stp.CreateWorkItemsGroup(10, wigStartInfo);

			wig.QueueWorkItem(new WorkItemCallback(this.DoWork));

			Assert.IsFalse(wig.WaitForIdle(200));

			wig.Start();

			Assert.IsTrue(wig.WaitForIdle(200));
		}

		[Test]
		public void STPAndWIGStartSuspended()
		{
			STPStartInfo stpStartInfo = new STPStartInfo();
			stpStartInfo.StartSuspended = true;

			SmartThreadPool stp = new SmartThreadPool(stpStartInfo);

			WIGStartInfo wigStartInfo = new WIGStartInfo();
			wigStartInfo.StartSuspended = true;

			IWorkItemsGroup wig = stp.CreateWorkItemsGroup(10, wigStartInfo);

			wig.QueueWorkItem(new WorkItemCallback(this.DoWork));

			Assert.IsFalse(wig.WaitForIdle(200));

			wig.Start();

			Assert.IsFalse(wig.WaitForIdle(200));

			stp.Start();

			Assert.IsTrue(wig.WaitForIdle(5000), "WIG is not idle");
            Assert.IsTrue(stp.WaitForIdle(5000), "STP is not idle");
		}

		[Test]
		public void TwoWIGsStartSuspended()
		{
			SmartThreadPool stp = new SmartThreadPool();

			WIGStartInfo wigStartInfo = new WIGStartInfo();
			wigStartInfo.StartSuspended = true;

			IWorkItemsGroup wig1 = stp.CreateWorkItemsGroup(10, wigStartInfo);
			IWorkItemsGroup wig2 = stp.CreateWorkItemsGroup(10, wigStartInfo);

			wig1.QueueWorkItem(new WorkItemCallback(this.DoWork));
			wig2.QueueWorkItem(new WorkItemCallback(this.DoWork));

			Assert.IsFalse(wig1.WaitForIdle(200));
			Assert.IsFalse(wig2.WaitForIdle(200));

			wig1.Start();

			Assert.IsTrue(wig1.WaitForIdle(200));
			Assert.IsFalse(wig2.WaitForIdle(200));

			wig2.Start();

			Assert.IsTrue(wig1.WaitForIdle(0));
			Assert.IsTrue(wig2.WaitForIdle(200));
		}


		private object DoWork(object state)
		{
			Thread.Sleep(100);
			return null;
		}

	}
}
using System;
using Crestron.SimplSharpPro.CrestronThread;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
    public class CallerState
    {
        public int Value { get; private set; }

        protected void IncValue()
        {
            ++Value;
        }
    }

    public class NonDisposableCallerState : CallerState
    {
        public NonDisposableCallerState()
        {
            IncValue();
        }
    }

    public class DisposableCallerState : CallerState, IDisposable
    {
        public DisposableCallerState()
        {
            IncValue();
        }

        #region IDisposable Members

        public void Dispose()
        {
            IncValue();
        }

        #endregion
    }


	/// <summary>
	/// Summary description for StateDisposeExample.
	/// </summary>
	[TestFixture]
	[Category("TestStateDispose")]
	public class TestStateDispose
	{
	    /// <summary>
        /// Example of non disposable caller state
        /// </summary>
        [Test]
        public void DisposeCallerState() 
        { 
            STPStartInfo stpStartInfo = new STPStartInfo();
            stpStartInfo.DisposeOfStateObjects = true;
     
            SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo);

            CallerState nonDisposableCallerState = new NonDisposableCallerState();
            CallerState disposableCallerState = new DisposableCallerState();

            IWorkItemResult wir1 = 
                smartThreadPool.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                nonDisposableCallerState);

            IWorkItemResult wir2 = 
                smartThreadPool.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                disposableCallerState);

            wir1.GetResult();
			Assert.AreEqual(1, nonDisposableCallerState.Value);

            wir2.GetResult();

			// Wait a little bit for the working thread to call dispose on the 
			// work item's state.
			smartThreadPool.WaitForIdle();

			Assert.AreEqual(2, disposableCallerState.Value);

            smartThreadPool.Shutdown();
        } 

        /// <summary>
        /// Example of non disposable caller state
        /// </summary>
        [Test]
        public void DontDisposeCallerState() 
        { 
            STPStartInfo stpStartInfo = new STPStartInfo();
            stpStartInfo.DisposeOfStateObjects = false;
     
            SmartThreadPool smartThreadPool = new SmartThreadPool(stpStartInfo);

            CallerState nonDisposableCallerState = new NonDisposableCallerState();
            CallerState disposableCallerState = new DisposableCallerState();

            IWorkItemResult wir1 = 
                smartThreadPool.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                nonDisposableCallerState);

            IWorkItemResult wir2 = 
                smartThreadPool.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                disposableCallerState);

            wir1.GetResult();
            bool success = (1 == nonDisposableCallerState.Value);

            wir2.GetResult();

            success = success && (1 == disposableCallerState.Value);

            smartThreadPool.Shutdown();

            Assert.IsTrue(success);
        } 

        private object DoSomeWork(object state)
        { 
            Thread.Sleep(1000);
            return 1;
        }
    }
}
using System.Threading;
using NUnit.Framework;
using Amib.Threading;

namespace SmartThreadPoolTests
{
	/// <summary>
    /// Summary description for TestThreadApartment.
	/// </summary>
	[TestFixture]
    [Category("TestThreadApartment")]
	public class TestThreadApartmentState
	{
		[Test]
		public void TestSTA()
		{
            CheckApartmentState(ApartmentState.STA);
		}

		[Test]
		public void TestMTA()
		{
            CheckApartmentState(ApartmentState.MTA);
		}

	    private static void CheckApartmentState(ApartmentState requestApartmentState)
	    {
	        STPStartInfo stpStartInfo = new STPStartInfo();
            stpStartInfo.ApartmentState = requestApartmentState;

	        SmartThreadPool stp = new SmartThreadPool(stpStartInfo);

	        IWorkItemResult<ApartmentState> wir = stp.QueueWorkItem(() => GetCurrentThreadApartmentState());

	        ApartmentState resultApartmentState = wir.GetResult();

	        stp.WaitForIdle();

	        Assert.AreEqual(requestApartmentState, resultApartmentState);
	    }

	    private static ApartmentState GetCurrentThreadApartmentState()
	    {
	        return Thread.CurrentThread.GetApartmentState();
	    }
	}
}
using Crestron.SimplSharpPro.CrestronThread;
using NUnit.Framework;
using Amib.Threading;

namespace SmartThreadPoolTests
{
	/// <summary>
    /// Summary description for TestThreadIsBackground.
	/// </summary>
	[TestFixture]
    [Category("TestThreadIsBackground")]
	public class TestThreadIsBackground
	{
		[Test]
        public void TestIsBackground()
		{
            CheckIsBackground(true);
		}

		[Test]
        public void TestNotIsBackground()
		{
            CheckIsBackground(false);
		}

        private static void CheckIsBackground(bool isBackground)
	    {
	        STPStartInfo stpStartInfo = new STPStartInfo();
	        stpStartInfo.AreThreadsBackground = isBackground;

	        SmartThreadPool stp = new SmartThreadPool(stpStartInfo);

            IWorkItemResult<bool> wir = stp.QueueWorkItem(() => GetCurrentThreadIsBackground());

	        bool resultIsBackground = wir.GetResult();

	        stp.WaitForIdle();

            Assert.AreEqual(isBackground, resultIsBackground);
	    }

	    private static bool GetCurrentThreadIsBackground()
	    {
	        return Thread.CurrentThread.IsBackground;
	    }
	}
}

using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;
using ThreadPriority = Crestron.SimplSharpPro.CrestronThread.Thread.eThreadPriority;
using WaitHandle = Crestron.SimplSharp.CEventHandle;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
	{
	/// <summary>
	/// Summary description for TestThreadPriority.
	/// </summary>
	[TestFixture]
	[Category ("TestThreadPriority")]
	public class TestThreadPriority
		{
		[Test]
		public void TestDefaultPriority ()
			{
			CheckSinglePriority (SmartThreadPool.DefaultThreadPriority);
			}

		[Test]
		public void TestLowestPriority ()
			{
			CheckSinglePriority (ThreadPriority.LowestPriority);
			}

#if !SSHARP
        [Test]
        public void TestBelowNormalPriority()
		{
            CheckSinglePriority(ThreadPriority.BelowNormal);
		}
#endif
		[Test]
		public void TestNormalPriority ()
			{
			CheckSinglePriority (ThreadPriority.MediumPriority);
			}

#if !SSHARP
		[Test]
		public void TestAboveNormalPriority ()
			{
			CheckSinglePriority (ThreadPriority.AboveNormal);
			}
#endif

		[Test]
		public void TestHighestPriority ()
			{
			CheckSinglePriority (ThreadPriority.HighPriority);
			}

		private void CheckSinglePriority (ThreadPriority threadPriority)
			{
			STPStartInfo stpStartInfo = new STPStartInfo ();
			stpStartInfo.ThreadPriority = threadPriority;

			SmartThreadPool stp = new SmartThreadPool (stpStartInfo);

			IWorkItemResult wir = stp.QueueWorkItem (new WorkItemCallback (GetThreadPriority));
			ThreadPriority currentThreadPriority = (ThreadPriority)wir.GetResult ();

			Assert.AreEqual (threadPriority, currentThreadPriority);
			}

		private object GetThreadPriority (object state)
			{
			return Thread.CurrentThread.Priority;
			}
		}
	}
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.CrestronThread;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
	{
	/// <summary>
	/// </summary>
	[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 += OnInitialization;
			stp.OnThreadTermination += OnTermination;

			stp.QueueWorkItem (new WorkItemCallback (DoSomeWork), null);

			stp.WaitForIdle ();
			stp.Shutdown ();

			Assert.IsTrue (_initSuccess, "initSuccess");
			Assert.IsTrue (_workItemSuccess, "workItemSuccess");
			Assert.IsTrue (_termSuccess, "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);
			}


		// Can't run this test, StackOverflowException crashes the application and can't be catched and ignored
		//[Test]
		public void NotTestThreadsMaxStackSize ()
			{
			STPStartInfo stpStartInfo = new STPStartInfo ()
			{
#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) && !(_WINDOWS_CE)
				MaxStackSize = 64 * 1024,
#endif
			};

			SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
			stp.Start ();

			IWorkItemResult<bool> wir = stp.QueueWorkItem (() => AllocateBufferOnStack (10 * 1024));

			bool result = wir.GetResult ();
			Assert.IsTrue (result);

			wir = stp.QueueWorkItem (() => AllocateBufferOnStack (1000 * 1024));

			result = wir.GetResult ();
			Assert.IsFalse (result);
			}

		private static unsafe bool AllocateBufferOnStack (int size)
			{
			try
				{
				byte* p = stackalloc byte[size];
				}
			catch (StackOverflowException ex)
				{
				return false;
				}
			catch (Exception ex)
				{
				return false;
				}

			return true;
			}
		}

	internal class ThreadContextState
		{
		private static LocalDataStoreSlot _localData;

		static ThreadContextState ()
			{
			_localData = Thread.AllocateDataSlot ();
			}

		public int Counter { get; set; }

		// Static member so it can be used anywhere in code of the work item method
		public static ThreadContextState Current
			{
			get
				{
				ThreadContextState _threadContextState = (ThreadContextState)Thread.GetData (_localData);
				// If the _threadContextState is null then it was not yet initialized
				// for this thread.
				if (null == _threadContextState)
					{
					// Create a ThreadContextState object
					_threadContextState = new ThreadContextState ();
					Thread.SetData (_localData, _threadContextState);
					}
				return _threadContextState;
				}
			}
		}
	}
using Amib.SSharpThreading;
using NUnit.Framework;

namespace WorkItemsGroupTests
{
    /// <summary>
    /// Summary description for TestCancel.
    /// </summary>
    [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;
        }
    }
}
using System;
using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
	{
	/// <summary>
	/// Summary description for TestChainedDelegates.
	/// </summary>
	[TestFixture]
	[Category ("Test WorkItemsGroup ChainedDelegates")]
	public class TestChainedDelegates
		{
		[Test]
		public void GoodCallback ()
			{
			SmartThreadPool smartThreadPool = new SmartThreadPool ();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);

			workItemsGroup.QueueWorkItem (new WorkItemCallback (DoWork));

			workItemsGroup.WaitForIdle ();

			smartThreadPool.Shutdown ();
			}

		[Test]
		public void ChainedDelegatesCallback ()
			{
			Assert.Throws<NotSupportedException> (() =>
				{
					SmartThreadPool smartThreadPool = new SmartThreadPool ();
					IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);

					WorkItemCallback workItemCallback = new WorkItemCallback (DoWork);
					workItemCallback += new WorkItemCallback (DoWork);

					workItemsGroup.QueueWorkItem (workItemCallback);

					workItemsGroup.WaitForIdle ();

					smartThreadPool.Shutdown ();
				});
			}

		[Test]
		public void GoodPostExecute ()
			{
			SmartThreadPool smartThreadPool = new SmartThreadPool ();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);

			workItemsGroup.QueueWorkItem (
				new WorkItemCallback (DoWork),
				null,
				new PostExecuteWorkItemCallback (DoPostExecute));

			workItemsGroup.WaitForIdle ();

			smartThreadPool.Shutdown ();
			}

		[Test]
		public void ChainedDelegatesPostExecute ()
			{
			Assert.Throws<NotSupportedException> (() =>
				{
					SmartThreadPool smartThreadPool = new SmartThreadPool ();
					IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup (int.MaxValue);

					PostExecuteWorkItemCallback postExecuteWorkItemCallback = new PostExecuteWorkItemCallback (DoPostExecute);
					postExecuteWorkItemCallback += new PostExecuteWorkItemCallback (DoPostExecute);

					workItemsGroup.QueueWorkItem (new WorkItemCallback (DoWork), null, postExecuteWorkItemCallback);

					workItemsGroup.WaitForIdle ();

					smartThreadPool.Shutdown ();
				});
			}


		private object DoWork (object state)
			{
			return null;
			}

		private void DoPostExecute (IWorkItemResult wir)
			{
			}


		}
	}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using Interlocked = Crestron.SimplSharp.Interlocked;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
	{
	/// <summary>
	/// Summary description for TestWIGConcurrency.
	/// </summary>
	[TestFixture]
	[Category ("TestWIGConcurrency")]
	public class TestWIGConcurrency
		{
		private Random _randGen;
		private int[] _concurrentOps;
		private int _concurrencyPerWig;
		private bool _success;

		[Test]
		public void TestConcurrencies ()
			{
			Concurrency (1, 1, 10);
			Concurrency (1, 1, 100);

			Concurrency (1, 5, 10);
			Concurrency (1, 5, 100);

			Concurrency (5, 5, 10);
			Concurrency (5, 5, 100);
			}

		private void Concurrency (
			int concurrencyPerWig,
			int wigsCount,
			int workItemsCount)
			{
			/*
			Console.WriteLine (
				"Testing : concurrencyPerWig = {0}, wigsCount = {1}, workItemsCount = {2}",
				concurrencyPerWig,
				wigsCount,
				workItemsCount);
			*/
			_success = true;
			_concurrencyPerWig = concurrencyPerWig;
			_randGen = new Random (0);

			STPStartInfo stpStartInfo = new STPStartInfo ();
			stpStartInfo.StartSuspended = true;

			SmartThreadPool stp = new SmartThreadPool (stpStartInfo);

			_concurrentOps = new int[wigsCount];

			IWorkItemsGroup[] wigs = new IWorkItemsGroup[wigsCount];

			for (int i = 0; i < wigs.Length; ++i)
				{
				wigs[i] = stp.CreateWorkItemsGroup (_concurrencyPerWig);
				for (int j = 0; j < workItemsCount; ++j)
					{
					wigs[i].QueueWorkItem (new WorkItemCallback (this.DoWork), i);
					}

				wigs[i].Start ();
				}

			stp.Start ();

			stp.WaitForIdle ();

			Assert.IsTrue (_success);

			stp.Shutdown ();
			}

		private object DoWork (object state)
			{
			int wigsIndex = (int)state;

			int val = Interlocked.Increment (ref _concurrentOps[wigsIndex]);
			_success = _success && (val <= _concurrencyPerWig);

			int waitTime = _randGen.Next (50);
			Thread.Sleep (waitTime);

			val = Interlocked.Decrement (ref _concurrentOps[wigsIndex]);
			_success = _success && (val >= 0);

			return null;
			}
		}
	}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;
using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
	/// <summary>
    /// Summary description for TestWIGConcurrencyChanges.
	/// </summary>
	[TestFixture]
	[Category("TestWIGConcurrencyChanges")]
	public class TestWIGConcurrencyChanges
	{
	    /// <summary>
        /// Example of waiting for idle
        /// </summary>
        [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();
        }

        /// <summary>
        /// Example of waiting for idle
        /// </summary>
        [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;
        }




/*
        /// <summary>
        /// Example of waiting for idle
        /// </summary>
        [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;
		}
 */ 
	}
}
using System;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
	/// <summary>
	/// Summary description for TestExceptions.
	/// </summary>
	[TestFixture]
	[Category("Test WorkItemsGroup Exceptions")]
	public class TestExceptions
	{
		private class DivArgs
		{
			public int x;
			public int y;
		}

		[Test]
		public void ExceptionThrowing() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			DivArgs divArgs = new DivArgs();
			divArgs.x = 10;
			divArgs.y = 0;

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);

			try
			{
				wir.GetResult();
			}
			catch(WorkItemResultException wire)
			{
				Assert.IsTrue(wire.InnerException is DivideByZeroException);
				return;
			}
			catch(Exception e)
			{
                e.GetHashCode();
				Assert.Fail();
			}
			Assert.Fail();
		} 

		[Test]
		public void ExceptionReturning() 
		{ 
			bool success = true;

			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			DivArgs divArgs = new DivArgs();
			divArgs.x = 10;
			divArgs.y = 0;

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoDiv), divArgs);

			Exception e = null;
			try
			{
				wir.GetResult(out e);
			}
			catch (Exception ex)
			{
                ex.GetHashCode();
				success = false;
			}

			Assert.IsTrue(success);
			Assert.IsTrue(e is DivideByZeroException);
		} 

		private object DoDiv(object state)
		{ 
			DivArgs divArgs = (DivArgs)state;
			return (divArgs.x / divArgs.y);
		}

	}
}
using System;
using Crestron.SimplSharp;
using Amib.SSharpThreading;
using NUnit.Framework;

namespace STPTests
{
    /// <summary>
    /// Summary description for TestWIGFillStateWithArgs.
    /// </summary>
    [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<int> wir = _wig.QueueWorkItem(new Func<int>(Func0));
            Assert.AreEqual(wir.State, null);
        }

        [Test]
        public void FuncT1()
        {
            IWorkItemResult<bool> wir = _wig.QueueWorkItem(new Func<int, bool>(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<string> wir = _wig.QueueWorkItem(new Func<char, string, string>(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<char> wir = _wig.QueueWorkItem(new Func<bool, char[], object, char>(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<IPAddress> wir = _wig.QueueWorkItem(new Func<long, IntPtr, IPAddress, Guid, IPAddress>(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;
        }
    }
}
using System;
using Amib.Threading;
using NUnit.Framework;
using System.Net;

namespace STPTests
	{
	/// <summary>
	/// Summary description for TestFillStateWithParams.
	/// </summary>
	[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<int> wir = _stp.QueueWorkItem (new Func<int> (Func0));
			Assert.AreEqual (wir.State, null);
			}

		[Test]
		public void FuncT1 ()
			{
			IWorkItemResult<bool> wir = _stp.QueueWorkItem (new Func<int, bool> (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<string> wir = _stp.QueueWorkItem (new Func<char, string, string> (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<char> wir = _stp.QueueWorkItem (new Func<bool, char[], object, char> (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<IPAddress> wir = _stp.QueueWorkItem (new Func<long, IntPtr, IPAddress, Guid, IPAddress> (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;
			}
		}
	}
using System;
using Amib.SSharpThreading;
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<int> wir = _wig.QueueWorkItem(new Func<int>(MaxInt));

            int result = wir.GetResult();

            Assert.AreEqual(result, int.MaxValue);
        }

        [Test]
        public void FuncT1()
        {
            IWorkItemResult<bool> wir = _wig.QueueWorkItem(new Func<bool, bool>(Not), true);

            bool result = wir.Result;

            Assert.AreEqual(result, false);
        }

        [Test]
        public void FuncT2()
        {
            IWorkItemResult<string> wir = _wig.QueueWorkItem(new Func<string, string, string>(string.Concat), "ABC", "xyz");

            string result = wir.Result;

            Assert.AreEqual(result, "ABCxyz");
        }

        [Test]
        public void FuncT3()
        {
            IWorkItemResult<string> wir = _wig.QueueWorkItem(new Func<string, int, int, string>(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<int[]> wir = _wig.QueueWorkItem(new Func<int[], int, int, int, int[]>(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;
        }
    }
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
	/// <summary>
	/// Summary description for GetResultExample.
	/// </summary>
	[TestFixture]
	[Category("Test WorkItemsGroup GetResult")]
	public class TestGetResult
	{
		/// <summary>
		/// Example of how to queue a work item and then wait infinitely for the result.
		/// </summary>
		[Test]
		public void BlockingCall() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			bool success = false;

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);

			if (!wir.IsCompleted)
			{
				int result = (int)wir.GetResult();
				success = (1 == result);
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue a work item and then wait on a timeout for the result.
		/// </summary>
		[Test]
		public void Timeout() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			bool success = false;

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);

			try
			{
				wir.GetResult(500, true);
			}
			catch (WorkItemTimeoutException)
			{
				success = true;
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to interrupt the waiting for a work item to complete.
		/// </summary>
		[Test]
		public void WorkItemWaitCanceling() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			ManualResetEvent cancelWaitHandle = new ManualResetEvent(false);

			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.SignalCancel), cancelWaitHandle);

			try
			{
				wir1.GetResult(Crestron.SimplSharp.Timeout.Infinite, true, cancelWaitHandle);
			}
			catch (WorkItemTimeoutException) 
			{
				success = true;
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		private object DoSomeWork(object state)
		{ 
			Thread.Sleep(1000);
			return 1;
		}

		private object SignalCancel(object state)
		{ 
			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
			Thread.Sleep(250);
			cancelWaitHandle.Set();
			return null;
		}
	}
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
	/// <summary>
	/// Summary description for MultipleWorkItemsExample.
	/// </summary>
	[TestFixture]
	[Category("Test WorkItemsGroup MultipleWorkItems")]
	public class TestMultipleWorkItems
	{
		/// <summary>
		/// Example of how to queue several work items and then wait infinitely for 
		/// all of them to complete.
		/// </summary>
		[Test]
		public void WaitAll() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			bool success = true;

			IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			SmartThreadPool.WaitAll(wirs);

			for(int i = 0; i < wirs.Length; ++i)
			{
				if (!wirs[i].IsCompleted)
				{
					success = false;
					break;
				}
				else
				{
					int result = (int)wirs[i].GetResult();
					if (1 != result)
					{
						success = false;
						break;
					}
				}
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait infinitely for 
		/// one of them to complete.
		/// 
		/// You can use this technique if you have several work items that return the same 
		/// infomration, but use different method to aquire it. Just execute all of them at
		/// once and wait for the first work item to complete.
		/// 
		/// For example: You need an information about a person and you can query several 
		/// information sites (FBI, CIA, etc.). Query all of them at once and use the first
		/// answer to arrive.
		/// </summary>
		[Test]
		public void WaitAny() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			bool success = false;

			IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			int index = SmartThreadPool.WaitAny(wirs);

			if (wirs[index].IsCompleted)
			{
				int result = (int)wirs[index].GetResult();
				if (1 == result)
				{
					success = true;
				}
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for all
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAllWithTimeoutSuccess()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

		    IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			bool timeout = !SmartThreadPool.WaitAll(wirs, 1500, true);
			bool success = !timeout;

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for all
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAllWithTimeoutFailure()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

		    IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			bool timeout = !SmartThreadPool.WaitAll(wirs, 10, true);
			bool success = timeout;

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for any
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAnyWithTimeoutSuccess()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			bool success;

			IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			int index = SmartThreadPool.WaitAny(wirs, 1500, true);

			success = (index != WaitHandle.WaitTimeout);

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		}

		/// <summary>
		/// Example of how to queue several work items and then wait on a timeout for any
		/// of them to complete.
		/// </summary>
		[Test]
		public void WaitAnyWithTimeoutFailure()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

		    IWorkItemResult [] wirs = new IWorkItemResult[5];

			for(int i = 0; i < wirs.Length; ++i)
			{
				wirs[i] = 
					workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);
			}

			int index = SmartThreadPool.WaitAny(wirs, 10, true);

			bool success = (index == WaitHandle.WaitTimeout);

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		}

		private object DoSomeWork(object state)
		{ 
			Thread.Sleep(1000);
			return 1;
		}
	}
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
	/// <summary>
	/// Summary description for DoTestPostExecute.
	/// </summary>
	[TestFixture]
	[Category("WorkItemsGroup")]
	public class TestPostExecution
	{
		/// <summary>
		/// 
		/// </summary>
		[Test]
		public void DefaultPostExecute_AlwaysCall()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Always, true));
		}

		[Test]
		public void DefaultPostExecute_NeverCall()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.Never, false));
		}

		[Test]
		public void DefaultPostExecute_CallWhenCanceled()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
		}

		[Test]
		public void DefaultPostExecute_CallWhenNotCanceled()
		{
			Assert.IsTrue(DoTestDefaultPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
		}

		/// <summary>
		/// 
		/// </summary>
		[Test]
		public void PostExecute_AlwaysCall()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Always, true));
		}

		[Test]
		public void PostExecute_NeverCall()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.Never, false));
		}

		[Test]
		public void PostExecute_CallWhenCanceled()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemCanceled, false));
		}

		[Test]
		public void PostExecute_CallWhenNotCanceled()
		{
			Assert.IsTrue(DoTestPostExecute(CallToPostExecute.WhenWorkItemNotCanceled, true));
		}

		/// <summary>
		/// 
		/// </summary>
		[Test]
		public void PostExecuteWithCancel_AlwaysCall()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Always, true));
		}

		[Test]
		public void PostExecuteWithCancel_NeverCall()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.Never, false));
		}

		[Test]
		public void PostExecuteWithCancel_CallWhenCanceled()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemCanceled, true));
		}

		[Test]
		public void PostExecuteWithCancel_CallWhenNotCanceled()
		{
			Assert.IsTrue(DoTestPostExecuteWithCancel(CallToPostExecute.WhenWorkItemNotCanceled, false));
		}


		private class PostExecuteResult
		{
			public ManualResetEvent wh = new ManualResetEvent(false);
		}

		/// <summary>
		/// Example of how to use the post execute callback
		/// </summary>
		private bool DoTestDefaultPostExecute(CallToPostExecute callToPostExecute, bool answer)
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			
			WIGStartInfo wigStartInfo = new WIGStartInfo();
			wigStartInfo.CallToPostExecute = callToPostExecute;
			wigStartInfo.PostExecuteWorkItemCallback = new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork);

			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue, wigStartInfo);

			bool success = false;

			PostExecuteResult postExecuteResult = new PostExecuteResult();

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				postExecuteResult);

			if (!wir.IsCompleted)
			{
				int result = (int)wir.GetResult();
				success = (1 == result);
				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
			}

			smartThreadPool.Shutdown();

			return success;
		}


		/// <summary>
		/// Example of how to use the post execute callback
		/// </summary>
		private bool DoTestPostExecute(CallToPostExecute callToPostExecute, bool answer)
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
			
			bool success = false;

			PostExecuteResult postExecuteResult = new PostExecuteResult();

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				postExecuteResult,
				new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
				callToPostExecute);

			if (!wir.IsCompleted)
			{
				int result = (int)wir.GetResult();
				success = (1 == result);
				success = success && (postExecuteResult.wh.WaitOne(1000, true) == answer);
			}

			smartThreadPool.Shutdown();

			return success;
		}

		/// <summary>
		/// Example of how to queue a work item and then cancel it while it is in the queue.
		/// </summary>
		private bool DoTestPostExecuteWithCancel(CallToPostExecute callToPostExecute, bool answer) 
		{ 
			// Create a SmartThreadPool with only one thread.
			// It just to show how to use the work item canceling feature
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(1);

			bool success = false;
			PostExecuteResult postExecuteResult = new PostExecuteResult();

			// Queue a work item that will occupy the thread in the pool
			workItemsGroup.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				null);

			// Queue another work item that will wait for the first to complete
			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(
					new WorkItemCallback(this.DoSomeWork), 
					postExecuteResult,
					new PostExecuteWorkItemCallback(this.DoSomePostExecuteWork),
					callToPostExecute);


			// Wait a while for the thread pool to start executing the first work item
			Thread.Sleep(100);

			// Cancel the second work item while it still in the queue
			if (wir.Cancel())
			{
				success = (postExecuteResult.wh.WaitOne(1000, true) == answer);
			}

			smartThreadPool.Shutdown();

			return success;
		} 

		private object DoSomeWork(object state)
		{ 
			Thread.Sleep(1000);
			return 1;
		}

		private void DoSomePostExecuteWork(IWorkItemResult wir)
		{ 
			PostExecuteResult postExecuteResult = wir.State as PostExecuteResult;
			postExecuteResult.wh.Set();
		}

		private object SignalCancel(object state)
		{ 
			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
			Thread.Sleep(250);
			cancelWaitHandle.Set();
			return null;
		}
	}
}
using NUnit.Framework;

using Amib.SSharpThreading;
using SmartThreadPoolTests;

namespace WorkItemsGroupTests
{
	/// <summary>
    /// Tests for QueueWorkItem.
	/// </summary>
	[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);
        }
	}
}
using System;
using Crestron.SimplSharpPro.CrestronThread;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
    public class CallerState
    {
        public int Value { get; private set; }

        protected void IncValue()
        {
            ++Value;
        }
    }

    public class NonDisposableCallerState : CallerState
    {
        public NonDisposableCallerState()
        {
            IncValue();
        }
    }

    public class DisposableCallerState : CallerState, IDisposable
    {
        public DisposableCallerState()
        {
            IncValue();
        }

        #region IDisposable Members

        public void Dispose()
        {
            IncValue();
        }

        #endregion
    }


	/// <summary>
	/// Summary description for StateDisposeExample.
	/// </summary>
	[TestFixture]
	[Category("WorkItemsGroup")]
	public class TestStateDispose
	{
	    /// <summary>
        /// Example of non disposable caller state
        /// </summary>
        [Test]
        public void DisposeCallerState() 
        { 
			SmartThreadPool smartThreadPool = new SmartThreadPool();

			WIGStartInfo wigStartInfo = new WIGStartInfo();
            wigStartInfo.DisposeOfStateObjects = true;

			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue, wigStartInfo);

            CallerState nonDisposableCallerState = new NonDisposableCallerState();
            CallerState disposableCallerState = new DisposableCallerState();

            IWorkItemResult wir1 = 
                workItemsGroup.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                nonDisposableCallerState);

            IWorkItemResult wir2 = 
                workItemsGroup.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                disposableCallerState);

            wir1.GetResult();
			Assert.AreEqual(1, nonDisposableCallerState.Value);

            wir2.GetResult();

			// Wait a little bit for the working thread to call dispose on the 
			// work item's state.
			workItemsGroup.WaitForIdle();

			Assert.AreEqual(2, disposableCallerState.Value);

            smartThreadPool.Shutdown();
        } 

        /// <summary>
        /// Example of non disposable caller state
        /// </summary>
        [Test]
        public void DontDisposeCallerState() 
        { 
			SmartThreadPool smartThreadPool = new SmartThreadPool();

			WIGStartInfo wigStartInfo = new WIGStartInfo();
			wigStartInfo.DisposeOfStateObjects = false;

			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue, wigStartInfo);

            CallerState nonDisposableCallerState = new NonDisposableCallerState();
            CallerState disposableCallerState = new DisposableCallerState();

            IWorkItemResult wir1 = 
                workItemsGroup.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                nonDisposableCallerState);

            IWorkItemResult wir2 = 
                workItemsGroup.QueueWorkItem(
                new WorkItemCallback(this.DoSomeWork), 
                disposableCallerState);

            wir1.GetResult();
            bool success = (1 == nonDisposableCallerState.Value);

            wir2.GetResult();

            success = success && (1 == disposableCallerState.Value);

            smartThreadPool.Shutdown();

            Assert.IsTrue(success);
        } 

        private object DoSomeWork(object state)
        { 
            Thread.Sleep(1000);
            return 1;
        }
    }
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;
using NUnit.Framework;
using Interlocked = Crestron.SimplSharp.Interlocked;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
	/// <summary>
	/// Summary description for TestWaitForIdle.
	/// </summary>
	[TestFixture]
	[Category("WorkItemsGroup")]
	public class TestWaitForIdle
	{
	    /// <summary>
        /// Example of waiting for idle
        /// </summary>
        [Test]
        public void WaitForIdle() 
        { 
            SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

            ManualResetEvent isRunning = new ManualResetEvent(false);

            for (int i = 0; i < 4; ++i)
            {
                workItemsGroup.QueueWorkItem(delegate { isRunning.WaitOne(); });
            }

            bool success = !workItemsGroup.WaitForIdle(1000);

            isRunning.Set();

            success = success && workItemsGroup.WaitForIdle(1000);

            smartThreadPool.Shutdown();

            Assert.IsTrue(success);
        } 

		[Test]
		public void WaitForIdleOnSTPThread() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			IWorkItemResult wir = workItemsGroup.QueueWorkItem(
				new WorkItemCallback(this.DoWaitForIdle), 
				workItemsGroup);

			Exception e;
			wir.GetResult(out e);

			smartThreadPool.Shutdown();

			Assert.IsNotNull(e);
		}

		[Test]
		public void WaitForIdleOnSTPThreadForAnotherWorkItemsGroup()
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);
			IWorkItemsGroup workItemsGroup1 = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);
			IWorkItemsGroup workItemsGroup2 = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			workItemsGroup1.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				1000);

			workItemsGroup1.QueueWorkItem(
				new WorkItemCallback(this.DoSomeWork), 
				1000);

			IWorkItemResult wir = workItemsGroup2.QueueWorkItem(
				new WorkItemCallback(this.DoWaitForIdle), 
				workItemsGroup1);

			Exception e;
			wir.GetResult(out e);

			smartThreadPool.Shutdown();

			Assert.IsNull(e);
		} 


        private int _x = 0;
        private object DoSomeWork(object state)
        {
            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;
        }

		private object DoWaitForIdle(object state)
		{ 
			IWorkItemsGroup workItemsGroup = state as IWorkItemsGroup;
			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();
        }

        [Test]
        public void WaitForIdleEvent()
        {
            SmartThreadPool smartThreadPool = new SmartThreadPool();
            IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(1);
            ManualResetEvent wigIsIdle = new ManualResetEvent(false);

            workItemsGroup.OnIdle += wig => wigIsIdle.Set();

            workItemsGroup.QueueWorkItem(() => { });

            bool eventFired = wigIsIdle.WaitOne(100, true);

            smartThreadPool.Shutdown();

            Assert.IsTrue(eventFired);
        }
	}
}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSMono.Diagnostics;
using SSharp.Threading;
using Interlocked = Crestron.SimplSharp.Interlocked;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
{
	/// <summary>
	/// Summary description for TestWaitForIdle.
	/// </summary>
	[TestFixture]
	[Category("TestWaitForIdle")]
	public class TestWaitForIdle
	{
	    /// <summary>
        /// Example of waiting for idle
        /// </summary>
        [Test]
        public void WaitForIdle() 
        { 
            SmartThreadPool smartThreadPool = new SmartThreadPool(10*1000, 25, 0);

            ManualResetEvent isRunning = new ManualResetEvent(false);

            for(int i = 0; i < 4; ++i)
            {
                smartThreadPool.QueueWorkItem(delegate { isRunning.WaitOne(); });
            }

            bool success = !smartThreadPool.WaitForIdle(1000);

            isRunning.Set();

            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;
		}
	}
}
ï»¿using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;

using NUnit.Framework;

using Amib.SSharpThreading;

namespace SmartThreadPoolTests
	{
	[TestFixture]
	[Category ("TestWorkItemTimeout")]
	public class TestWorkItemTimeout
		{
		/// <summary>
		/// 1. Create STP in suspended mode
		/// 2. Queue work item into the STP
		/// 3. Wait for the work item to expire
		/// 4. Work item's GetResult should throw WorkItemCancelException
		/// </summary>        
		[Test]
		public void CancelInQueueWorkItem ()
			{
			Assert.Throws<WorkItemCancelException> (() =>
				{
					STPStartInfo stpStartInfo = new STPStartInfo ();
					stpStartInfo.StartSuspended = true;

					bool hasRun = false;

					SmartThreadPool stp = new SmartThreadPool (stpStartInfo);
					IWorkItemResult wir = stp.QueueWorkItem (new WorkItemInfo ()
						{
							Timeout = 500
						}, state =>
							{
								hasRun = true;
								return null;
							});

					Assert.IsFalse (wir.IsCanceled);

					Thread.Sleep (2000);

					Assert.IsTrue (wir.IsCanceled);

					stp.Start ();
					stp.WaitForIdle ();

					Assert.IsFalse (hasRun);

					try
						{
						wir.GetResult ();
						}
					finally
						{
						stp.Shutdown ();
						}
				});
			}

		/// <summary>
		/// 1. Create STP
		/// 2. Queue work item that takes some time
		/// 3. Wait for it to start
		/// 4. The work item timeout expires
		/// 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
		/// </summary>        
		[Test]
		public void TimeoutInProgressWorkItemWithSample ()
			{
			bool timedout = false;
			ManualResetEvent waitToStart = new ManualResetEvent (false);
			ManualResetEvent waitToComplete = new ManualResetEvent (false);

			SmartThreadPool stp = new SmartThreadPool ();
			IWorkItemResult wir = stp.QueueWorkItem (
				new WorkItemInfo () { Timeout = 500 },
				state =>
					{
					waitToStart.Set ();
					Thread.Sleep (1000);
					timedout = SmartThreadPool.IsWorkItemCanceled;
					waitToComplete.Set ();
					return null;
					});

			waitToStart.WaitOne ();

			waitToComplete.WaitOne ();

			Assert.IsTrue (timedout);

			stp.Shutdown ();
			}

		/// <summary>
		/// 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. The work item expires
		/// 5. Work item's GetResult should return value
		/// </summary>        
		[Test]
		public void TimeoutCompletedWorkItem ()
			{
			SmartThreadPool stp = new SmartThreadPool ();
			IWorkItemResult wir =
				stp.QueueWorkItem (
				new WorkItemInfo () { Timeout = 500 },
				state => 1);

			stp.WaitForIdle ();

			Assert.AreEqual (wir.GetResult (), 1);

			Thread.Sleep (1000);

			Assert.AreEqual (wir.GetResult (), 1);

			stp.Shutdown ();
			}

		/// <summary>
		/// 1. Create STP
		/// 2. Queue work item that takes some time
		/// 3. Wait for it to start
		/// 4. Cancel the work item (soft)
		/// 5. Call to AbortOnWorkItemCancel
		/// 5. Wait for the STP to get idle
		/// 6. Make sure nothing ran in the work item after the AbortOnWorkItemCancel
		/// </summary>        
		[Test]
		public void TimeoutInProgressWorkItemSoftWithAbortOnWorkItemCancel ()
			{
			bool abortFailed = false;
			ManualResetEvent waitToStart = new ManualResetEvent (false);
			ManualResetEvent waitToComplete = new ManualResetEvent (false);

			SmartThreadPool stp = new SmartThreadPool ();
			IWorkItemResult wir = stp.QueueWorkItem (
				new WorkItemInfo () { Timeout = 500 },
				state =>
					{
					waitToStart.Set ();
					Thread.Sleep (1000);
					SmartThreadPool.AbortOnWorkItemCancel ();
					abortFailed = true;
					return null;
					});

			waitToStart.WaitOne ();

			stp.WaitForIdle ();

			Assert.IsTrue (wir.IsCanceled);
			Assert.IsFalse (abortFailed);
			stp.Shutdown ();
			}
		}
	}
using System;
using Crestron.SimplSharpPro.CrestronThread;
using SSharp.Threading;
using NUnit.Framework;

using Amib.SSharpThreading;

namespace WorkItemsGroupTests
{
	/// <summary>
	/// Summary description for TestWorkItemsGroups.
	/// </summary>
	[TestFixture]
	[Category("TestWorkItemsGroups")]
	public class TestWorkItemsGroups
	{
	    [Test]
		public void BlockingCall() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			bool success = false;

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);

			if (!wir.IsCompleted)
			{
				int result = (int)wir.GetResult();
				success = (1 == result);
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 


		/// <summary>
		/// Example of how to queue a work item and then wait on a timeout for the result.
		/// </summary>
		[Test]
		public void Timeout() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			bool success = false;

			IWorkItemResult wir = 
				workItemsGroup.QueueWorkItem(new WorkItemCallback(this.DoSomeWork), null);

			try
			{
				wir.GetResult(500, true);
			}
			catch (WorkItemTimeoutException)
			{
				success = true;
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		/// <summary>
		/// Example of how to interrupt the waiting for a work item to complete.
		/// </summary>
		[Test]
		public void WorkItemWaitCanceling() 
		{ 
			SmartThreadPool smartThreadPool = new SmartThreadPool();
			IWorkItemsGroup workItemsGroup = smartThreadPool.CreateWorkItemsGroup(int.MaxValue);

			ManualResetEvent cancelWaitHandle = new ManualResetEvent(false);

			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.SignalCancel), cancelWaitHandle);

			try
			{
				wir1.GetResult(Crestron.SimplSharp.Timeout.Infinite, true, cancelWaitHandle);
			}
			catch (WorkItemTimeoutException) 
			{
				success = true;
			}

			smartThreadPool.Shutdown();

			Assert.IsTrue(success);
		} 

		private object DoSomeWork(object state)
		{ 
			Thread.Sleep(1000);
			return 1;
		}

		private object SignalCancel(object state)
		{ 
			ManualResetEvent cancelWaitHandle = state as ManualResetEvent;
			Thread.Sleep(250);
			cancelWaitHandle.Set();
			return null;
		}

		[Test]
		public void Concurrency() 
		{ 
		}

		[Test]
		public void WaitForIdle() 
		{ 
		}

		[Test]
		public void OnIdleEvent() 
		{ 
		}

		[Test]
		public void MultipleGroups() 
		{ 
		}

	}
}
using NUnit.Framework;
using Amib.SSharpThreading.Internal;

namespace PriorityQueueTests
{
	/// <summary>
	/// Summary description for TestWorkItemsQueue.
	/// </summary>
	[TestFixture]
	[Category("TestWorkItemsQueue")]
	public class TestWorkItemsQueue
	{
	    [Test]
		public void Init()
		{
		}

		[Test]
		public void IdempotenceWaiterEntry()
		{
			WorkItemsQueue q = new WorkItemsQueue();

			Assert.AreEqual(0, q.WaitersCount);

			WorkItemsQueue.WaiterEntry we1 = new WorkItemsQueue.WaiterEntry();
			q.PushWaiter(we1);

			Assert.AreEqual(1, q.WaitersCount);

			q.PushWaiter(we1);	
	
			Assert.AreEqual(1, q.WaitersCount);

			WorkItemsQueue.WaiterEntry we2 = new WorkItemsQueue.WaiterEntry();
			q.PushWaiter(we2);	

			Assert.AreEqual(2, q.WaitersCount);

			q.PushWaiter(we2);	

			Assert.AreEqual(2, q.WaitersCount);
		}
	}
}
Microsoft Public License (MS-PL)
[OSI Approved License]
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.

1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.

2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.

3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.