diff --git a/src/DotNetty.Buffers/PoolThreadCache.cs b/src/DotNetty.Buffers/PoolThreadCache.cs index 5c8e1cc3d..5d2fd6cf4 100644 --- a/src/DotNetty.Buffers/PoolThreadCache.cs +++ b/src/DotNetty.Buffers/PoolThreadCache.cs @@ -6,10 +6,10 @@ namespace DotNetty.Buffers using System; using System.Diagnostics; using System.Diagnostics.Contracts; + using System.Threading; using DotNetty.Common; using DotNetty.Common.Internal; using DotNetty.Common.Internal.Logging; - using Thread = DotNetty.Common.Concurrency.XThread; /// /// Acts a Thread cache for allocations. This implementation is moduled after diff --git a/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs b/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs index 36a2ddd2e..29d3cdaa2 100644 --- a/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs +++ b/src/DotNetty.Common/Concurrency/AbstractEventExecutor.cs @@ -8,7 +8,6 @@ namespace DotNetty.Common.Concurrency using System.Threading; using System.Threading.Tasks; using DotNetty.Common.Internal.Logging; - using Thread = XThread; /// /// Abstract base class for implementations diff --git a/src/DotNetty.Common/Concurrency/IEventExecutor.cs b/src/DotNetty.Common/Concurrency/IEventExecutor.cs index 04fd893e2..185340f32 100644 --- a/src/DotNetty.Common/Concurrency/IEventExecutor.cs +++ b/src/DotNetty.Common/Concurrency/IEventExecutor.cs @@ -3,8 +3,8 @@ namespace DotNetty.Common.Concurrency { - using Thread = DotNetty.Common.Concurrency.XThread; - + using System.Threading; + public interface IEventExecutor : IEventExecutorGroup { /// diff --git a/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs b/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs index 225871962..4e9758fab 100644 --- a/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs +++ b/src/DotNetty.Common/Concurrency/SingleThreadEventExecutor.cs @@ -12,7 +12,6 @@ namespace DotNetty.Common.Concurrency using System.Threading.Tasks; using DotNetty.Common.Internal; using DotNetty.Common.Internal.Logging; - using Thread = XThread; /// /// backed by a single thread. diff --git a/src/DotNetty.Common/Concurrency/XThread.cs b/src/DotNetty.Common/Concurrency/XThread.cs deleted file mode 100644 index ec18d7122..000000000 --- a/src/DotNetty.Common/Concurrency/XThread.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace DotNetty.Common.Concurrency -{ - using System; - using System.Diagnostics; - using System.Threading; - using System.Threading.Tasks; - - public delegate void XParameterizedThreadStart(object obj); - - [DebuggerDisplay("ID={threadId}, Name={Name}, IsExplicit={isExplicit}")] - public sealed class XThread - { - static int maxThreadId; - - [ThreadStatic] - static XThread currentThread; - - readonly int threadId; -#pragma warning disable CS0414 - readonly bool isExplicit; // For debugging only -#pragma warning restore CS0414 - Task task; - readonly EventWaitHandle completed = new EventWaitHandle(false, EventResetMode.AutoReset); - readonly EventWaitHandle readyToStart = new EventWaitHandle(false, EventResetMode.AutoReset); - object startupParameter; - - static int GetNewThreadId() => Interlocked.Increment(ref maxThreadId); - - XThread() - { - this.threadId = GetNewThreadId(); - this.isExplicit = false; - this.IsAlive = false; - } - - public XThread(Action action) - { - this.threadId = GetNewThreadId(); - this.isExplicit = true; - this.IsAlive = false; - this.CreateLongRunningTask(x => action()); - } - - public XThread(XParameterizedThreadStart threadStartFunc) - { - this.threadId = GetNewThreadId(); - this.isExplicit = true; - this.IsAlive = false; - this.CreateLongRunningTask(threadStartFunc); - } - - public void Start() - { - this.readyToStart.Set(); - this.IsAlive = true; - } - - void CreateLongRunningTask(XParameterizedThreadStart threadStartFunc) - { - this.task = Task.Factory.StartNew( - () => - { - // We start the task running, then unleash it by signaling the readyToStart event. - // This is needed to avoid thread reuse for tasks (see below) - this.readyToStart.WaitOne(); - // This is the first time we're using this thread, therefore the TLS slot must be empty - if (currentThread != null) - { - Debug.WriteLine("warning: currentThread already created; OS thread reused"); - Debug.Assert(false); - } - currentThread = this; - threadStartFunc(this.startupParameter); - this.completed.Set(); - }, - CancellationToken.None, - // .NET always creates a brand new thread for LongRunning tasks - // This is not documented but unlikely to ever change: - // https://github.com/dotnet/corefx/issues/2576#issuecomment-126693306 - TaskCreationOptions.LongRunning, - TaskScheduler.Default); - } - - public void Start(object parameter) - { - this.startupParameter = parameter; - this.Start(); - } - - public static void Sleep(int millisecondsTimeout) => Task.Delay(millisecondsTimeout).Wait(); - - public int Id => this.threadId; - - public string Name { get; set; } - - public bool IsAlive { get; private set; } - - public static XThread CurrentThread => currentThread ?? (currentThread = new XThread()); - - public bool Join(TimeSpan timeout) => this.completed.WaitOne(timeout); - - public bool Join(int millisecondsTimeout) => this.completed.WaitOne(millisecondsTimeout); - } -} \ No newline at end of file diff --git a/src/DotNetty.Common/ThreadDeathWatcher.cs b/src/DotNetty.Common/ThreadDeathWatcher.cs index c824c7e47..026decfb7 100644 --- a/src/DotNetty.Common/ThreadDeathWatcher.cs +++ b/src/DotNetty.Common/ThreadDeathWatcher.cs @@ -10,7 +10,6 @@ namespace DotNetty.Common using DotNetty.Common.Concurrency; using DotNetty.Common.Internal; using DotNetty.Common.Internal.Logging; - using Thread = DotNetty.Common.Concurrency.XThread; public static class ThreadDeathWatcher { diff --git a/src/DotNetty.Common/ThreadLocalPool.cs b/src/DotNetty.Common/ThreadLocalPool.cs index cbcc28290..327cb15e9 100644 --- a/src/DotNetty.Common/ThreadLocalPool.cs +++ b/src/DotNetty.Common/ThreadLocalPool.cs @@ -10,7 +10,6 @@ namespace DotNetty.Common using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; using System.Threading; - using Thread = DotNetty.Common.Concurrency.XThread; public class ThreadLocalPool { diff --git a/src/DotNetty.Common/Utilities/HashedWheelTimer.cs b/src/DotNetty.Common/Utilities/HashedWheelTimer.cs index af96b56c6..1ccd1c23b 100644 --- a/src/DotNetty.Common/Utilities/HashedWheelTimer.cs +++ b/src/DotNetty.Common/Utilities/HashedWheelTimer.cs @@ -27,7 +27,7 @@ public sealed class HashedWheelTimer : ITimer const int InstanceCountLimit = 64; readonly Worker worker; - readonly XThread workerThread; + readonly Thread workerThread; readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); const int WorkerStateInit = 0; @@ -98,7 +98,7 @@ public HashedWheelTimer( tickInterval, long.MaxValue / this.wheel.Length)); } - this.workerThread = new XThread(st => this.worker.Run()); + this.workerThread = new Thread(st => this.worker.Run()); this.maxPendingTimeouts = maxPendingTimeouts; @@ -187,7 +187,7 @@ public async Task> StopAsync() { GC.SuppressFinalize(this); - if (XThread.CurrentThread == this.workerThread) + if (Thread.CurrentThread == this.workerThread) { throw new InvalidOperationException($"{nameof(HashedWheelTimer)}.stop() cannot be called from timer task."); } diff --git a/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs b/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs index 86bf433a1..2f453a0e6 100644 --- a/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs +++ b/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs @@ -6,7 +6,6 @@ namespace DotNetty.Common.Utilities using System; using System.Threading; using DotNetty.Common.Internal.Logging; - using Thread = DotNetty.Common.Concurrency.XThread; public static class ReferenceCountUtil { diff --git a/src/DotNetty.Common/Utilities/ThreadExtensions.cs b/src/DotNetty.Common/Utilities/ThreadExtensions.cs index 7f9431a74..d39dcf237 100644 --- a/src/DotNetty.Common/Utilities/ThreadExtensions.cs +++ b/src/DotNetty.Common/Utilities/ThreadExtensions.cs @@ -6,7 +6,6 @@ namespace DotNetty.Common.Utilities using System; using System.Diagnostics.Contracts; using System.Threading; - using Thread = DotNetty.Common.Concurrency.XThread; public static class ThreadExtensions { diff --git a/src/DotNetty.Transport.Libuv/EventLoopGroup.cs b/src/DotNetty.Transport.Libuv/EventLoopGroup.cs index d58e8bded..c82f3c670 100644 --- a/src/DotNetty.Transport.Libuv/EventLoopGroup.cs +++ b/src/DotNetty.Transport.Libuv/EventLoopGroup.cs @@ -74,7 +74,7 @@ public EventLoopGroup(int eventLoopCount) public override IEventExecutor GetNext() { // Attempt to select event loop based on thread first - int threadId = XThread.CurrentThread.Id; + int threadId = Thread.CurrentThread.ManagedThreadId; int i; for (i = 0; i < this.eventLoops.Length; i++) { diff --git a/src/DotNetty.Transport.Libuv/LoopExecutor.cs b/src/DotNetty.Transport.Libuv/LoopExecutor.cs index 373624c37..3eef653e8 100644 --- a/src/DotNetty.Transport.Libuv/LoopExecutor.cs +++ b/src/DotNetty.Transport.Libuv/LoopExecutor.cs @@ -36,7 +36,7 @@ class LoopExecutor : AbstractScheduledEventExecutor readonly ThreadLocalPool writeRequestPool = new ThreadLocalPool(handle => new WriteRequest(handle)); readonly long preciseBreakoutInterval; readonly IQueue taskQueue; - readonly XThread thread; + readonly Thread thread; readonly TaskScheduler scheduler; readonly ManualResetEventSlim loopRunStart; readonly TaskCompletionSource terminationCompletionSource; @@ -80,7 +80,7 @@ public LoopExecutor(IEventLoopGroup parent, string threadName, TimeSpan breakout { name = $"{name}({threadName})"; } - this.thread = new XThread(Run) { Name = name }; + this.thread = new Thread(Run) { Name = name }; this.loopRunStart = new ManualResetEventSlim(false, 1); } @@ -97,7 +97,7 @@ protected void Start() internal Loop UnsafeLoop => this.loop; - internal int LoopThreadId => this.thread.Id; + internal int LoopThreadId => this.thread.ManagedThreadId; static void Run(object state) { @@ -424,7 +424,7 @@ static IRunnable PollTaskFrom(IQueue taskQueue) => public override bool IsTerminated => this.executionState == TerminatedState; - public override bool IsInEventLoop(XThread t) => this.thread == t; + public override bool IsInEventLoop(Thread t) => this.thread == t; void WakeUp(bool inEventLoop) { diff --git a/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs b/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs index 1531f5676..1390b7a01 100644 --- a/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs +++ b/src/DotNetty.Transport/Channels/DefaultChannelPipeline.cs @@ -16,7 +16,6 @@ namespace DotNetty.Transport.Channels using DotNetty.Common.Concurrency; using DotNetty.Common.Internal.Logging; using DotNetty.Common.Utilities; - using Thread = DotNetty.Common.Concurrency.XThread; public class DefaultChannelPipeline : IChannelPipeline { diff --git a/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs b/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs index 6e5b0f557..8f2fa3b1f 100644 --- a/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs +++ b/src/DotNetty.Transport/Channels/Embedded/EmbeddedEventLoop.cs @@ -5,10 +5,10 @@ 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; - using Thread = DotNetty.Common.Concurrency.XThread; sealed class EmbeddedEventLoop : AbstractScheduledEventExecutor, IEventLoop { diff --git a/test/DotNetty.Common.Tests/ThreadLocalPoolTest.cs b/test/DotNetty.Common.Tests/ThreadLocalPoolTest.cs index ace0d40bb..a0ebeb182 100644 --- a/test/DotNetty.Common.Tests/ThreadLocalPoolTest.cs +++ b/test/DotNetty.Common.Tests/ThreadLocalPoolTest.cs @@ -23,14 +23,13 @@ public void ThreadCanBeCollectedEvenIfHandledObjectIsReferencedTest() ThreadLocalPool pool = NewPool(1024); HandledObject reference = null; WeakReference threadRef = null; - WeakReference xThreadRef = null; var thread1 = new Thread(() => { //Don't know the reason, but thread2 will not be collected without wrapped with thread1 var thread2 = new Thread(() => { - Volatile.Write(ref xThreadRef, new WeakReference(XThread.CurrentThread)); + Volatile.Write(ref threadRef, new WeakReference(Thread.CurrentThread)); HandledObject data = pool.Take(); // Store a reference to the HandledObject to ensure it is not collected when the run method finish. Volatile.Write(ref reference, data); @@ -39,7 +38,6 @@ public void ThreadCanBeCollectedEvenIfHandledObjectIsReferencedTest() thread2.Start(); thread2.Join(); Assert.True(Volatile.Read(ref threadRef)?.TryGetTarget(out _)); - Assert.True(Volatile.Read(ref xThreadRef)?.TryGetTarget(out _)); GC.KeepAlive(thread2); // Null out so it can be collected. @@ -53,12 +51,11 @@ public void ThreadCanBeCollectedEvenIfHandledObjectIsReferencedTest() GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); GC.WaitForPendingFinalizers(); - if (Volatile.Read(ref threadRef)?.TryGetTarget(out _) == true || Volatile.Read(ref xThreadRef)?.TryGetTarget(out _) == true) + if (Volatile.Read(ref threadRef)?.TryGetTarget(out _) == true || Volatile.Read(ref threadRef)?.TryGetTarget(out _) == true) Thread.Sleep(100); } Assert.False(Volatile.Read(ref threadRef)?.TryGetTarget(out _)); - Assert.False(Volatile.Read(ref xThreadRef)?.TryGetTarget(out _)); // Now call recycle after the Thread was collected to ensure this still works... reference.Release();