@@ -45,6 +45,7 @@ public class SingleThreadEventExecutor : AbstractScheduledEventExecutor
4545 PreciseTimeSpan gracefulShutdownQuietPeriod ;
4646 PreciseTimeSpan gracefulShutdownTimeout ;
4747 readonly ISet < Action > shutdownHooks = new HashSet < Action > ( ) ;
48+ long progress ;
4849
4950 /// <summary>Creates a new instance of <see cref="SingleThreadEventExecutor"/>.</summary>
5051 public SingleThreadEventExecutor ( string threadName , TimeSpan breakoutInterval )
@@ -86,6 +87,21 @@ protected SingleThreadEventExecutor(IEventExecutorGroup parent, string threadNam
8687 /// </summary>
8788 public TaskScheduler Scheduler => this . scheduler ;
8889
90+ /// <summary>
91+ /// Allows to track whether executor is progressing through its backlog. Useful for diagnosing / mitigating stalls due to blocking calls in conjunction with IsBacklogEmpty property.
92+ /// </summary>
93+ public long Progress => Volatile . Read ( ref this . progress ) ;
94+
95+ /// <summary>
96+ /// Indicates whether executor's backlog is empty. Useful for diagnosing / mitigating stalls due to blocking calls in conjunction with Progress property.
97+ /// </summary>
98+ public bool IsBacklogEmpty => this . taskQueue . IsEmpty ;
99+
100+ /// <summary>
101+ /// Gets length of backlog of tasks queued for immediate execution.
102+ /// </summary>
103+ public int BacklogLength => this . taskQueue . Count ;
104+
89105 void Loop ( )
90106 {
91107 this . SetCurrentExecutor ( this ) ;
@@ -140,6 +156,8 @@ public override void Execute(IRunnable task)
140156 }
141157 }
142158
159+ protected override IEnumerable < IEventExecutor > GetItems ( ) => new [ ] { this } ;
160+
143161 protected void WakeUp ( bool inEventLoop )
144162 {
145163 if ( ! inEventLoop || ( this . executionState == ST_SHUTTING_DOWN ) )
@@ -152,12 +170,12 @@ protected void WakeUp(bool inEventLoop)
152170 /// Adds an <see cref="Action"/> which will be executed on shutdown of this instance.
153171 /// </summary>
154172 /// <param name="action">The <see cref="Action"/> to run on shutdown.</param>
155- public void AddShutdownHook ( Action action )
173+ public void AddShutdownHook ( Action action )
156174 {
157- if ( this . InEventLoop )
175+ if ( this . InEventLoop )
158176 {
159177 this . shutdownHooks . Add ( action ) ;
160- }
178+ }
161179 else
162180 {
163181 this . Execute ( ( ) => this . shutdownHooks . Add ( action ) ) ;
@@ -169,53 +187,53 @@ public void AddShutdownHook(Action action)
169187 /// executed on shutdown of this instance.
170188 /// </summary>
171189 /// <param name="action">The <see cref="Action"/> to remove.</param>
172- public void RemoveShutdownHook ( Action action )
190+ public void RemoveShutdownHook ( Action action )
173191 {
174- if ( this . InEventLoop )
192+ if ( this . InEventLoop )
175193 {
176194 this . shutdownHooks . Remove ( action ) ;
177- }
195+ }
178196 else
179197 {
180198 this . Execute ( ( ) => this . shutdownHooks . Remove ( action ) ) ;
181199 }
182200 }
183201
184- bool RunShutdownHooks ( )
202+ bool RunShutdownHooks ( )
185203 {
186204 bool ran = false ;
187-
205+
188206 // Note shutdown hooks can add / remove shutdown hooks.
189- while ( this . shutdownHooks . Count > 0 )
207+ while ( this . shutdownHooks . Count > 0 )
190208 {
191209 var copy = this . shutdownHooks . ToArray ( ) ;
192210 this . shutdownHooks . Clear ( ) ;
193211
194212 for ( var i = 0 ; i < copy . Length ; i ++ )
195213 {
196- try
214+ try
197215 {
198216 copy [ i ] ( ) ;
199- }
200- catch ( Exception ex )
217+ }
218+ catch ( Exception ex )
201219 {
202220 Logger . Warn ( "Shutdown hook raised an exception." , ex ) ;
203- }
204- finally
221+ }
222+ finally
205223 {
206224 ran = true ;
207225 }
208226 }
209227 }
210228
211- if ( ran )
229+ if ( ran )
212230 {
213231 this . lastExecutionTime = PreciseTimeSpan . FromStart ;
214232 }
215233
216234 return ran ;
217235 }
218-
236+
219237
220238 /// <inheritdoc cref="IEventExecutor"/>
221239 public override Task ShutdownGracefullyAsync ( TimeSpan quietPeriod , TimeSpan timeout )
@@ -398,6 +416,7 @@ protected bool RunAllTasks()
398416
399417 while ( true )
400418 {
419+ Volatile . Write ( ref this . progress , this . progress + 1 ) ; // volatile write is enough as this is the only thread ever writing
401420 SafeExecute ( task ) ;
402421 task = this . PollTask ( ) ;
403422 if ( task == null )
0 commit comments