In the first version of the Async Core, threads in the TaskPool would spin (continually become the leader and relinquish leadership even though there was no items in the request queue) this would be ok for general server applications however in the case of DISCUS the taskpool threads will be spinning in the IIS process while doing no useful work.

rg2023 [2003-01-30 18:47:00]
In the first version of the Async Core, threads in the TaskPool would spin (continually become the leader and relinquish leadership even though there was no items in the request queue) this would be ok for general server applications however in the case of DISCUS the taskpool threads will be spinning in the IIS process while doing no useful work.
1) Added threadsafe signal object implementations (Auto & Manual ResetSignalObject) these are used to indicate when there are requests sitting in the queue. These objects are referenced by all threads in the TaskPool
2) Modified TPTask constructor to take a reference to a signal object
3) Made the request queue static and private and provided a synchronized access method that will signal any waiting threads that a request has arrived.
Filename
csharpsrc/AsyncCore/AsyncCore.csproj
csharpsrc/AsyncCore/AsyncCore.suo
csharpsrc/AsyncCore/AutoResetSignalObject.cs
csharpsrc/AsyncCore/ISignalObject.cs
csharpsrc/AsyncCore/ManualResetSignalObject.cs
csharpsrc/AsyncCore/TPTask.cs
csharpsrc/AsyncCore/TaskPool.cs
csharpsrc/AsyncCore/ThreadsafeSignalObject.cs
diff --git a/csharpsrc/AsyncCore/AsyncCore.csproj b/csharpsrc/AsyncCore/AsyncCore.csproj
index 9f87238..0bb8946 100644
--- a/csharpsrc/AsyncCore/AsyncCore.csproj
+++ b/csharpsrc/AsyncCore/AsyncCore.csproj
@@ -82,6 +82,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "AutoResetSignalObject.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "ISignalObject.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "ManualResetSignalObject.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "TaskPool.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -97,6 +112,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "ThreadsafeSignalObject.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "TPTask.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/csharpsrc/AsyncCore/AsyncCore.suo b/csharpsrc/AsyncCore/AsyncCore.suo
index 3a39f4c..393c51e 100644
Binary files a/csharpsrc/AsyncCore/AsyncCore.suo and b/csharpsrc/AsyncCore/AsyncCore.suo differ
diff --git a/csharpsrc/AsyncCore/AutoResetSignalObject.cs b/csharpsrc/AsyncCore/AutoResetSignalObject.cs
new file mode 100644
index 0000000..386f0cb
--- /dev/null
+++ b/csharpsrc/AsyncCore/AutoResetSignalObject.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Threading;
+
+namespace PSL.AsyncCore
+{
+	/// <summary>
+	/// Summary description for AutoResetSignalObject.
+	/// </summary>
+	public class AutoResetSignalObject:ThreadsafeSignalObject
+	{
+		private AutoResetEvent m_signal = null;
+
+		public AutoResetSignalObject( bool bInitialState )
+		{
+			this.m_signal = new AutoResetEvent( bInitialState );
+		}
+
+		public override void Signal()
+		{
+			lock( this )
+			{
+				this.m_signal.Set();
+			}
+		}
+
+		public override void Reset()
+		{
+			lock( this )
+			{
+				this.m_signal.Reset();
+			}
+		}
+
+		public override void WaitOnSignal()
+		{
+			this.m_signal.WaitOne();
+		}
+
+		public override void WaitOnSignal( int nMillisecondsTimeout, bool bExitContext )
+		{
+			this.m_signal.WaitOne( nMillisecondsTimeout, bExitContext );
+		}
+
+		public override void WaitOnSignal( TimeSpan timeout, bool bExitContext )
+		{
+			this.m_signal.WaitOne( timeout, bExitContext );
+		}
+	}
+}
diff --git a/csharpsrc/AsyncCore/ISignalObject.cs b/csharpsrc/AsyncCore/ISignalObject.cs
new file mode 100644
index 0000000..a30c464
--- /dev/null
+++ b/csharpsrc/AsyncCore/ISignalObject.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace PSL.AsyncCore
+{
+	/// <summary>
+	/// Summary description for ISignalObject.
+	/// </summary>
+	public interface ISignalObject
+	{
+		void Signal();
+		void Reset();
+		void WaitOnSignal();
+		void WaitOnSignal( int nMillisecondsTimeout, bool bExitContext );
+		void WaitOnSignal( TimeSpan timeout, bool bExitContext );
+	}
+}
diff --git a/csharpsrc/AsyncCore/ManualResetSignalObject.cs b/csharpsrc/AsyncCore/ManualResetSignalObject.cs
new file mode 100644
index 0000000..353171e
--- /dev/null
+++ b/csharpsrc/AsyncCore/ManualResetSignalObject.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Threading;
+
+namespace PSL.AsyncCore
+{
+	/// <summary>
+	/// Summary description for ManualResetSignalObject.
+	/// </summary>
+	public class ManualResetSignalObject:ThreadsafeSignalObject
+	{
+		private ManualResetEvent m_signal = null;
+		private bool m_bAllowReset = true;
+
+		public ManualResetSignalObject( bool bInitialState )
+		{
+			this.m_signal = new ManualResetEvent( bInitialState );
+		}
+
+		public bool AllowReset
+		{
+			get
+			{ return this.m_bAllowReset; }
+
+			set
+			{
+				lock( this )
+				{
+					this.m_bAllowReset = value;
+				}
+			}
+		}
+
+		public override void Signal()
+		{
+			lock( this )
+			{
+				this.m_signal.Set();
+			}
+		}
+
+		public override void Reset()
+		{
+			if( !m_bAllowReset )
+				return;
+
+			lock( this )
+			{
+				this.m_signal.Reset();
+			}
+		}
+
+		public override void WaitOnSignal()
+		{
+			this.m_signal.WaitOne();
+		}
+
+		public override void WaitOnSignal( int nMillisecondsTimeout, bool bExitContext )
+		{
+			this.m_signal.WaitOne( nMillisecondsTimeout, bExitContext );
+		}
+
+		public override void WaitOnSignal( TimeSpan timeout, bool bExitContext )
+		{
+			this.m_signal.WaitOne( timeout, bExitContext );
+		}
+	}
+}
diff --git a/csharpsrc/AsyncCore/TPTask.cs b/csharpsrc/AsyncCore/TPTask.cs
index a68c5c6..a723636 100644
--- a/csharpsrc/AsyncCore/TPTask.cs
+++ b/csharpsrc/AsyncCore/TPTask.cs
@@ -63,8 +63,9 @@ namespace PSL.AsyncCore
 		/// <summary>
 		/// Condition variable used to synchronize TPTasks running in the TaskPool
 		/// </summary>
