From 70874488d0b71ae376c302827288f8c4806c9b26 Mon Sep 17 00:00:00 2001 From: Anton Lapounov Date: Thu, 19 Jan 2017 14:11:48 -0800 Subject: Use native thread id in the Lock class so it may be implemented as a compiler intrinsic on Windows. CR: SergeyK, EJan [tfs-changeset: 1645128] --- .../src/Interop/Windows/Interop.Libraries.cs | 1 + .../Windows/mincore/Interop.GetCurrentThreadId.cs | 14 ++++++++++++++ .../src/Interop/Interop.manual.cs | 3 --- .../src/System.Private.CoreLib.csproj | 3 +++ .../src/System/Environment.Unix.cs | 2 ++ .../src/System/Environment.Windows.cs | 2 ++ .../src/System/Threading/Lock.cs | 22 ++++++++++++---------- 7 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs diff --git a/src/Common/src/Interop/Windows/Interop.Libraries.cs b/src/Common/src/Interop/Windows/Interop.Libraries.cs index 6740c2cb2..0c1679ed2 100644 --- a/src/Common/src/Interop/Windows/Interop.Libraries.cs +++ b/src/Common/src/Interop/Windows/Interop.Libraries.cs @@ -15,6 +15,7 @@ internal static partial class Interop internal const string IO = "api-ms-win-core-io-l1-1-0.dll"; internal const string Memory = "api-ms-win-core-memory-l1-1-0.dll"; internal const string ProcessEnvironment = "api-ms-win-core-processenvironment-l1-1-0.dll"; + internal const string ProcessThreads = "api-ms-win-core-processthreads-l1-1-0.dll"; internal const string RealTime = "api-ms-win-core-realtime-l1-1-0.dll"; internal const string SysInfo = "api-ms-win-core-sysinfo-l1-2-0.dll"; internal const string Kernel32 = "api-ms-win-core-kernel32-legacy-l1-1-0.dll"; diff --git a/src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs b/src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs new file mode 100644 index 000000000..10e850f03 --- /dev/null +++ b/src/Common/src/Interop/Windows/mincore/Interop.GetCurrentThreadId.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class mincore + { + [DllImport(Libraries.ProcessThreads)] + internal extern static uint GetCurrentThreadId(); + } +} diff --git a/src/System.Private.CoreLib/src/Interop/Interop.manual.cs b/src/System.Private.CoreLib/src/Interop/Interop.manual.cs index 08b9a91e5..ffc17b236 100644 --- a/src/System.Private.CoreLib/src/Interop/Interop.manual.cs +++ b/src/System.Private.CoreLib/src/Interop/Interop.manual.cs @@ -86,9 +86,6 @@ internal partial class Interop [DllImport("api-ms-win-core-synch-l1-1-0.dll", EntryPoint = "CreateSemaphoreExW", CharSet = CharSet.Unicode)] internal static extern IntPtr CreateSemaphoreEx(IntPtr lpSemaphoreAttributes, int lInitialCount, int lMaximumCount, string lpName, uint dwFlags, uint dwDesiredAccess); - [DllImport("api-ms-win-core-processthreads-l1-1-0.dll")] - internal extern static uint GetCurrentThreadId(); - [DllImport("api-ms-win-core-debug-l1-1-0.dll", EntryPoint = "IsDebuggerPresent", CharSet = CharSet.Unicode)] internal extern static bool IsDebuggerPresent(); diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 4998a45c6..41cce6086 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -752,6 +752,9 @@ Interop\Windows\Interop.BOOL.cs + + Interop\Windows\mincore\Interop.GetCurrentThreadId.cs + Interop\Windows\mincore\Interop.SetLastError.cs diff --git a/src/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/System.Private.CoreLib/src/System/Environment.Unix.cs index bcfdbe4f6..7cf60c683 100644 --- a/src/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/src/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -9,6 +9,8 @@ namespace System { public static partial class Environment { + internal static int CurrentNativeThreadId => ManagedThreadId.Current; + internal static long TickCount64 { get diff --git a/src/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/System.Private.CoreLib/src/System/Environment.Windows.cs index 7576857a9..3c35f0f7a 100644 --- a/src/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/src/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -6,6 +6,8 @@ namespace System { public static partial class Environment { + internal static int CurrentNativeThreadId => unchecked((int)Interop.mincore.GetCurrentThreadId()); + internal static long TickCount64 => (long)Interop.mincore.GetTickCount64(); public static int ProcessorCount diff --git a/src/System.Private.CoreLib/src/System/Threading/Lock.cs b/src/System.Private.CoreLib/src/System/Threading/Lock.cs index e81d5c3cf..1a122c21b 100644 --- a/src/System.Private.CoreLib/src/System/Threading/Lock.cs +++ b/src/System.Private.CoreLib/src/System/Threading/Lock.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#pragma warning disable 0420 //passing volatile field by reference - - using System.Diagnostics; using System.Runtime.CompilerServices; @@ -57,12 +54,14 @@ namespace System.Threading } } - /// Inlined version of Lock.Acquire has CurrentManagedThreadId not inlined, non-inlined version has it inlined. - /// So it saves code to keep this function non inlining while keep the same runtime cost + // On platforms where CurrentNativeThreadId redirects to ManagedThreadId.Current the inlined + // version of Lock.Acquire has the ManagedThreadId.Current call not inlined, while the non-inlined + // version has it inlined. So it saves code to keep this function not inlined while having + // the same runtime cost. [MethodImpl(MethodImplOptions.NoInlining)] public void Acquire() { - int currentThreadId = Environment.CurrentManagedThreadId; + int currentThreadId = Environment.CurrentNativeThreadId; // // Make one quick attempt to acquire an uncontended lock @@ -95,7 +94,7 @@ namespace System.Threading if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - int currentThreadId = Environment.CurrentManagedThreadId; + int currentThreadId = Environment.CurrentNativeThreadId; // // Make one quick attempt to acquire an uncontended lock @@ -230,6 +229,9 @@ namespace System.Threading { get { + // + // The comment below is for platforms where CurrentNativeThreadId redirects to + // ManagedThreadId.Current instead of being a compiler intrinsic. // // Compare the current owning thread ID with the current thread ID. We need // to read the current thread's ID before we read m_owningThreadId. Otherwise, @@ -237,7 +239,7 @@ namespace System.Threading // // 1) We read m_owningThreadId, and get, say 42, which belongs to another thread. // 2) Thread 42 releases the lock, and exits. - // 3) We call CurrentManagedThreadId. If this is the first time it's been called + // 3) We call ManagedThreadId.Current. If this is the first time it's been called // on this thread, we'll go get a new ID. We may reuse thread 42's ID, since // that thread is dead. // 4) Now we're thread 42, and it looks like we own the lock, even though we don't. @@ -246,8 +248,8 @@ namespace System.Threading // because while we're doing this check the current thread is definitely still // alive. // - int currentManagedThreadId = Environment.CurrentManagedThreadId; - bool acquired = (currentManagedThreadId == _owningThreadId); + int currentThreadId = Environment.CurrentNativeThreadId; + bool acquired = (currentThreadId == _owningThreadId); if (acquired) Debug.Assert((_state & Locked) != 0); return acquired; -- cgit v1.2.3