diff --git a/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs b/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs
index 9a0efe00d..36a2ddd2e 100644
--- a/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs
+++ b/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs
@@ -4,6 +4,7 @@
namespace DotNetty.Common.Concurrency
{
using System;
+ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using DotNetty.Common.Internal.Logging;
@@ -46,6 +47,11 @@ protected AbstractEventExecutor(IEventExecutorGroup parent)
///
public bool InEventLoop => this.IsInEventLoop(Thread.CurrentThread);
+ ///
+ public IEnumerable Items => this.GetItems();
+
+ protected abstract IEnumerable GetItems();
+
///
public abstract bool IsInEventLoop(Thread thread);
diff --git a/src/DotNetty.Common/Concurrency/AbstractEventExecutorGroup.cs b/src/DotNetty.Common/Concurrency/AbstractEventExecutorGroup.cs
index c9e7653e5..5cbe1149d 100644
--- a/src/DotNetty.Common/Concurrency/AbstractEventExecutorGroup.cs
+++ b/src/DotNetty.Common/Concurrency/AbstractEventExecutorGroup.cs
@@ -4,6 +4,7 @@
namespace DotNetty.Common.Concurrency
{
using System;
+ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -20,6 +21,8 @@ public abstract class AbstractEventExecutorGroup : IEventExecutorGroup
public abstract Task TerminationCompletion { get; }
+ public IEnumerable Items => this.GetItems();
+
public abstract IEventExecutor GetNext();
public void Execute(IRunnable task) => this.GetNext().Execute(task);
@@ -65,5 +68,7 @@ public abstract class AbstractEventExecutorGroup : IEventExecutorGroup
public Task ShutdownGracefullyAsync() => this.ShutdownGracefullyAsync(DefaultShutdownQuietPeriod, DefaultShutdownTimeout);
public abstract Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan timeout);
+
+ protected abstract IEnumerable GetItems();
}
}
\ No newline at end of file
diff --git a/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs b/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs
index 1d0af9c9c..091cbf966 100644
--- a/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs
+++ b/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs
@@ -4,6 +4,7 @@
namespace DotNetty.Common.Concurrency
{
using System;
+ using System.Collections.Generic;
using System.Threading.Tasks;
///
@@ -11,6 +12,11 @@ namespace DotNetty.Common.Concurrency
///
public interface IEventExecutorGroup : IScheduledExecutorService
{
+ ///
+ /// Returns list of owned event executors.
+ ///
+ IEnumerable Items { get; }
+
///
/// Returns true if and only if this executor is being shut down via .
///
diff --git a/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs b/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs
index 7cd814dc6..225871962 100644
--- a/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs
+++ b/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs
@@ -45,6 +45,7 @@ public class SingleThreadEventExecutor : AbstractScheduledEventExecutor
PreciseTimeSpan gracefulShutdownQuietPeriod;
PreciseTimeSpan gracefulShutdownTimeout;
readonly ISet shutdownHooks = new HashSet();
+ long progress;
/// Creates a new instance of .
public SingleThreadEventExecutor(string threadName, TimeSpan breakoutInterval)
@@ -86,6 +87,21 @@ protected SingleThreadEventExecutor(IEventExecutorGroup parent, string threadNam
///
public TaskScheduler Scheduler => this.scheduler;
+ ///
+ /// Allows to track whether executor is progressing through its backlog. Useful for diagnosing / mitigating stalls due to blocking calls in conjunction with IsBacklogEmpty property.
+ ///
+ public long Progress => Volatile.Read(ref this.progress);
+
+ ///
+ /// Indicates whether executor's backlog is empty. Useful for diagnosing / mitigating stalls due to blocking calls in conjunction with Progress property.
+ ///
+ public bool IsBacklogEmpty => this.taskQueue.IsEmpty;
+
+ ///
+ /// Gets length of backlog of tasks queued for immediate execution.
+ ///
+ public int BacklogLength => this.taskQueue.Count;
+
void Loop()
{
this.SetCurrentExecutor(this);
@@ -140,6 +156,8 @@ public override void Execute(IRunnable task)
}
}
+ protected override IEnumerable GetItems() => new[] { this };
+
protected void WakeUp(bool inEventLoop)
{
if (!inEventLoop || (this.executionState == ST_SHUTTING_DOWN))
@@ -152,12 +170,12 @@ protected void WakeUp(bool inEventLoop)
/// Adds an which will be executed on shutdown of this instance.
///
/// The to run on shutdown.
- public void AddShutdownHook(Action action)
+ public void AddShutdownHook(Action action)
{
- if (this.InEventLoop)
+ if (this.InEventLoop)
{
this.shutdownHooks.Add(action);
- }
+ }
else
{
this.Execute(() => this.shutdownHooks.Add(action));
@@ -169,53 +187,53 @@ public void AddShutdownHook(Action action)
/// executed on shutdown of this instance.
///
/// The to remove.
- public void RemoveShutdownHook(Action action)
+ public void RemoveShutdownHook(Action action)
{
- if (this.InEventLoop)
+ if (this.InEventLoop)
{
this.shutdownHooks.Remove(action);
- }
+ }
else
{
this.Execute(() => this.shutdownHooks.Remove(action));
}
}
- bool RunShutdownHooks()
+ bool RunShutdownHooks()
{
bool ran = false;
-
+
// Note shutdown hooks can add / remove shutdown hooks.
- while (this.shutdownHooks.Count > 0)
+ while (this.shutdownHooks.Count > 0)
{
var copy = this.shutdownHooks.ToArray();
this.shutdownHooks.Clear();
for (var i = 0; i < copy.Length; i++)
{
- try
+ try
{
copy[i]();
- }
- catch (Exception ex)
+ }
+ catch (Exception ex)
{
Logger.Warn("Shutdown hook raised an exception.", ex);
- }
- finally
+ }
+ finally
{
ran = true;
}
}
}
- if (ran)
+ if (ran)
{
this.lastExecutionTime = PreciseTimeSpan.FromStart;
}
return ran;
}
-
+
///
public override Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan timeout)
@@ -398,6 +416,7 @@ protected bool RunAllTasks()
while (true)
{
+ Volatile.Write(ref this.progress, this.progress + 1); // volatile write is enough as this is the only thread ever writing
SafeExecute(task);
task = this.PollTask();
if (task == null)
diff --git a/src/DotNetty.Transport.Libuv/DispatcherEventLoop.cs b/src/DotNetty.Transport.Libuv/DispatcherEventLoop.cs
index d05550acf..788598694 100644
--- a/src/DotNetty.Transport.Libuv/DispatcherEventLoop.cs
+++ b/src/DotNetty.Transport.Libuv/DispatcherEventLoop.cs
@@ -4,6 +4,7 @@
namespace DotNetty.Transport.Libuv
{
using System;
+ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
@@ -69,5 +70,7 @@ internal void Dispatch(NativeHandle handle)
public Task RegisterAsync(IChannel channel) => channel.Unsafe.RegisterAsync(this);
public new IEventLoopGroup Parent => (IEventLoopGroup)base.Parent;
+
+ public new IEnumerable Items => new[] { this };
}
}
\ No newline at end of file
diff --git a/src/DotNetty.Transport.Libuv/DispatcherEventLoopGroup.cs b/src/DotNetty.Transport.Libuv/DispatcherEventLoopGroup.cs
index 646ad6b45..3b9010096 100644
--- a/src/DotNetty.Transport.Libuv/DispatcherEventLoopGroup.cs
+++ b/src/DotNetty.Transport.Libuv/DispatcherEventLoopGroup.cs
@@ -6,6 +6,7 @@
namespace DotNetty.Transport.Libuv
{
using System;
+ using System.Collections.Generic;
using System.Threading.Tasks;
using DotNetty.Common.Concurrency;
using DotNetty.Transport.Channels;
@@ -29,6 +30,10 @@ public DispatcherEventLoopGroup()
internal DispatcherEventLoop Dispatcher => this.dispatcherEventLoop;
+ protected override IEnumerable GetItems() => new[] { this.dispatcherEventLoop };
+
+ public new IEnumerable Items => new[] { this.dispatcherEventLoop };
+
IEventLoop IEventLoopGroup.GetNext() => (IEventLoop)this.GetNext();
public override IEventExecutor GetNext() => this.dispatcherEventLoop;
diff --git a/src/DotNetty.Transport.Libuv/EventLoop.cs b/src/DotNetty.Transport.Libuv/EventLoop.cs
index f3cade01d..fcced02e8 100644
--- a/src/DotNetty.Transport.Libuv/EventLoop.cs
+++ b/src/DotNetty.Transport.Libuv/EventLoop.cs
@@ -3,6 +3,7 @@
namespace DotNetty.Transport.Libuv
{
+ using System.Collections.Generic;
using System.Threading.Tasks;
using DotNetty.Transport.Channels;
@@ -19,5 +20,7 @@ public EventLoop(IEventLoopGroup parent, string threadName)
public Task RegisterAsync(IChannel channel) => channel.Unsafe.RegisterAsync(this);
public new IEventLoopGroup Parent => (IEventLoopGroup)base.Parent;
+
+ public new IEnumerable Items => new[] { this };
}
}
\ No newline at end of file
diff --git a/src/DotNetty.Transport.Libuv/EventLoopGroup.cs b/src/DotNetty.Transport.Libuv/EventLoopGroup.cs
index 1df143da1..d58e8bded 100644
--- a/src/DotNetty.Transport.Libuv/EventLoopGroup.cs
+++ b/src/DotNetty.Transport.Libuv/EventLoopGroup.cs
@@ -6,6 +6,7 @@
namespace DotNetty.Transport.Libuv
{
using System;
+ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -27,6 +28,8 @@ public sealed class EventLoopGroup : AbstractEventExecutorGroup, IEventLoopGroup
public override Task TerminationCompletion { get; }
+ public new IEnumerable Items => this.eventLoops;
+
public EventLoopGroup()
: this(DefaultEventLoopCount)
{
@@ -119,5 +122,7 @@ public override Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan time
}
return this.TerminationCompletion;
}
+
+ protected override IEnumerable GetItems() => this.eventLoops;
}
}
\ No newline at end of file
diff --git a/src/DotNetty.Transport.Libuv/LoopExecutor.cs b/src/DotNetty.Transport.Libuv/LoopExecutor.cs
index 6f4c0f410..373624c37 100644
--- a/src/DotNetty.Transport.Libuv/LoopExecutor.cs
+++ b/src/DotNetty.Transport.Libuv/LoopExecutor.cs
@@ -7,6 +7,7 @@
namespace DotNetty.Transport.Libuv
{
using System;
+ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Threading.Tasks;
@@ -17,7 +18,6 @@ namespace DotNetty.Transport.Libuv
using System.Threading;
using DotNetty.Common;
using DotNetty.Transport.Libuv.Native;
-
using Timer = Native.Timer;
class LoopExecutor : AbstractScheduledEventExecutor
@@ -297,7 +297,7 @@ void RunAllTasks(long timeout)
long runTasks = 0;
long executionTime;
this.wakeUp = false;
- for (;;)
+ for (; ; )
{
SafeExecute(task);
@@ -402,7 +402,7 @@ static bool RunAllTasksFrom(IQueue taskQueue)
{
return false;
}
- for (;;)
+ for (; ; )
{
SafeExecute(task);
task = PollTaskFrom(taskQueue);
@@ -488,7 +488,7 @@ public override Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan time
bool inEventLoop = this.InEventLoop;
bool wakeUpLoop;
int oldState;
- for (;;)
+ for (; ; )
{
if (this.IsShuttingDown)
{
@@ -540,5 +540,7 @@ public override Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan time
return this.TerminationCompletion;
}
+
+ protected override IEnumerable GetItems() => new[] { this };
}
}
diff --git a/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs b/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs
index df0f8b408..214818460 100644
--- a/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs
+++ b/src/DotNetty.Transport.Libuv/WorkerEventLoop.cs
@@ -7,6 +7,7 @@
namespace DotNetty.Transport.Libuv
{
using System;
+ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Threading.Tasks;
@@ -112,6 +113,8 @@ void OnRead(Pipe handle, int status)
public new IEventLoopGroup Parent => (IEventLoopGroup)base.Parent;
+ IEnumerable IEventLoopGroup.Items => new[] { this };
+
sealed class PipeConnect : ConnectRequest
{
const int MaximumRetryCount = 10;
diff --git a/src/DotNetty.Transport.Libuv/WorkerEventLoopGroup.cs b/src/DotNetty.Transport.Libuv/WorkerEventLoopGroup.cs
index 87ac1c1df..a8986ca17 100644
--- a/src/DotNetty.Transport.Libuv/WorkerEventLoopGroup.cs
+++ b/src/DotNetty.Transport.Libuv/WorkerEventLoopGroup.cs
@@ -6,6 +6,7 @@
namespace DotNetty.Transport.Libuv
{
using System;
+ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
@@ -84,6 +85,8 @@ public WorkerEventLoopGroup(DispatcherEventLoopGroup eventLoopGroup, int eventLo
internal string PipeName { get; }
+ IEnumerable IEventLoopGroup.Items => this.eventLoops;
+
internal void Accept(NativeHandle handle)
{
Debug.Assert(this.dispatcherLoop != null);
@@ -126,5 +129,7 @@ public override Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan time
}
return this.TerminationCompletion;
}
+
+ protected override IEnumerable GetItems() => this.eventLoops;
}
}
\ No newline at end of file
diff --git a/src/DotNetty.Transport/Channels/AffinitizedEventLoopGroup.cs b/src/DotNetty.Transport/Channels/AffinitizedEventLoopGroup.cs
index 25d487f3b..fa72871f2 100644
--- a/src/DotNetty.Transport/Channels/AffinitizedEventLoopGroup.cs
+++ b/src/DotNetty.Transport/Channels/AffinitizedEventLoopGroup.cs
@@ -4,6 +4,7 @@
namespace DotNetty.Transport.Channels
{
using System;
+ using System.Collections.Generic;
using System.Threading.Tasks;
using DotNetty.Common.Concurrency;
@@ -23,6 +24,10 @@ public class AffinitizedEventLoopGroup : AbstractEventExecutorGroup, IEventLoopG
///
public override Task TerminationCompletion => this.innerGroup.TerminationCompletion;
+ protected override IEnumerable GetItems() => this.innerGroup.Items;
+
+ public new IEnumerable Items => ((IEventLoopGroup)this.innerGroup).Items;
+
///
/// Creates a new instance of .
///
diff --git a/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs b/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs
index 9419861e5..6e5b0f557 100644
--- a/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs
+++ b/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs
@@ -28,6 +28,10 @@ sealed class EmbeddedEventLoop : AbstractScheduledEventExecutor, IEventLoop
public new IEventLoopGroup Parent => (IEventLoopGroup)base.Parent;
+ protected override IEnumerable GetItems() => new[] { this };
+
+ public new IEnumerable Items => new[] { this };
+
public override bool IsInEventLoop(Thread thread) => true;
public override void Execute(IRunnable command)
@@ -48,7 +52,7 @@ public override Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan time
internal void RunTasks()
{
- for (;;)
+ for (; ; )
{
// have to perform an additional check since Queue throws upon empty dequeue in .NET
if (this.tasks.Count == 0)
@@ -67,7 +71,7 @@ internal void RunTasks()
internal PreciseTimeSpan RunScheduledTasks()
{
PreciseTimeSpan time = GetNanos();
- for (;;)
+ for (; ; )
{
IRunnable task = this.PollScheduledTask(time);
if (task == null)
diff --git a/src/DotNetty.Transport/Channels/IEventLoopGroup.cs b/src/DotNetty.Transport/Channels/IEventLoopGroup.cs
index 43e8a2865..cdc5b99dd 100644
--- a/src/DotNetty.Transport/Channels/IEventLoopGroup.cs
+++ b/src/DotNetty.Transport/Channels/IEventLoopGroup.cs
@@ -3,6 +3,7 @@
namespace DotNetty.Transport.Channels
{
+ using System.Collections.Generic;
using System.Threading.Tasks;
using DotNetty.Common.Concurrency;
@@ -13,7 +14,12 @@ namespace DotNetty.Transport.Channels
public interface IEventLoopGroup : IEventExecutorGroup
{
///
- /// Returns .
+ /// Returns list of owned event loops.
+ ///
+ new IEnumerable Items { get; }
+
+ ///
+ /// Returns one of owned event loops.
///
new IEventLoop GetNext();
diff --git a/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs b/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs
index 4ef18c90d..24b9b6d5f 100644
--- a/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs
+++ b/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs
@@ -4,6 +4,7 @@
namespace DotNetty.Transport.Channels
{
using System;
+ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -29,6 +30,12 @@ public sealed class MultithreadEventLoopGroup : AbstractEventExecutorGroup, IEve
///
public override Task TerminationCompletion { get; }
+ ///
+ protected override IEnumerable GetItems() => this.eventLoops;
+
+ ///
+ public new IEnumerable Items => this.eventLoops;
+
/// Creates a new instance of .
public MultithreadEventLoopGroup()
: this(DefaultEventLoopFactory, DefaultEventLoopThreadCount)
diff --git a/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs b/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs
index 360b1527b..ddf2c25df 100644
--- a/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs
+++ b/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs
@@ -4,6 +4,7 @@
namespace DotNetty.Transport.Channels
{
using System;
+ using System.Collections.Generic;
using System.Threading.Tasks;
using DotNetty.Common.Concurrency;
using DotNetty.Common.Internal;
@@ -70,5 +71,7 @@ protected SingleThreadEventLoop(IEventLoopGroup parent, string threadName, TimeS
///
public new IEventLoopGroup Parent => (IEventLoopGroup)base.Parent;
+
+ public new IEnumerable Items => new[] { this };
}
}
\ No newline at end of file