-		private AutoResetEvent m_condition = null;
-
+		private AutoResetSignalObject m_leaderSignal = null;
+		private ManualResetSignalObject m_workSignal = null;
+
 		/// <summary>
 		/// After a thread becomes the leader, if there are no
 		/// pending requests the leader will exit the TaskPool
@@ -82,6 +83,7 @@ namespace PSL.AsyncCore
 		/// </summary>
 		private static bool bLeaderAvailable = false;

+
 		/// <summary>
 		/// Returns the number of TPTask threads running in this process.
 		/// </summary>
@@ -91,7 +93,7 @@ namespace PSL.AsyncCore
 		/// Queue used to hold TaskRequests to service
 		/// <seealso cref="TaskRequest"/>
 		/// </summary>
-		internal static Queue requestQ = new Queue();
+		private static Queue requestQ = new Queue();

 		/// <summary>
 		/// Ctor
@@ -99,9 +101,10 @@ namespace PSL.AsyncCore
 		/// </summary>
 		/// <param name="condition">Condition variable shared by all TPTasks in
 		/// a TaskPool</param>
-		public TPTask( ref AutoResetEvent condition )
+		internal TPTask( ref AutoResetSignalObject leaderSignal, ref ManualResetSignalObject workSignal )
 		{
-			m_condition = condition;
+			m_leaderSignal = leaderSignal;
+			m_workSignal = workSignal;
 		}

 		/// <summary>
@@ -148,6 +151,15 @@ namespace PSL.AsyncCore
 			get
 			{ return nNumTasks; }
 		}
+
+		public static void QueueTaskRequest( TaskRequest req )
+		{
+			lock( TPTask.requestQ.SyncRoot )
+			{
+				// Add request item to TPTask requestQ
+				TPTask.requestQ.Enqueue( req );
+			}
+		}

 		/// <summary>
 		/// Procedure enforces the leader-followers behavior of TPTask threads
