diff options
Diffstat (limited to 'src/System.Private.CoreLib/src/Internal/Runtime')
11 files changed, 381 insertions, 127 deletions
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs index 6d28d393b..76ead8cb7 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/EnvironmentAugments.Unix.cs @@ -27,10 +27,55 @@ namespace Internal.Runtime.Augments public static IEnumerable<KeyValuePair<string,string>> EnumerateEnvironmentVariables() { - if ("".Length != 0) - throw new NotImplementedException(); // Need to return something better than an empty environment block. + IntPtr block = Interop.Sys.GetEnviron(); + if (block == IntPtr.Zero) + yield break; - return Array.Empty<KeyValuePair<string,string>>(); + // Per man page, environment variables come back as an array of pointers to strings + // Parse each pointer of strings individually + while (ParseEntry(block, out string key, out string value)) + { + if (key != null && value != null) + yield return new KeyValuePair<string, string>(key, value); + + // Increment to next environment variable entry + block += IntPtr.Size; + } + + // Use a local, unsafe function since we cannot use `yield return` inside of an `unsafe` block + unsafe bool ParseEntry(IntPtr current, out string key, out string value) + { + // Setup + key = null; + value = null; + + // Point to current entry + byte* entry = *(byte**)current; + + // Per man page, "The last pointer in this array has the value NULL" + // Therefore, if entry is null then we're at the end and can bail + if (entry == null) + return false; + + // Parse each byte of the entry until we hit either the separator '=' or '\0'. + // This finds the split point for creating key/value strings below. + // On some old OS, the environment block can be corrupted. + // Some will not have '=', so we need to check for '\0'. + byte* splitpoint = entry; + while (*splitpoint != '=' && *splitpoint != '\0') + splitpoint++; + + // Skip over entries starting with '=' and entries with no value (just a null-terminating char '\0') + if (splitpoint == entry || *splitpoint == '\0') + return true; + + // The key is the bytes from start (0) until our splitpoint + key = new string((sbyte*)entry, 0, checked((int)(splitpoint - entry))); + // The value is the rest of the bytes starting after the splitpoint + value = new string((sbyte*)(splitpoint + 1)); + + return true; + } } private static void ExitRaw() diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs index c37c0d064..bf19ab608 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs @@ -47,7 +47,7 @@ namespace Internal.Runtime.Augments // Flotsam and jetsam. public abstract Exception CreateMissingMetadataException(Type typeWithMissingMetadata); - public abstract String GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle); + public abstract string GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle); public abstract MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress); public abstract int ValueTypeGetHashCodeUsingReflection(object valueType); public abstract bool ValueTypeEqualsUsingReflection(object left, object right); diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 0213e8821..23c22a965 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -87,11 +87,11 @@ namespace Internal.Runtime.Augments // // In these cases, this helper returns "null" and ConstructorInfo.Invoke() must deal with these specially. // - public static Object NewObject(RuntimeTypeHandle typeHandle) + public static object NewObject(RuntimeTypeHandle typeHandle) { EETypePtr eeType = typeHandle.ToEETypePtr(); if (eeType.IsNullable - || eeType == EETypePtr.EETypePtrOf<String>() + || eeType == EETypePtr.EETypePtrOf<string>() ) return null; return RuntimeImports.RhNewObject(eeType); @@ -102,7 +102,7 @@ namespace Internal.Runtime.Augments // Unlike the NewObject API, this is the raw version that does not special case any EEType, and should be used with // caution for very specific scenarios. // - public static Object RawNewObject(RuntimeTypeHandle typeHandle) + public static object RawNewObject(RuntimeTypeHandle typeHandle) { return RuntimeImports.RhNewObject(typeHandle.ToEETypePtr()); } @@ -192,7 +192,7 @@ namespace Internal.Runtime.Augments // // Helper to create a delegate on a runtime-supplied type. // - public static Delegate CreateDelegate(RuntimeTypeHandle typeHandleForDelegate, IntPtr ldftnResult, Object thisObject, bool isStatic, bool isOpen) + public static Delegate CreateDelegate(RuntimeTypeHandle typeHandleForDelegate, IntPtr ldftnResult, object thisObject, bool isStatic, bool isOpen) { return Delegate.CreateDelegate(typeHandleForDelegate.ToEETypePtr(), ldftnResult, thisObject, isStatic: isStatic, isOpen: isOpen); } @@ -248,7 +248,7 @@ namespace Internal.Runtime.Augments return new IntPtr(RuntimeImports.RhGetThreadStaticFieldAddress(typeHandle.ToEETypePtr(), threadStaticsBlockOffset, fieldOffset)); } - public static unsafe void StoreValueTypeField(IntPtr address, Object fieldValue, RuntimeTypeHandle fieldType) + public static unsafe void StoreValueTypeField(IntPtr address, object fieldValue, RuntimeTypeHandle fieldType) { RuntimeImports.RhUnbox(fieldValue, *(void**)&address, fieldType.ToEETypePtr()); } @@ -258,7 +258,7 @@ namespace Internal.Runtime.Augments return ref obj.GetRawData(); } - public static unsafe Object LoadValueTypeField(IntPtr address, RuntimeTypeHandle fieldType) + public static unsafe object LoadValueTypeField(IntPtr address, RuntimeTypeHandle fieldType) { return RuntimeImports.RhBox(fieldType.ToEETypePtr(), *(void**)&address); } @@ -268,12 +268,12 @@ namespace Internal.Runtime.Augments return Pointer.Box(*(void**)address, Type.GetTypeFromHandle(fieldType)); } - public static unsafe void StoreValueTypeField(ref byte address, Object fieldValue, RuntimeTypeHandle fieldType) + public static unsafe void StoreValueTypeField(ref byte address, object fieldValue, RuntimeTypeHandle fieldType) { RuntimeImports.RhUnbox(fieldValue, ref address, fieldType.ToEETypePtr()); } - public static unsafe void StoreValueTypeField(Object obj, int fieldOffset, Object fieldValue, RuntimeTypeHandle fieldType) + public static unsafe void StoreValueTypeField(object obj, int fieldOffset, object fieldValue, RuntimeTypeHandle fieldType) { fixed (IntPtr* pObj = &obj.m_pEEType) { @@ -283,7 +283,7 @@ namespace Internal.Runtime.Augments } } - public static unsafe Object LoadValueTypeField(Object obj, int fieldOffset, RuntimeTypeHandle fieldType) + public static unsafe object LoadValueTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType) { fixed (IntPtr* pObj = &obj.m_pEEType) { @@ -293,7 +293,7 @@ namespace Internal.Runtime.Augments } } - public static unsafe Object LoadPointerTypeField(Object obj, int fieldOffset, RuntimeTypeHandle fieldType) + public static unsafe object LoadPointerTypeField(object obj, int fieldOffset, RuntimeTypeHandle fieldType) { fixed (IntPtr* pObj = &obj.m_pEEType) { @@ -303,17 +303,17 @@ namespace Internal.Runtime.Augments } } - public static unsafe void StoreReferenceTypeField(IntPtr address, Object fieldValue) + public static unsafe void StoreReferenceTypeField(IntPtr address, object fieldValue) { - Volatile.Write<Object>(ref Unsafe.As<IntPtr, Object>(ref *(IntPtr*)address), fieldValue); + Volatile.Write<Object>(ref Unsafe.As<IntPtr, object>(ref *(IntPtr*)address), fieldValue); } - public static unsafe Object LoadReferenceTypeField(IntPtr address) + public static unsafe object LoadReferenceTypeField(IntPtr address) { - return Volatile.Read<Object>(ref Unsafe.As<IntPtr, Object>(ref *(IntPtr*)address)); + return Volatile.Read<Object>(ref Unsafe.As<IntPtr, object>(ref *(IntPtr*)address)); } - public static unsafe void StoreReferenceTypeField(Object obj, int fieldOffset, Object fieldValue) + public static unsafe void StoreReferenceTypeField(object obj, int fieldOffset, object fieldValue) { fixed (IntPtr* pObj = &obj.m_pEEType) { @@ -323,7 +323,7 @@ namespace Internal.Runtime.Augments } } - public static unsafe Object LoadReferenceTypeField(Object obj, int fieldOffset) + public static unsafe object LoadReferenceTypeField(object obj, int fieldOffset) { fixed (IntPtr* pObj = &obj.m_pEEType) { @@ -473,7 +473,7 @@ namespace Internal.Runtime.Augments // Note that this is not versionable as it is exposed as a const (and needs to be a const so we can used as a custom attribute argument - which // is the other reason this string is not versionable.) // - public const String HiddenScopeAssemblyName = "HiddenScope, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + public const string HiddenScopeAssemblyName = "HiddenScope, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; // // This implements the "IsAssignableFrom()" api for runtime-created types. By policy, we let the underlying runtime decide assignability. @@ -716,7 +716,7 @@ namespace Internal.Runtime.Augments return true; } - public static Object CheckArgument(Object srcObject, RuntimeTypeHandle dstType, BinderBundle binderBundle) + public static object CheckArgument(object srcObject, RuntimeTypeHandle dstType, BinderBundle binderBundle) { return InvokeUtils.CheckArgument(srcObject, dstType, binderBundle); } @@ -728,7 +728,7 @@ namespace Internal.Runtime.Augments return InvokeUtils.CheckArgument(srcObject, dstType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.SetFieldDirect, binderBundle: null); } - public static bool IsAssignable(Object srcObject, RuntimeTypeHandle dstType) + public static bool IsAssignable(object srcObject, RuntimeTypeHandle dstType) { EETypePtr srcEEType = srcObject.EETypePtr; return RuntimeImports.AreTypesAssignable(srcEEType, dstType.ToEETypePtr()); @@ -751,9 +751,9 @@ namespace Internal.Runtime.Augments // // Useful helper for finding .pdb's. (This design is admittedly tied to the single-module design of Project N.) // - public static String TryGetFullPathToMainApplication() + public static string TryGetFullPathToMainApplication() { - Func<String> delegateToAnythingInsideMergedApp = TryGetFullPathToMainApplication; + Func<string> delegateToAnythingInsideMergedApp = TryGetFullPathToMainApplication; IntPtr ipToAnywhereInsideMergedApp = delegateToAnythingInsideMergedApp.GetFunctionPointer(out RuntimeTypeHandle _, out bool _, out bool _); IntPtr moduleBase = RuntimeImports.RhGetOSModuleFromPointer(ipToAnywhereInsideMergedApp); return TryGetFullPathToApplicationModule(moduleBase); @@ -763,7 +763,7 @@ namespace Internal.Runtime.Augments /// Locate the file path for a given native application module. /// </summary> /// <param name="moduleBase">Module base address</param> - public static unsafe String TryGetFullPathToApplicationModule(IntPtr moduleBase) + public static unsafe string TryGetFullPathToApplicationModule(IntPtr moduleBase) { #if PLATFORM_UNIX byte* pModuleNameUtf8; @@ -772,7 +772,7 @@ namespace Internal.Runtime.Augments #else // PLATFORM_UNIX char* pModuleName; int numChars = RuntimeImports.RhGetModuleFileName(moduleBase, out pModuleName); - String modulePath = new String(pModuleName, 0, numChars); + string modulePath = new string(pModuleName, 0, numChars); #endif // PLATFORM_UNIX return modulePath; } @@ -1111,7 +1111,7 @@ namespace Internal.Runtime.Augments return RuntimeImports.RhBoxAny((void*)pData, new EETypePtr(pEEType)); } - public static IntPtr RhHandleAlloc(Object value, GCHandleType type) + public static IntPtr RhHandleAlloc(object value, GCHandleType type) { return RuntimeImports.RhHandleAlloc(value, type); } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs index 7b9d380e7..bc3a042eb 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Unix.cs @@ -144,6 +144,14 @@ namespace Internal.Runtime.Augments return IntPtr.Zero; } + private void InitializeComOnNewThread() + { + } + + internal static void InitializeCom() + { + } + public void Interrupt() => WaitSubsystem.Interrupt(this); internal static void UninterruptibleSleep0() => WaitSubsystem.UninterruptibleSleep0(); private static void SleepInternal(int millisecondsTimeout) => WaitSubsystem.Sleep(millisecondsTimeout); @@ -159,5 +167,17 @@ namespace Internal.Runtime.Augments { throw new PlatformNotSupportedException(); } + + private static int ComputeCurrentProcessorId() + { + int processorId = Interop.Sys.SchedGetCpu(); + + // sched_getcpu doesn't exist on all platforms. On those it doesn't exist on, the shim + // returns -1. As a fallback in that case and to spread the threads across the buckets + // by default, we use the current managed thread ID as a proxy. + if (processorId < 0) processorId = Environment.CurrentManagedThreadId; + + return processorId; + } } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs index 6d0e937dc..e9fe5485b 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.Windows.cs @@ -21,8 +21,13 @@ namespace Internal.Runtime.Augments [ThreadStatic] private static ApartmentType t_apartmentType; + [ThreadStatic] + private static bool t_comInitializedByUs; + private SafeWaitHandle _osHandle; + private ApartmentState _initialAppartmentState = ApartmentState.Unknown; + /// <summary> /// Used by <see cref="WaitHandle"/>'s multi-wait functions /// </summary> @@ -99,7 +104,7 @@ namespace Internal.Runtime.Augments // Throw an ApplicationException for compatibility with CoreCLR. First save the error code. int errorCode = Marshal.GetLastWin32Error(); var ex = new ApplicationException(); - ex.SetErrorCode(errorCode); + ex.HResult = errorCode; throw ex; } @@ -283,10 +288,103 @@ namespace Internal.Runtime.Augments return 0; } - public ApartmentState GetApartmentState() { throw null; } - public bool TrySetApartmentState(ApartmentState state) { throw null; } - public void DisableComObjectEagerCleanup() { throw null; } - public void Interrupt() { throw null; } + public ApartmentState GetApartmentState() + { + if (this != CurrentThread) + { + if (HasStarted()) + throw new ThreadStateException(); + return _initialAppartmentState; + } + + switch (GetCurrentApartmentType()) + { + case ApartmentType.STA: + return ApartmentState.STA; + case ApartmentType.MTA: + return ApartmentState.MTA; + default: + return ApartmentState.Unknown; + } + } + + public bool TrySetApartmentState(ApartmentState state) + { + if (this != CurrentThread) + { + using (LockHolder.Hold(_lock)) + { + if (HasStarted()) + throw new ThreadStateException(); + _initialAppartmentState = state; + return true; + } + } + + if (state != ApartmentState.Unknown) + { + InitializeCom(state); + } + else + { + UninitializeCom(); + } + + // Clear the cache and check whether new state matches the desired state + t_apartmentType = ApartmentType.Unknown; + return state == GetApartmentState(); + } + + private void InitializeComOnNewThread() + { + InitializeCom(_initialAppartmentState); + } + + internal static void InitializeCom(ApartmentState state = ApartmentState.MTA) + { + if (t_comInitializedByUs) + return; + +#if ENABLE_WINRT + int hr = Interop.WinRT.RoInitialize( + (state == ApartmentState.STA) ? Interop.WinRT.RO_INIT_SINGLETHREADED + : Interop.WinRT.RO_INIT_MULTITHREADED); +#else + int hr = Interop.Ole32.CoInitializeEx(IntPtr.Zero, + (state == ApartmentState.STA) ? Interop.Ole32.COINIT_APARTMENTTHREADED + : Interop.Ole32.COINIT_MULTITHREADED); +#endif + // RPC_E_CHANGED_MODE indicates this thread has been already initialized with a different + // concurrency model. We stay away and let whoever else initialized the COM to be in control. + if (hr == HResults.RPC_E_CHANGED_MODE) + return; + if (hr < 0) + throw new OutOfMemoryException(); + + t_comInitializedByUs = true; + + // If the thread has already been CoInitialized to the proper mode, then + // we don't want to leave an outstanding CoInit so we CoUninit. + if (hr > 0) + UninitializeCom(); + } + + private static void UninitializeCom() + { + if (!t_comInitializedByUs) + return; + +#if ENABLE_WINRT + Interop.WinRT.RoUninitialize(); +#else + Interop.Ole32.CoUninitialize(); +#endif + t_comInitializedByUs = false; + } + + // TODO: https://github.com/dotnet/corefx/issues/20766 + public void DisableComObjectEagerCleanup() { } + public void Interrupt() { throw new PlatformNotSupportedException(); } internal static void UninterruptibleSleep0() { @@ -324,40 +422,40 @@ namespace Internal.Runtime.Augments if (currentThreadType != ApartmentType.Unknown) return currentThreadType; - Interop._APTTYPE aptType; - Interop._APTTYPEQUALIFIER aptTypeQualifier; - int result = Interop.mincore.CoGetApartmentType(out aptType, out aptTypeQualifier); + Interop.APTTYPE aptType; + Interop.APTTYPEQUALIFIER aptTypeQualifier; + int result = Interop.Ole32.CoGetApartmentType(out aptType, out aptTypeQualifier); ApartmentType type = ApartmentType.Unknown; - switch ((Interop.Constants)result) + switch (result) { - case Interop.Constants.CoENotInitialized: + case HResults.CO_E_NOTINITIALIZED: type = ApartmentType.None; break; - case Interop.Constants.SOk: + case HResults.S_OK: switch (aptType) { - case Interop._APTTYPE.APTTYPE_STA: - case Interop._APTTYPE.APTTYPE_MAINSTA: + case Interop.APTTYPE.APTTYPE_STA: + case Interop.APTTYPE.APTTYPE_MAINSTA: type = ApartmentType.STA; break; - case Interop._APTTYPE.APTTYPE_MTA: + case Interop.APTTYPE.APTTYPE_MTA: type = ApartmentType.MTA; break; - case Interop._APTTYPE.APTTYPE_NA: + case Interop.APTTYPE.APTTYPE_NA: switch (aptTypeQualifier) { - case Interop._APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA: - case Interop._APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: type = ApartmentType.MTA; break; - case Interop._APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA: - case Interop._APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA: + case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA: type = ApartmentType.STA; break; @@ -386,5 +484,7 @@ namespace Internal.Runtime.Augments STA, MTA } + + private static int ComputeCurrentProcessorId() => (int)Interop.mincore.GetCurrentProcessorNumber(); } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs index b21d9ced8..b3e9a14fe 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeThread.cs @@ -7,6 +7,7 @@ using System; using System.Diagnostics; using System.Globalization; using System.Runtime; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; @@ -79,6 +80,14 @@ namespace Internal.Runtime.Augments } } + internal static ulong CurrentOSThreadId + { + get + { + return RuntimeImports.RhCurrentOSThreadId(); + } + } + // Slow path executed once per thread private static RuntimeThread InitializeExistingThread(bool threadPoolThread) { @@ -103,7 +112,7 @@ namespace Internal.Runtime.Augments if (threadPoolThread) { - RoInitialize(); + InitializeCom(); } return currentThread; @@ -142,16 +151,6 @@ namespace Internal.Runtime.Augments } /// <summary> - /// Ensures the Windows Runtime is initialized on the current thread. - /// </summary> - internal static void RoInitialize() - { -#if ENABLE_WINRT - Interop.WinRT.RoInitialize(); -#endif - } - - /// <summary> /// Returns true if the underlying OS thread has been created and started execution of managed code. /// </summary> private bool HasStarted() @@ -370,6 +369,7 @@ namespace Internal.Runtime.Augments return JoinInternal(millisecondsTimeout); } + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. public static void Sleep(int millisecondsTimeout) => SleepInternal(VerifyTimeoutMilliseconds(millisecondsTimeout)); /// <summary> @@ -381,6 +381,8 @@ namespace Internal.Runtime.Augments internal static readonly int OptimalMaxSpinWaitsPerSpinIteration = 64; public static void SpinWait(int iterations) => RuntimeImports.RhSpinWait(iterations); + + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. public static bool Yield() => RuntimeImports.RhYield(); public void Start() => StartInternal(null); @@ -455,7 +457,7 @@ namespace Internal.Runtime.Augments { t_currentThread = thread; System.Threading.ManagedThreadId.SetForCurrentThread(thread._managedThreadId); - RoInitialize(); + thread.InitializeComOnNewThread(); } catch (OutOfMemoryException) { @@ -491,5 +493,42 @@ namespace Internal.Runtime.Augments thread.SetThreadStateBit(ThreadState.Stopped); } } + + // The upper bits of t_currentProcessorIdCache are the currentProcessorId. The lower bits of + // the t_currentProcessorIdCache are counting down to get it periodically refreshed. + // TODO: Consider flushing the currentProcessorIdCache on Wait operations or similar + // actions that are likely to result in changing the executing core + [ThreadStatic] + private static int t_currentProcessorIdCache; + + private const int ProcessorIdCacheShift = 16; + private const int ProcessorIdCacheCountDownMask = (1 << ProcessorIdCacheShift) - 1; + private const int ProcessorIdRefreshRate = 5000; + + private static int RefreshCurrentProcessorId() + { + int currentProcessorId = ComputeCurrentProcessorId(); + + // Add offset to make it clear that it is not guaranteed to be 0-based processor number + currentProcessorId += 100; + + Debug.Assert(ProcessorIdRefreshRate <= ProcessorIdCacheCountDownMask); + + // Mask with int.MaxValue to ensure the execution Id is not negative + t_currentProcessorIdCache = ((currentProcessorId << ProcessorIdCacheShift) & int.MaxValue) + ProcessorIdRefreshRate; + + return currentProcessorId; + } + + // Cached processor id used as a hint for which per-core stack to access. It is periodically + // refreshed to trail the actual thread core affinity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetCurrentProcessorId() + { + int currentProcessorIdCache = t_currentProcessorIdCache--; + if ((currentProcessorIdCache & ProcessorIdCacheCountDownMask) == 0) + return RefreshCurrentProcessorId(); + return (currentProcessorIdCache >> ProcessorIdCacheShift); + } } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs index ad79c2510..dc598bbb7 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Text; -using System.Runtime.InteropServices; using System.Diagnostics; -using Interlocked = System.Threading.Interlocked; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; namespace Internal.Runtime.CompilerHelpers { @@ -15,7 +16,7 @@ namespace Internal.Runtime.CompilerHelpers /// </summary> internal static class InteropHelpers { - internal static unsafe byte* StringToAnsiString(String str, bool bestFit, bool throwOnUnmappableChar) + internal static unsafe byte* StringToAnsiString(string str, bool bestFit, bool throwOnUnmappableChar) { return PInvokeMarshal.StringToAnsiString(str, bestFit, throwOnUnmappableChar); } @@ -37,7 +38,7 @@ namespace Internal.Runtime.CompilerHelpers return PInvokeMarshal.ByValAnsiStringToString(buffer, length); } - internal static unsafe void StringToUnicodeFixedArray(String str, UInt16* buffer, int length) + internal static unsafe void StringToUnicodeFixedArray(string str, ushort* buffer, int length) { if (buffer == null) return; @@ -58,16 +59,16 @@ namespace Internal.Runtime.CompilerHelpers } } - internal static unsafe string UnicodeToStringFixedArray(UInt16* buffer, int length) + internal static unsafe string UnicodeToStringFixedArray(ushort* buffer, int length) { if (buffer == null) - return String.Empty; + return string.Empty; - string result = String.Empty; + string result = string.Empty; if (length > 0) { - result = new String(' ', length); + result = new string(' ', length); fixed (char* pTemp = result) { @@ -78,7 +79,7 @@ namespace Internal.Runtime.CompilerHelpers return result; } - internal static unsafe char* StringToUnicodeBuffer(String str) + internal static unsafe char* StringToUnicodeBuffer(string str) { if (str == null) return null; @@ -98,7 +99,7 @@ namespace Internal.Runtime.CompilerHelpers public static unsafe string UnicodeBufferToString(char* buffer) { - return new String(buffer); + return new string(buffer); } public static unsafe byte* AllocMemoryForAnsiStringBuilder(StringBuilder sb) @@ -208,6 +209,7 @@ namespace Internal.Runtime.CompilerHelpers return ResolvePInvokeSlow(pCell); } + [MethodImpl(MethodImplOptions.NoInlining)] internal static unsafe IntPtr ResolvePInvokeSlow(MethodFixupCell* pCell) { ModuleFixupCell* pModuleCell = pCell->Module; @@ -304,8 +306,8 @@ namespace Internal.Runtime.CompilerHelpers { byte* methodName = (byte*)pCell->MethodName; -#if !PLATFORM_UNIX - pCell->Target = Interop.mincore.GetProcAddress(hModule, methodName); +#if PLATFORM_WINDOWS + pCell->Target = GetProcAddress(hModule, methodName, pCell->CharSetMangling); #else pCell->Target = Interop.Sys.GetProcAddress(hModule, methodName); #endif @@ -316,6 +318,44 @@ namespace Internal.Runtime.CompilerHelpers } } +#if PLATFORM_WINDOWS + private static unsafe IntPtr GetProcAddress(IntPtr hModule, byte* methodName, CharSet charSetMangling) + { + // First look for the unmangled name. If it is unicode function, we are going + // to need to check for the 'W' API because it takes precedence over the + // unmangled one (on NT some APIs have unmangled ANSI exports). + + var exactMatch = Interop.mincore.GetProcAddress(hModule, methodName); + + if ((charSetMangling == CharSet.Ansi && exactMatch != IntPtr.Zero) || charSetMangling == 0) + { + return exactMatch; + } + + int nameLength = strlen(methodName); + + // We need to add an extra byte for the suffix, and an extra byte for the null terminator + byte* probedMethodName = stackalloc byte[nameLength + 2]; + + for (int i = 0; i < nameLength; i++) + { + probedMethodName[i] = methodName[i]; + } + + probedMethodName[nameLength + 1] = 0; + + probedMethodName[nameLength] = (charSetMangling == CharSet.Ansi) ? (byte)'A' : (byte)'W'; + + IntPtr probedMethod = Interop.mincore.GetProcAddress(hModule, probedMethodName); + if (probedMethod != IntPtr.Zero) + { + return probedMethod; + } + + return exactMatch; + } +#endif + internal static unsafe int strlen(byte* pString) { byte* p = pString; @@ -379,6 +419,7 @@ namespace Internal.Runtime.CompilerHelpers public IntPtr Target; public IntPtr MethodName; public ModuleFixupCell* Module; + public CharSet CharSetMangling; } } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs index c31521d80..6947cc9f3 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/MathHelpers.cs @@ -24,64 +24,64 @@ namespace Internal.Runtime.CompilerHelpers // Helper to multiply two 32-bit uints [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static UInt64 Mul32x32To64(UInt32 a, UInt32 b) + private static ulong Mul32x32To64(uint a, uint b) { - return a * (UInt64)b; + return a * (ulong)b; } // Helper to get high 32-bit of 64-bit int [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static UInt32 Hi32Bits(Int64 a) + private static uint Hi32Bits(long a) { - return (UInt32)(a >> 32); + return (uint)(a >> 32); } // Helper to get high 32-bit of 64-bit int [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static UInt32 Hi32Bits(UInt64 a) + private static uint Hi32Bits(ulong a) { - return (UInt32)(a >> 32); + return (uint)(a >> 32); } [RuntimeExport("LMulOvf")] - public static Int64 LMulOvf(Int64 i, Int64 j) + public static long LMulOvf(long i, long j) { - Int64 ret; + long ret; // Remember the sign of the result - Int32 sign = (Int32)(Hi32Bits(i) ^ Hi32Bits(j)); + int sign = (int)(Hi32Bits(i) ^ Hi32Bits(j)); // Convert to unsigned multiplication if (i < 0) i = -i; if (j < 0) j = -j; // Get the upper 32 bits of the numbers - UInt32 val1High = Hi32Bits(i); - UInt32 val2High = Hi32Bits(j); + uint val1High = Hi32Bits(i); + uint val2High = Hi32Bits(j); - UInt64 valMid; + ulong valMid; if (val1High == 0) { // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, (UInt32)i); + valMid = Mul32x32To64(val2High, (uint)i); } else { if (val2High != 0) goto ThrowExcep; // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, (UInt32)j); + valMid = Mul32x32To64(val1High, (uint)j); } // See if any bits after bit 32 are set if (Hi32Bits(valMid) != 0) goto ThrowExcep; - ret = (Int64)(Mul32x32To64((UInt32)i, (UInt32)j) + (valMid << 32)); + ret = (long)(Mul32x32To64((uint)i, (uint)j) + (valMid << 32)); // check for overflow - if (Hi32Bits(ret) < (UInt32)valMid) + if (Hi32Bits(ret) < (uint)valMid) goto ThrowExcep; if (sign >= 0) @@ -104,39 +104,39 @@ namespace Internal.Runtime.CompilerHelpers } [RuntimeExport("ULMulOvf")] - public static UInt64 ULMulOvf(UInt64 i, UInt64 j) + public static ulong ULMulOvf(ulong i, ulong j) { - UInt64 ret; + ulong ret; // Get the upper 32 bits of the numbers - UInt32 val1High = Hi32Bits(i); - UInt32 val2High = Hi32Bits(j); + uint val1High = Hi32Bits(i); + uint val2High = Hi32Bits(j); - UInt64 valMid; + ulong valMid; if (val1High == 0) { if (val2High == 0) - return Mul32x32To64((UInt32)i, (UInt32)j); + return Mul32x32To64((uint)i, (uint)j); // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val2High, (UInt32)i); + valMid = Mul32x32To64(val2High, (uint)i); } else { if (val2High != 0) goto ThrowExcep; // Compute the 'middle' bits of the long multiplication - valMid = Mul32x32To64(val1High, (UInt32)j); + valMid = Mul32x32To64(val1High, (uint)j); } // See if any bits after bit 32 are set if (Hi32Bits(valMid) != 0) goto ThrowExcep; - ret = Mul32x32To64((UInt32)i, (UInt32)j) + (valMid << 32); + ret = Mul32x32To64((uint)i, (uint)j) + (valMid << 32); // check for overflow - if (Hi32Bits(ret) < (UInt32)valMid) + if (Hi32Bits(ret) < (uint)valMid) goto ThrowExcep; return ret; @@ -212,7 +212,7 @@ namespace Internal.Runtime.CompilerHelpers // Note that this expression also works properly for val = NaN case // We need to compare with the very next double to two63. 0x402 is epsilon to get us there. if (val > -two63 - 0x402 && val < two63) - return ((Int64)val); + return ((long)val); return ThrowIntOvf(); } @@ -222,13 +222,13 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpIDiv")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern Int32 RhpIDiv(Int32 i, Int32 j); + private static extern int RhpIDiv(int i, int j); - public static int IDiv(Int32 i, Int32 j) + public static int IDiv(int i, int j) { if (j == 0) return ThrowIntDivByZero(); - else if (j == -1 && i == Int32.MinValue) + else if (j == -1 && i == int.MinValue) return ThrowIntArithExc(); else return RhpIDiv(i, j); @@ -236,9 +236,9 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpUDiv")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern UInt32 RhpUDiv(UInt32 i, UInt32 j); + private static extern uint RhpUDiv(uint i, uint j); - public static long UDiv(UInt32 i, UInt32 j) + public static long UDiv(uint i, uint j) { if (j == 0) return ThrowUIntDivByZero(); @@ -248,9 +248,9 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpULDiv")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern UInt64 RhpULDiv(UInt64 i, UInt64 j); + private static extern ulong RhpULDiv(ulong i, ulong j); - public static ulong ULDiv(UInt64 i, UInt64 j) + public static ulong ULDiv(ulong i, ulong j) { if (j == 0) return ThrowULngDivByZero(); @@ -260,13 +260,13 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpLDiv")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern Int64 RhpLDiv(Int64 i, Int64 j); + private static extern long RhpLDiv(long i, long j); - public static long LDiv(Int64 i, Int64 j) + public static long LDiv(long i, long j) { if (j == 0) return ThrowLngDivByZero(); - else if (j == -1 && i == Int64.MinValue) + else if (j == -1 && i == long.MinValue) return ThrowLngArithExc(); else return RhpLDiv(i, j); @@ -274,9 +274,9 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpIMod")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern Int32 RhpIMod(Int32 i, Int32 j); + private static extern int RhpIMod(int i, int j); - public static int IMod(Int32 i, Int32 j) + public static int IMod(int i, int j) { if (j == 0) return ThrowIntDivByZero(); @@ -286,9 +286,9 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpUMod")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern UInt32 RhpUMod(UInt32 i, UInt32 j); + private static extern uint RhpUMod(uint i, uint j); - public static long UMod(UInt32 i, UInt32 j) + public static long UMod(uint i, uint j) { if (j == 0) return ThrowUIntDivByZero(); @@ -298,9 +298,9 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpULMod")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern UInt64 RhpULMod(UInt64 i, UInt64 j); + private static extern ulong RhpULMod(ulong i, ulong j); - public static ulong ULMod(UInt64 i, UInt64 j) + public static ulong ULMod(ulong i, ulong j) { if (j == 0) return ThrowULngDivByZero(); @@ -310,9 +310,9 @@ namespace Internal.Runtime.CompilerHelpers [RuntimeImport(RuntimeLibrary, "RhpLMod")] [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern Int64 RhpLMod(Int64 i, Int64 j); + private static extern long RhpLMod(long i, long j); - public static long LMod(Int64 i, Int64 j) + public static long LMod(long i, long j) { if (j == 0) return ThrowLngDivByZero(); diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs index 30694aead..018c8005c 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs @@ -54,7 +54,7 @@ namespace Internal.Runtime.CompilerHelpers lockTaken = false; } - private static Object GetStaticLockObject(IntPtr pEEType) + private static object GetStaticLockObject(IntPtr pEEType) { return Internal.Reflection.Core.NonPortable.RuntimeTypeUnifier.GetRuntimeTypeForEEType(new System.EETypePtr(pEEType)); } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RelocatedTypeAttribute.cs b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RelocatedTypeAttribute.cs index a848f5a94..74363b72b 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RelocatedTypeAttribute.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/RelocatedTypeAttribute.cs @@ -19,7 +19,7 @@ namespace Internal.Runtime.CompilerServices /// </summary> /// <param name="originalAssemblySimpleName">Simple name of the CoreFX assembly the type was relocated from. /// For example, System.Collections (with no version or public key token)</param> - public RelocatedTypeAttribute(String originalAssemblySimpleName) + public RelocatedTypeAttribute(string originalAssemblySimpleName) { } } diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs b/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs index db6977ee5..be6537858 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs @@ -4,6 +4,7 @@ using System; using System.Runtime; +using System.Runtime.CompilerServices; using Internal.Runtime.CompilerHelpers; namespace Internal.Runtime @@ -18,21 +19,29 @@ namespace Internal.Runtime /// This method is called from a ReadyToRun helper to get base address of thread /// static storage for the given type. /// </summary> - internal static unsafe object GetThreadStaticBaseForType(TypeManagerSlot* pModuleData, Int32 typeTlsIndex) + internal static unsafe object GetThreadStaticBaseForType(TypeManagerSlot* pModuleData, int typeTlsIndex) { // Get the array that holds thread static memory blocks for each type in the given module - Int32 moduleIndex = pModuleData->ModuleIndex; - object[] storage = (object[])RuntimeImports.RhGetThreadStaticStorageForModule(moduleIndex); + object[] storage = RuntimeImports.RhGetThreadStaticStorageForModule(pModuleData->ModuleIndex); // Check whether thread static storage has already been allocated for this module and type. - if ((storage != null) && (typeTlsIndex < storage.Length) && (storage[typeTlsIndex] != null)) + if ((storage != null) && ((uint)typeTlsIndex < (uint)storage.Length) && (storage[typeTlsIndex] != null)) { return storage[typeTlsIndex]; } + return GetThreadStaticBaseForTypeSlow(pModuleData, typeTlsIndex); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static unsafe object GetThreadStaticBaseForTypeSlow(TypeManagerSlot* pModuleData, int typeTlsIndex) + { + // Get the array that holds thread static memory blocks for each type in the given module + object[] storage = RuntimeImports.RhGetThreadStaticStorageForModule(pModuleData->ModuleIndex); + // This the first access to the thread statics of the type corresponding to typeTlsIndex. // Make sure there is enough storage allocated to hold it. - storage = EnsureThreadStaticStorage(moduleIndex, storage, requiredSize: typeTlsIndex + 1); + storage = EnsureThreadStaticStorage(pModuleData->ModuleIndex, storage, requiredSize: typeTlsIndex + 1); // Allocate an object that will represent a memory block for all thread static fields of the type object threadStaticBase = AllocateThreadStaticStorageForType(pModuleData->TypeManager, typeTlsIndex); @@ -45,7 +54,7 @@ namespace Internal.Runtime /// if it is required, this method extends thread static storage of the given module /// to the specified size and then registers the memory with the runtime. /// </summary> - private static object[] EnsureThreadStaticStorage(Int32 moduleIndex, object[] existingStorage, Int32 requiredSize) + private static object[] EnsureThreadStaticStorage(int moduleIndex, object[] existingStorage, int requiredSize) { if ((existingStorage != null) && (requiredSize < existingStorage.Length)) { @@ -73,9 +82,9 @@ namespace Internal.Runtime /// This method allocates an object that represents a memory block for all thread static fields of the type /// that corresponds to the specified TLS index. /// </summary> - private static unsafe object AllocateThreadStaticStorageForType(TypeManagerHandle typeManager, Int32 typeTlsIndex) + private static unsafe object AllocateThreadStaticStorageForType(TypeManagerHandle typeManager, int typeTlsIndex) { - Int32 length; + int length; IntPtr* threadStaticRegion; // Get a pointer to the beginning of the module's Thread Static section. Then get a pointer |