diff --git a/src/DotNetty.Buffers/project.json b/src/DotNetty.Buffers/project.json index 34e0a9b..8189b3f 100644 --- a/src/DotNetty.Buffers/project.json +++ b/src/DotNetty.Buffers/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "DotNetty.Common": { diff --git a/src/DotNetty.Codecs.Mqtt/project.json b/src/DotNetty.Codecs.Mqtt/project.json index 07af2ed..d895229 100644 --- a/src/DotNetty.Codecs.Mqtt/project.json +++ b/src/DotNetty.Codecs.Mqtt/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "DotNetty.Common": { diff --git a/src/DotNetty.Codecs.Protobuf/project.json b/src/DotNetty.Codecs.Protobuf/project.json index 95d85e9..b8980bb 100644 --- a/src/DotNetty.Codecs.Protobuf/project.json +++ b/src/DotNetty.Codecs.Protobuf/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "DotNetty.Common": { diff --git a/src/DotNetty.Codecs.ProtocolBuffers/project.json b/src/DotNetty.Codecs.ProtocolBuffers/project.json index aceaa54..ccf75b3 100644 --- a/src/DotNetty.Codecs.ProtocolBuffers/project.json +++ b/src/DotNetty.Codecs.ProtocolBuffers/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "DotNetty.Common": { diff --git a/src/DotNetty.Codecs.Redis/project.json b/src/DotNetty.Codecs.Redis/project.json index 5965fa2..6e8e18d 100644 --- a/src/DotNetty.Codecs.Redis/project.json +++ b/src/DotNetty.Codecs.Redis/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "DotNetty.Common": { diff --git a/src/DotNetty.Codecs/project.json b/src/DotNetty.Codecs/project.json index 64d8b40..9dd451a 100644 --- a/src/DotNetty.Codecs/project.json +++ b/src/DotNetty.Codecs/project.json @@ -20,7 +20,8 @@ "buildOptions": { "allowUnsafe": true, "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "DotNetty.Common": { diff --git a/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs b/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs index 496277e..3288b62 100644 --- a/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs +++ b/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs @@ -6,81 +6,116 @@ namespace DotNetty.Common.Concurrency using System; using System.Threading; using System.Threading.Tasks; - using Thread = DotNetty.Common.Concurrency.XThread; + using Thread = XThread; /// /// Abstract base class for implementations /// public abstract class AbstractEventExecutor : IEventExecutor { - protected static readonly TimeSpan DefaultShutdownQuietPeriod = TimeSpan.FromSeconds(2); - protected static readonly TimeSpan DefaultShutdownTimeout = TimeSpan.FromSeconds(15); + static readonly TimeSpan DefaultShutdownQuietPeriod = TimeSpan.FromSeconds(2); + static readonly TimeSpan DefaultShutdownTimeout = TimeSpan.FromSeconds(15); - //TODO: support for EventExecutorGroup + /// Creates an instance of . + protected AbstractEventExecutor() + : this(null) + { + } + /// Creates an instance of . + protected AbstractEventExecutor(IEventExecutorGroup parent) + { + this.Parent = parent; + } + + /// public bool InEventLoop => this.IsInEventLoop(Thread.CurrentThread); + /// public abstract bool IsShuttingDown { get; } + /// public abstract Task TerminationCompletion { get; } + /// public abstract bool IsShutdown { get; } + /// public abstract bool IsTerminated { get; } + /// + public IEventExecutorGroup Parent { get; } + + /// public abstract bool IsInEventLoop(Thread thread); + /// public abstract void Execute(IRunnable task); + /// public void Execute(Action action, object state) => this.Execute(new StateActionTaskQueueNode(action, state)); + /// public void Execute(Action action, object context, object state) => this.Execute(new StateActionWithContextTaskQueueNode(action, context, state)); + /// public void Execute(Action action) => this.Execute(new ActionTaskQueueNode(action)); + /// public virtual IScheduledTask Schedule(IRunnable action, TimeSpan delay) { throw new NotSupportedException(); } + /// public virtual IScheduledTask Schedule(Action action, TimeSpan delay) { throw new NotSupportedException(); } + /// public virtual IScheduledTask Schedule(Action action, object state, TimeSpan delay) { throw new NotSupportedException(); } + /// public virtual IScheduledTask Schedule(Action action, object context, object state, TimeSpan delay) { throw new NotSupportedException(); } + /// public virtual Task ScheduleAsync(Action action, TimeSpan delay) => this.ScheduleAsync(action, delay, CancellationToken.None); + /// public virtual Task ScheduleAsync(Action action, object state, TimeSpan delay, CancellationToken cancellationToken) { throw new NotSupportedException(); } + /// public virtual Task ScheduleAsync(Action action, object state, TimeSpan delay) => this.ScheduleAsync(action, state, delay, CancellationToken.None); + /// public virtual Task ScheduleAsync(Action action, TimeSpan delay, CancellationToken cancellationToken) { throw new NotSupportedException(); } + /// public virtual Task ScheduleAsync(Action action, object context, object state, TimeSpan delay) => this.ScheduleAsync(action, context, state, delay, CancellationToken.None); + /// public virtual Task ScheduleAsync(Action action, object context, object state, TimeSpan delay, CancellationToken cancellationToken) { throw new NotSupportedException(); } + /// public Task SubmitAsync(Func func) => this.SubmitAsync(func, CancellationToken.None); + /// public Task SubmitAsync(Func func, CancellationToken cancellationToken) { var node = new FuncSubmitQueueNode(func, cancellationToken); @@ -88,8 +123,10 @@ namespace DotNetty.Common.Concurrency return node.Completion; } + /// public Task SubmitAsync(Func func, object state) => this.SubmitAsync(func, state, CancellationToken.None); + /// public Task SubmitAsync(Func func, object state, CancellationToken cancellationToken) { var node = new StateFuncSubmitQueueNode(func, state, cancellationToken); @@ -97,8 +134,10 @@ namespace DotNetty.Common.Concurrency return node.Completion; } + /// public Task SubmitAsync(Func func, object context, object state) => this.SubmitAsync(func, context, state, CancellationToken.None); + /// public Task SubmitAsync(Func func, object context, object state, CancellationToken cancellationToken) { var node = new StateFuncWithContextSubmitQueueNode(func, context, state, cancellationToken); @@ -106,10 +145,13 @@ namespace DotNetty.Common.Concurrency return node.Completion; } + /// public Task ShutdownGracefullyAsync() => this.ShutdownGracefullyAsync(DefaultShutdownQuietPeriod, DefaultShutdownTimeout); + /// public abstract Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan timeout); + /// protected void SetCurrentExecutor(IEventExecutor executor) => ExecutionEnvironment.SetCurrentExecutor(executor); #region Queuing data structures diff --git a/src/DotNetty.Common/Concurrency/AbstractScheduledEventExecutor.cs b/src/DotNetty.Common/Concurrency/AbstractScheduledEventExecutor.cs index bf2b69d..53fa542 100644 --- a/src/DotNetty.Common/Concurrency/AbstractScheduledEventExecutor.cs +++ b/src/DotNetty.Common/Concurrency/AbstractScheduledEventExecutor.cs @@ -17,7 +17,14 @@ namespace DotNetty.Common.Concurrency { protected readonly PriorityQueue ScheduledTaskQueue = new PriorityQueue(); - // TODO: support for EventExecutorGroup + protected AbstractScheduledEventExecutor() + { + } + + protected AbstractScheduledEventExecutor(IEventExecutorGroup parent) + : base(parent) + { + } protected static PreciseTimeSpan GetNanos() => PreciseTimeSpan.FromStart; diff --git a/src/DotNetty.Common/Concurrency/IEventExecutor.cs b/src/DotNetty.Common/Concurrency/IEventExecutor.cs index 8288518..e8bada1 100644 --- a/src/DotNetty.Common/Concurrency/IEventExecutor.cs +++ b/src/DotNetty.Common/Concurrency/IEventExecutor.cs @@ -43,6 +43,11 @@ namespace DotNetty.Common.Concurrency /// bool IsTerminated { get; } + /// + /// Parent . + /// + IEventExecutorGroup Parent { get; } + /// /// Returns true if the given belongs to this event loop, /// false> otherwise. diff --git a/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs b/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs index 41ecf99..3e2af24 100644 --- a/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs +++ b/src/DotNetty.Common/Concurrency/IEventExecutorGroup.cs @@ -6,14 +6,31 @@ namespace DotNetty.Common.Concurrency using System; using System.Threading.Tasks; + /// + /// Provides an access to a set of s it manages. + /// public interface IEventExecutorGroup { + /// + /// A for completion of termination. . + /// Task TerminationCompletion { get; } + /// + /// Returns . + /// IEventExecutor GetNext(); + /// + /// Terminates this and all its s. + /// + /// for completion of termination. Task ShutdownGracefullyAsync(); + /// + /// Terminates this and all its s. + /// + /// for completion of termination. Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan timeout); } } \ No newline at end of file diff --git a/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs b/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs index 2e42d1c..f090a3c 100644 --- a/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs +++ b/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs @@ -9,8 +9,11 @@ namespace DotNetty.Common.Concurrency using System.Threading.Tasks; using DotNetty.Common.Internal; using DotNetty.Common.Internal.Logging; - using Thread = DotNetty.Common.Concurrency.XThread; + using Thread = XThread; + /// + /// backed by a single thread. + /// public class SingleThreadEventExecutor : AbstractScheduledEventExecutor { #pragma warning disable 420 // referencing volatile fields is fine in Interlocked methods @@ -39,12 +42,24 @@ namespace DotNetty.Common.Concurrency PreciseTimeSpan gracefulShutdownQuietPeriod; PreciseTimeSpan gracefulShutdownTimeout; + /// Creates a new instance of . public SingleThreadEventExecutor(string threadName, TimeSpan breakoutInterval) - : this(threadName, breakoutInterval, new CompatibleConcurrentQueue()) + : this(null, threadName, breakoutInterval, new CompatibleConcurrentQueue()) + { + } + + /// Creates a new instance of . + public SingleThreadEventExecutor(IEventExecutorGroup parent, string threadName, TimeSpan breakoutInterval) + : this(parent, threadName, breakoutInterval, new CompatibleConcurrentQueue()) { } protected SingleThreadEventExecutor(string threadName, TimeSpan breakoutInterval, IQueue taskQueue) + : this(null, threadName, breakoutInterval, taskQueue) + { } + + protected SingleThreadEventExecutor(IEventExecutorGroup parent, string threadName, TimeSpan breakoutInterval, IQueue taskQueue) + : base(parent) { this.terminationCompletionSource = new TaskCompletionSource(); this.taskQueue = taskQueue; @@ -86,16 +101,22 @@ namespace DotNetty.Common.Concurrency this.scheduler); } + /// public override bool IsShuttingDown => this.executionState >= ST_SHUTTING_DOWN; + /// public override Task TerminationCompletion => this.terminationCompletionSource.Task; + /// public override bool IsShutdown => this.executionState >= ST_SHUTDOWN; + /// public override bool IsTerminated => this.executionState == ST_TERMINATED; + /// public override bool IsInEventLoop(Thread t) => this.thread == t; + /// public override void Execute(IRunnable task) { this.taskQueue.TryEnqueue(task); @@ -114,6 +135,7 @@ namespace DotNetty.Common.Concurrency } } + /// public override Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan timeout) { Contract.Requires(quietPeriod >= TimeSpan.Zero); diff --git a/src/DotNetty.Common/project.json b/src/DotNetty.Common/project.json index 9d51a2e..97d7ba3 100644 --- a/src/DotNetty.Common/project.json +++ b/src/DotNetty.Common/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "Microsoft.Extensions.Logging": "1.0.0" diff --git a/src/DotNetty.Handlers/project.json b/src/DotNetty.Handlers/project.json index 7be7a34..7fc448a 100644 --- a/src/DotNetty.Handlers/project.json +++ b/src/DotNetty.Handlers/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": [ "CS1591" ] }, "dependencies": { "DotNetty.Common": { diff --git a/src/DotNetty.Transport/Channels/AffinitizedEventLoopGroup.cs b/src/DotNetty.Transport/Channels/AffinitizedEventLoopGroup.cs new file mode 100644 index 0000000..4f50fa0 --- /dev/null +++ b/src/DotNetty.Transport/Channels/AffinitizedEventLoopGroup.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace DotNetty.Transport.Channels +{ + using System; + using System.Threading.Tasks; + using DotNetty.Common.Concurrency; + + /// + /// that works as a wrapper for another providing affinity on call. + /// + public class AffinitizedEventLoopGroup : IEventLoopGroup + { + readonly IEventLoopGroup innerGroup; + + /// + /// Creates a new instance of . + /// + /// serving as an actual provider of s. + public AffinitizedEventLoopGroup(IEventLoopGroup innerGroup) + { + this.innerGroup = innerGroup; + } + + /// + public Task TerminationCompletion => this.innerGroup.TerminationCompletion; + + IEventExecutor IEventExecutorGroup.GetNext() => this.GetNext(); + + /// + /// If running in a context of an existing , this is returned. + /// Otherwise, is retrieved from underlying . + /// + public IEventLoop GetNext() + { + IEventExecutor executor; + if (ExecutionEnvironment.TryGetCurrentExecutor(out executor)) + { + var loop = executor as IEventLoop; + if (loop != null && loop.Parent == this.innerGroup) + { + return loop; + } + } + return this.innerGroup.GetNext(); + } + + /// + public Task ShutdownGracefullyAsync() => this.innerGroup.ShutdownGracefullyAsync(); + + /// + public Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan timeout) => this.innerGroup.ShutdownGracefullyAsync(quietPeriod, timeout); + } +} \ No newline at end of file diff --git a/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs b/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs index f8e8f61..f604acb 100644 --- a/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs +++ b/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs @@ -5,7 +5,6 @@ namespace DotNetty.Transport.Channels.Embedded { using System; using System.Collections.Generic; - using System.Threading; using System.Threading.Tasks; using DotNetty.Common; using DotNetty.Common.Concurrency; @@ -30,6 +29,8 @@ namespace DotNetty.Transport.Channels.Embedded public override bool IsTerminated => false; + public new IEventLoopGroup Parent => (IEventLoopGroup)base.Parent; + public override bool IsInEventLoop(Thread thread) => true; public override void Execute(IRunnable command) diff --git a/src/DotNetty.Transport/Channels/IEventLoop.cs b/src/DotNetty.Transport/Channels/IEventLoop.cs index 779de99..4e2569b 100644 --- a/src/DotNetty.Transport/Channels/IEventLoop.cs +++ b/src/DotNetty.Transport/Channels/IEventLoop.cs @@ -6,8 +6,21 @@ namespace DotNetty.Transport.Channels using System.Threading.Tasks; using DotNetty.Common.Concurrency; + /// + /// specialized to handle I/O operations of assigned s. + /// public interface IEventLoop : IEventExecutor { + /// + /// Parent . + /// + new IEventLoopGroup Parent { get; } + + /// + /// Registers provided with this . + /// + /// to register. + /// for completion of registration. Task RegisterAsync(IChannel channel); } } \ No newline at end of file diff --git a/src/DotNetty.Transport/Channels/IEventLoopGroup.cs b/src/DotNetty.Transport/Channels/IEventLoopGroup.cs index bec742a..7f51038 100644 --- a/src/DotNetty.Transport/Channels/IEventLoopGroup.cs +++ b/src/DotNetty.Transport/Channels/IEventLoopGroup.cs @@ -5,8 +5,14 @@ namespace DotNetty.Transport.Channels { using DotNetty.Common.Concurrency; + /// + /// specialized for handling s. + /// public interface IEventLoopGroup : IEventExecutorGroup { + /// + /// Returns . + /// new IEventLoop GetNext(); } } \ No newline at end of file diff --git a/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs b/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs index 75c3909..21df3c5 100644 --- a/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs +++ b/src/DotNetty.Transport/Channels/MultithreadEventLoopGroup.cs @@ -9,30 +9,37 @@ namespace DotNetty.Transport.Channels using System.Threading.Tasks; using DotNetty.Common.Concurrency; + /// + /// backed by a set of instances. + /// public sealed class MultithreadEventLoopGroup : IEventLoopGroup { static readonly int DefaultEventLoopThreadCount = Environment.ProcessorCount * 2; - static readonly Func DefaultEventLoopFactory = () => new SingleThreadEventLoop(); + static readonly Func DefaultEventLoopFactory = group => new SingleThreadEventLoop(group); readonly IEventLoop[] eventLoops; int requestId; + /// Creates a new instance of . public MultithreadEventLoopGroup() : this(DefaultEventLoopFactory, DefaultEventLoopThreadCount) { } + /// Creates a new instance of . public MultithreadEventLoopGroup(int eventLoopCount) : this(DefaultEventLoopFactory, eventLoopCount) { } - public MultithreadEventLoopGroup(Func eventLoopFactory) + /// Creates a new instance of . + public MultithreadEventLoopGroup(Func eventLoopFactory) : this(eventLoopFactory, DefaultEventLoopThreadCount) { } - public MultithreadEventLoopGroup(Func eventLoopFactory, int eventLoopCount) + /// Creates a new instance of . + public MultithreadEventLoopGroup(Func eventLoopFactory, int eventLoopCount) { this.eventLoops = new IEventLoop[eventLoopCount]; var terminationTasks = new Task[eventLoopCount]; @@ -42,7 +49,7 @@ namespace DotNetty.Transport.Channels bool success = false; try { - eventLoop = eventLoopFactory(); + eventLoop = eventLoopFactory(this); success = true; } catch (Exception ex) @@ -54,8 +61,8 @@ namespace DotNetty.Transport.Channels if (!success) { Task.WhenAll(this.eventLoops - .Take(i) - .Select(loop => loop.ShutdownGracefullyAsync())) + .Take(i) + .Select(loop => loop.ShutdownGracefullyAsync())) .Wait(); } } @@ -66,16 +73,20 @@ namespace DotNetty.Transport.Channels this.TerminationCompletion = Task.WhenAll(terminationTasks); } + /// public Task TerminationCompletion { get; } + /// public IEventLoop GetNext() { int id = Interlocked.Increment(ref this.requestId); return this.eventLoops[Math.Abs(id % this.eventLoops.Length)]; } + /// IEventExecutor IEventExecutorGroup.GetNext() => this.GetNext(); + /// public Task ShutdownGracefullyAsync() { foreach (IEventLoop eventLoop in this.eventLoops) @@ -85,6 +96,7 @@ namespace DotNetty.Transport.Channels return this.TerminationCompletion; } + /// public Task ShutdownGracefullyAsync(TimeSpan quietPeriod, TimeSpan timeout) { foreach (IEventLoop eventLoop in this.eventLoops) diff --git a/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs b/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs index 3905231..dfa41df 100644 --- a/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs +++ b/src/DotNetty.Transport/Channels/SingleThreadEventLoop.cs @@ -8,30 +8,65 @@ namespace DotNetty.Transport.Channels using DotNetty.Common.Concurrency; using DotNetty.Common.Internal; + /// + /// implementation based on . + /// public class SingleThreadEventLoop : SingleThreadEventExecutor, IEventLoop { static readonly TimeSpan DefaultBreakoutInterval = TimeSpan.FromMilliseconds(100); + /// Creates a new instance of . public SingleThreadEventLoop() : this(null, DefaultBreakoutInterval) { } + /// Creates a new instance of . public SingleThreadEventLoop(string threadName) : this(threadName, DefaultBreakoutInterval) { } + /// Creates a new instance of . public SingleThreadEventLoop(string threadName, TimeSpan breakoutInterval) : base(threadName, breakoutInterval) { } - protected SingleThreadEventLoop(string threadName, TimeSpan breakoutInterval, IQueue taskQueue) - : base(threadName, breakoutInterval, taskQueue) + /// Creates a new instance of . + public SingleThreadEventLoop(IEventLoopGroup parent) + : this(parent, null, DefaultBreakoutInterval) { } + /// Creates a new instance of . + public SingleThreadEventLoop(IEventLoopGroup parent, string threadName) + : this(parent, threadName, DefaultBreakoutInterval) + { + } + + /// Creates a new instance of . + public SingleThreadEventLoop(IEventLoopGroup parent, string threadName, TimeSpan breakoutInterval) + : base(parent, threadName, breakoutInterval) + { + } + + /// Creates a new instance of . + protected SingleThreadEventLoop(string threadName, TimeSpan breakoutInterval, IQueue taskQueue) + : base(null, threadName, breakoutInterval, taskQueue) + { + } + + /// Creates a new instance of . + protected SingleThreadEventLoop(IEventLoopGroup parent, string threadName, TimeSpan breakoutInterval, IQueue taskQueue) + : base(parent, threadName, breakoutInterval, taskQueue) + { + } + + /// public Task RegisterAsync(IChannel channel) => channel.Unsafe.RegisterAsync(this); + + /// + public new IEventLoopGroup Parent => (IEventLoopGroup)base.Parent; } } \ No newline at end of file diff --git a/src/DotNetty.Transport/project.json b/src/DotNetty.Transport/project.json index f86f868..3fadf8d 100644 --- a/src/DotNetty.Transport/project.json +++ b/src/DotNetty.Transport/project.json @@ -19,7 +19,8 @@ }, "buildOptions": { "keyFile": "../../DotNetty.snk", - "xmlDoc": true + "xmlDoc": true, + "nowarn": ["CS1591"] }, "dependencies": { "DotNetty.Common": {