@@ -161,35 +173,53 @@ namespace PSL.AsyncCore
 			while( true )
 			{
 				// Begin critical section
-				Monitor.Enter( this );
-				// While a leader has been selected...wait around
-				while( bLeaderAvailable )
+				lock( this )
 				{
-					m_condition.WaitOne();
+					Console.WriteLine( "Checking for leader" );
+					// While a leader has been selected...wait around
+					while( bLeaderAvailable )
+					{
+						Console.WriteLine( "Waiting to be leader" );
+						m_leaderSignal.WaitOnSignal();
+						Console.WriteLine( "Received leader signal" );
+					}
+
+					// Assert self as leader before leaving critical section
+					bLeaderAvailable = true;
+					// Leave critical section
+					Console.WriteLine( "Leaving as leader" );
 				}

-				// Assert self as leader before leaving critical section
-				bLeaderAvailable = true;
-				// Leave critical section
-				Monitor.Exit( this );
+				// Only one thread active at this point
+
+				Console.WriteLine( "Waiting on work" );
+
+				// waiting on work
+				m_workSignal.WaitOnSignal();
+
+				Console.WriteLine( "Got work" );

 				bool bExitLoop = false;
-
+
 				// Nothing else to do so this thread can exit or be recycled
 				if( requestQ.Count == 0 )
+				{
+					// No work to do so let other threads (next leader) wait on work
+					m_workSignal.Reset();
 					bExitLoop = true;
-
+				}
+
 				// Begin critical section
-				Monitor.Enter( this );
-
-				// Signal self is no longer the leader
-				bLeaderAvailable = false;
-
-				Monitor.Exit( this );
-
-				// Signal a follower to become the new leader
-				m_condition.Set();
+				lock( this )
+				{
+					// Signal self is no longer the leader
+					bLeaderAvailable = false;

+					// Signal a follower to become the new leader
+					m_leaderSignal.Signal();
+					// Leave critical section
+				}
+
 				if( bExitLoop )
 				{
 					// If this task is not marked as recyclable
diff --git a/csharpsrc/AsyncCore/TaskPool.cs b/csharpsrc/AsyncCore/TaskPool.cs
index d091f6b..9500fe3 100644
--- a/csharpsrc/AsyncCore/TaskPool.cs
+++ b/csharpsrc/AsyncCore/TaskPool.cs
@@ -49,8 +49,9 @@ namespace PSL.AsyncCore
 		/// Condition variable used to synchronize TPTask threads running in
 		/// the TaskPool and ensure they abide by the leaders-followers model
 		/// </summary>
-		private static AutoResetEvent condition = new AutoResetEvent( true );
-
+		private static AutoResetSignalObject leaderSignal = new AutoResetSignalObject( false );
+		private static ManualResetSignalObject workSignal = new ManualResetSignalObject( false );
+
 		/// <summary>
 		/// Indicates whether the TaskPool has been initialized or not
 		/// </summary>
@@ -76,7 +77,7 @@ namespace PSL.AsyncCore
 		public static int Threads
 		{
 			get
-			{return TPTask.Tasks; }
+			{ return TPTask.Tasks; }
 		}

 		/// <summary>
@@ -155,7 +156,7 @@ namespace PSL.AsyncCore

 			try
 			{
-				lock( TPTask.requestQ.SyncRoot )
+				/*lock( TPTask.requestQ.SyncRoot )
 				{
 					// Generate Guid - task id if none exists
 					if( req.TaskID == Guid.Empty )
@@ -166,7 +167,18 @@ namespace PSL.AsyncCore
 					// Add request item to TPTask requestQ
 					TPTask.requestQ.Enqueue( req );
 				}// End-lock on TPTask.requestQ
+				*/
+
+				// Generate Guid - task id if none exists
+				if( req.TaskID == Guid.Empty )
+					req.TaskID = Guid.NewGuid();
+
+				taskID = req.TaskID;

+				TPTask.QueueTaskRequest( req );
+
+				TaskPool.workSignal.Signal();
+
 				// Tag taskID to notification callback
 				lock( ClientNotification.SyncRoot )
 				{
@@ -214,7 +226,7 @@ namespace PSL.AsyncCore
 					{
 						TPTaskInit init = new TPTaskInit();
 						// Create new TPTask
-						init.Task = new TPTask( ref condition );
+						init.Task = new TPTask( ref leaderSignal, ref workSignal );
 						// Keep thread in ThreadPool
 						init.Task.RecycleTask = true;
 						// Create worker thread
@@ -247,6 +259,9 @@ namespace PSL.AsyncCore
 			{
 				// Signal that we are shutting down
 				bShuttingDown = true;
+
+				workSignal.AllowReset = false;
+				workSignal.Signal();

 				for( int i = 0; i < lstTasks.Count; i++ )
 				{
diff --git a/csharpsrc/AsyncCore/ThreadsafeSignalObject.cs b/csharpsrc/AsyncCore/ThreadsafeSignalObject.cs
new file mode 100644
index 0000000..dae8b5b
--- /dev/null
+++ b/csharpsrc/AsyncCore/ThreadsafeSignalObject.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Threading;
+
+namespace PSL.AsyncCore
+{
+	/// <summary>
+	/// Summary description for ThreadsafeSignalObject.
+	/// </summary>
+	public abstract class ThreadsafeSignalObject:ISignalObject
+	{
+		public ThreadsafeSignalObject()
+		{
+		}
+
+		public abstract void Signal();
+		public abstract void Reset();
+		public abstract void WaitOnSignal();
+		public abstract void WaitOnSignal( int nMillisecondsTimeout, bool bExitContext );
+		public abstract void WaitOnSignal( TimeSpan timeSpan, bool bExitContext );
+	}
+}