From 30e2d44a36788c69646920b3256c58939316dfde Mon Sep 17 00:00:00 2001 From: Eugene Rozenfeld Date: Fri, 20 Mar 2020 13:26:36 -0700 Subject: [PATCH] Determinism changes. 1. The timer workaround is no longer needed since EventPipe file polling was removed in https://github.com/dotnet/coreclr/pull/24225 2. https://github.com/dotnet/runtime/pull/467 introduced a change that causes pmi non determinism. System.Threading.Thread.s_isProcessorNumberReallyFast can have different values on two invocations of the process on the same machine. (https://github.com/dotnet/runtime/blob/aedf8f52006619ef5d4eca65d79f42cc4b7bc402/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs#L502) That causes non-determinism in generated code in methods' inlining System.Threading.Thread.GetCurrentProcessorId() (https://github.com/dotnet/runtime/blob/aedf8f52006619ef5d4eca65d79f42cc4b7bc402/src/coreclr/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs#L492-L498) The workaround is to set the value of System.Threading.Thread.s_isProcessorNumberReallyFast to true via reflection. --- src/pmi/pmi.cs | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/pmi/pmi.cs b/src/pmi/pmi.cs index 19e6b1e9..2917f227 100644 --- a/src/pmi/pmi.cs +++ b/src/pmi/pmi.cs @@ -12,7 +12,6 @@ using System.Runtime.CompilerServices; using System.Runtime.Loader; using System.Text.RegularExpressions; -using System.Threading; using System.Threading.Tasks; using System.Buffers; @@ -1327,28 +1326,13 @@ private static int Usage() return 101; } - private static Timer timer; - private static int flag = 0; - - private static void DummyTimerCallback(object state) - { - timer.Change(Timeout.Infinite, Timeout.Infinite); - flag = 1; - } - - private static void EnsureTimerCallbackIsJitted() + private static void EnsureGetCurrentProcessorIdIsDeterministic() { - Thread.Sleep(1); - timer = new Timer( - callback: new TimerCallback(DummyTimerCallback), - state: null, - dueTime: Timeout.Infinite, - period: Timeout.Infinite); - timer.Change(10, Timeout.Infinite); - while (flag == 0) - { - Thread.Sleep(1); - } +#if NETCOREAPP + var type = typeof(System.Threading.Thread); + var field = type.GetField("s_isProcessorNumberReallyFast", BindingFlags.NonPublic | BindingFlags.Static); + field.SetValue(null, true); +#endif } private static void EnsureGen2GcCallbackFuncIsJitted() @@ -1371,11 +1355,12 @@ public static int Main(string[] args) return Usage(); } - // Tracing infrastructure unconditionally creates a Timer and uses it for checking - // whether tracing has been enabled. Since the Timer callback is called on a worker thread, - // we may get corrupted disasm output. To prevent that, force jitting of Timer callback infrastructure - // before we PMI any method. - EnsureTimerCallbackIsJitted(); + // System.Threading.Thread.s_isProcessorNumberReallyFast can have different + // values on two invocations of the process on the same machine. That causes + // non-determinism in generated code in methods inlining System.Threading.Thread.GetCurrentProcessorId(). + // This methods uses reflection to set the value of System.Threading.Thread.s_isProcessorNumberReallyFast + // to true. + EnsureGetCurrentProcessorIdIsDeterministic(); // TlsOverPerCoreLockedStacksArrayPool.Return registers Gen2GcCallbackFunc on Gen2GcCallback. // Gen2GcCallbackFunc is then called from Gen2GcCallback finalizer after a gc.