From 7a1dbc9538fbef67bbb7eaf6007790cd32d6715b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 14 Jul 2025 23:59:25 +0000 Subject: [PATCH 01/33] Use the wait subsystem on CoreCLR on Unix platforms. --- .../System.Private.CoreLib.csproj | 3 +- .../System/Threading/Mutex.CoreCLR.Unix.cs | 178 ------------------ .../src/System/Threading/Thread.CoreCLR.cs | 44 +++++ ...reCLR.cs => WaitHandle.CoreCLR.Windows.cs} | 0 src/coreclr/vm/comsynchronizable.cpp | 26 +++ src/coreclr/vm/comsynchronizable.h | 2 + src/coreclr/vm/qcallentrypoints.cpp | 1 + src/coreclr/vm/threads.h | 2 +- .../System.Private.CoreLib.Shared.projitems | 28 +-- .../src/System/Threading/WaitHandle.cs | 8 - .../System/Threading/WaitSubsystem.Unix.cs | 3 - 11 files changed, 89 insertions(+), 206 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs rename src/coreclr/System.Private.CoreLib/src/System/Threading/{WaitHandle.CoreCLR.cs => WaitHandle.CoreCLR.Windows.cs} (100%) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index c12d6c854ac0c0..a64294355f6e44 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -238,7 +238,6 @@ - @@ -291,7 +290,6 @@ - @@ -301,6 +299,7 @@ Common\Interop\Windows\OleAut32\Interop.VariantChangeTypeEx.cs + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs deleted file mode 100644 index 08b0748b412c8c..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using Microsoft.Win32.SafeHandles; - -namespace System.Threading -{ - /// - /// Synchronization primitive that can also be used for interprocess synchronization - /// - public sealed partial class Mutex : WaitHandle - { - private unsafe void CreateMutexCore(bool initiallyOwned) - { - SafeWaitHandle handle = - CreateMutex( - initiallyOwned, - name: null, - currentUserOnly: false, - systemCallErrors: null, - systemCallErrorsBufferSize: 0); - if (handle.IsInvalid) - { - int errorCode = Marshal.GetLastPInvokeError(); - handle.SetHandleAsInvalid(); - throw Win32Marshal.GetExceptionForWin32Error(errorCode); - } - - SafeWaitHandle = handle; - } - - private void CreateMutexCore( - bool initiallyOwned, - string? name, - NamedWaitHandleOptionsInternal options, - out bool createdNew) - { - bool currentUserOnly = false; - if (!string.IsNullOrEmpty(name) && options.WasSpecified) - { - name = options.GetNameWithSessionPrefix(name); - currentUserOnly = options.CurrentUserOnly; - } - - SafeWaitHandle mutexHandle = - CreateMutexCore(initiallyOwned, name, currentUserOnly, out int errorCode, out string? errorDetails); - if (mutexHandle.IsInvalid) - { - mutexHandle.SetHandleAsInvalid(); - if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE) - // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 - throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name)); - if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) - throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name)); - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, name, errorDetails); - } - - createdNew = errorCode != Interop.Errors.ERROR_ALREADY_EXISTS; - SafeWaitHandle = mutexHandle; - } - - private static OpenExistingResult OpenExistingWorker( - string name, - NamedWaitHandleOptionsInternal options, - out Mutex? result) - { - ArgumentException.ThrowIfNullOrEmpty(name); - - bool currentUserOnly = false; - if (options.WasSpecified) - { - name = options.GetNameWithSessionPrefix(name); - currentUserOnly = options.CurrentUserOnly; - } - - result = null; - // To allow users to view & edit the ACL's, call OpenMutex - // with parameters to allow us to view & edit the ACL. This will - // fail if we don't have permission to view or edit the ACL's. - // If that happens, ask for less permissions. - SafeWaitHandle myHandle = OpenMutexCore(name, currentUserOnly, out int errorCode, out string? errorDetails); - - if (myHandle.IsInvalid) - { - myHandle.Dispose(); - - if (errorCode == Interop.Errors.ERROR_FILENAME_EXCED_RANGE) - { - // On Unix, length validation is done by CoreCLR's PAL after converting to utf-8 - throw new ArgumentException(SR.Argument_WaitHandleNameTooLong, nameof(name)); - } - if (Interop.Errors.ERROR_FILE_NOT_FOUND == errorCode || Interop.Errors.ERROR_INVALID_NAME == errorCode) - return OpenExistingResult.NameNotFound; - if (Interop.Errors.ERROR_PATH_NOT_FOUND == errorCode) - return OpenExistingResult.PathNotFound; - if (Interop.Errors.ERROR_INVALID_HANDLE == errorCode) - return OpenExistingResult.NameInvalid; - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, name, errorDetails); - } - - result = new Mutex(myHandle); - return OpenExistingResult.Success; - } - - // Note: To call ReleaseMutex, you must have an ACL granting you - // MUTEX_MODIFY_STATE rights (0x0001). The other interesting value - // in a Mutex's ACL is MUTEX_ALL_ACCESS (0x1F0001). - public void ReleaseMutex() - { - if (!Interop.Kernel32.ReleaseMutex(SafeWaitHandle)) - { - throw new ApplicationException(SR.Arg_SynchronizationLockException); - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Unix-specific implementation - - private const int SystemCallErrorsBufferSize = 256; - - private static unsafe SafeWaitHandle CreateMutexCore( - bool initialOwner, - string? name, - bool currentUserOnly, - out int errorCode, - out string? errorDetails) - { - byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize]; - SafeWaitHandle mutexHandle = - CreateMutex(initialOwner, name, currentUserOnly, systemCallErrors, SystemCallErrorsBufferSize); - - // Get the error code even if the handle is valid, as it could be ERROR_ALREADY_EXISTS, indicating that the mutex - // already exists and was opened - errorCode = Marshal.GetLastPInvokeError(); - - errorDetails = mutexHandle.IsInvalid ? GetErrorDetails(systemCallErrors) : null; - return mutexHandle; - } - - private static unsafe SafeWaitHandle OpenMutexCore(string name, bool currentUserOnly, out int errorCode, out string? errorDetails) - { - byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize]; - SafeWaitHandle mutexHandle = OpenMutex(name, currentUserOnly, systemCallErrors, SystemCallErrorsBufferSize); - errorCode = mutexHandle.IsInvalid ? Marshal.GetLastPInvokeError() : Interop.Errors.ERROR_SUCCESS; - errorDetails = mutexHandle.IsInvalid ? GetErrorDetails(systemCallErrors) : null; - return mutexHandle; - } - - private static unsafe string? GetErrorDetails(byte* systemCallErrors) - { - int systemCallErrorsLength = - new ReadOnlySpan(systemCallErrors, SystemCallErrorsBufferSize).IndexOf((byte)'\0'); - if (systemCallErrorsLength > 0) - { - try - { - return - SR.Format(SR.Unix_SystemCallErrors, Encoding.UTF8.GetString(systemCallErrors, systemCallErrorsLength)); - } - catch { } // avoid hiding the original error due to an error here - } - - return null; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_CreateMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - private static unsafe partial SafeWaitHandle CreateMutex([MarshalAs(UnmanagedType.Bool)] bool initialOwner, string? name, [MarshalAs(UnmanagedType.Bool)] bool currentUserOnly, byte* systemCallErrors, uint systemCallErrorsBufferSize); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_OpenMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - private static unsafe partial SafeWaitHandle OpenMutex(string name, [MarshalAs(UnmanagedType.Bool)] bool currentUserOnly, byte* systemCallErrors, uint systemCallErrorsBufferSize); - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 99569377896a14..24cc0e91781022 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -62,6 +62,10 @@ public sealed partial class Thread private bool _isDead; private bool _isThreadPool; +#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI + internal WaitSubsystem.ThreadWaitInfo? _waitInfo; +#endif + private Thread() { } public int ManagedThreadId @@ -298,6 +302,30 @@ public ThreadState ThreadState [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")] private static partial int GetThreadState(ThreadHandle t); +#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI + internal void SetWaitSleepJoinState() + { + // This method is called when the thread is about to enter a wait, sleep, or join state. + // It sets the state in the native layer to indicate that the thread is waiting. + SetWaitSleepJoinState(GetNativeHandle()); + GC.KeepAlive(this); + } + + internal void ClearWaitSleepJoinState() + { + // This method is called when the thread is no longer in a wait, sleep, or join state. + // It clears the state in the native layer to indicate that the thread is no longer waiting. + ClearWaitSleepJoinState(GetNativeHandle()); + GC.KeepAlive(this); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetWaitSleepJoinState")] + private static partial void SetWaitSleepJoinState(ThreadHandle t); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ClearWaitSleepJoinState")] + private static partial void ClearWaitSleepJoinState(ThreadHandle t); +#endif + /// /// An unstarted thread can be marked to indicate that it will host a /// single-threaded or multi-threaded apartment. @@ -513,6 +541,22 @@ private static void PollGC() static void PollGCWorker() => PollGCInternal(); } +#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI + internal WaitSubsystem.ThreadWaitInfo WaitInfo + { + get + { + return Volatile.Read(ref _waitInfo) ?? AllocateWaitInfo(); + + WaitSubsystem.ThreadWaitInfo AllocateWaitInfo() + { + Interlocked.CompareExchange(ref _waitInfo, new WaitSubsystem.ThreadWaitInfo(this), null!); + return _waitInfo; + } + } + } +#endif + [StructLayout(LayoutKind.Sequential)] private struct NativeThreadClass { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs rename to src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index e98f7ec66de23a..a1095dc564eecf 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -447,6 +447,32 @@ extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle threa return res; } +extern "C" void QCALLTYPE ThreadNative_SetWaitSleepJoinState(QCall::ThreadHandle thread) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + // Set the state bits. + thread->SetThreadState(Thread::TS_Interruptible); + thread->SetThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin); + + END_QCALL; +} + +extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHandle thread) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + // Clear the state bits. + thread->ResetThreadState(Thread::TS_Interruptible); + thread->ResetThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin); + + END_QCALL; +} + #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT // Return whether the thread hosts an STA, is a member of the MTA or is not diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index 5c78eb8be2039f..1931eca7a79afd 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -56,6 +56,8 @@ extern "C" void QCALLTYPE ThreadNative_PollGC(); extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId(); extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t); extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread); +extern "C" void QCALLTYPE ThreadNative_SetWaitSleepJoinState(QCall::ThreadHandle thread); +extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHandle thread); #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT extern "C" INT32 QCALLTYPE ThreadNative_GetApartmentState(QCall::ObjectHandleOnStack t); diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e2dd498b8b483a..89d85bbd8ee37b 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -289,6 +289,7 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_GetCurrentOSThreadId) DllImportEntry(ThreadNative_Initialize) DllImportEntry(ThreadNative_GetThreadState) + DllImportEntry(ThreadNative_SetWaitSleepJoinState) #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT DllImportEntry(ThreadNative_GetApartmentState) DllImportEntry(ThreadNative_SetApartmentState) diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index d50b680f4862e4..5b79157aa208e0 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -907,8 +907,8 @@ class Thread // the top of the object. Also, we want cache line filling to work for us // so the critical stuff is ordered based on frequency of use. - Volatile m_State; // Bits for the state of the thread + Volatile m_State; // Bits for the state of the thread // If TRUE, GC is scheduled cooperatively with this thread. // NOTE: This "byte" is actually a boolean - we don't allow // recursive disables. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 56bb2664ff9c33..3ed8313b45956a 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1775,6 +1775,15 @@ Common\Interop\Windows\Kernel32\Interop.CreateFile.cs + + Common\Interop\Windows\Kernel32\Interop.EventWaitHandle.cs + + + Common\Interop\Windows\Kernel32\Interop.Mutex.cs + + + Common\Interop\Windows\Kernel32\Interop.Semaphore.cs + Interop\Windows\Kernel32\Interop.Timer.cs @@ -2233,6 +2242,7 @@ + @@ -2271,10 +2281,12 @@ + + @@ -2299,16 +2311,12 @@ - Common\Interop\Windows\Kernel32\Interop.CloseHandle.cs Common\Interop\Windows\Kernel32\Interop.Constants.cs - - Common\Interop\Windows\Kernel32\Interop.EventWaitHandle.cs - Common\Interop\Windows\Kernel32\Interop.GetEnvironmentVariable.cs @@ -2321,12 +2329,6 @@ Common\Interop\Windows\Kernel32\Interop.FormatMessage.cs - - Common\Interop\Windows\Kernel32\Interop.Mutex.cs - - - Common\Interop\Windows\Kernel32\Interop.Semaphore.cs - Common\Interop\Windows\Kernel32\Interop.SetEnvironmentVariable.cs @@ -2337,8 +2339,6 @@ Common\System\Memory\FixedBufferExtensions.cs - - @@ -2846,7 +2846,7 @@ - + @@ -2857,7 +2857,7 @@ - + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs index 70bbc3bd2e20e7..bc401ac142d3d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @@ -143,7 +143,6 @@ internal bool WaitOneNoCheck( if (!usedSyncContextWait) { -#if !CORECLR // CoreCLR sends the wait events from the native side bool sendWaitEvents = millisecondsTimeout != 0 && !useTrivialWaits && @@ -178,17 +177,14 @@ internal bool WaitOneNoCheck( // When tryNonblockingWaitFirst is true, we have a final wait result from the nonblocking wait above if (!tryNonblockingWaitFirst) -#endif { waitResult = WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout, useTrivialWaits); } -#if !CORECLR // CoreCLR sends the wait events from the native side if (sendWaitEvents) { NativeRuntimeEventSource.Log.WaitHandleWaitStop(); } -#endif } if (waitResult == WaitAbandoned) @@ -400,7 +396,6 @@ internal static int WaitMultipleIgnoringSyncContext(ReadOnlySpan handles { int waitResult = WaitFailed; -#if !CORECLR // CoreCLR sends the wait events from the native side bool sendWaitEvents = millisecondsTimeout != 0 && NativeRuntimeEventSource.Log.IsEnabled( @@ -432,17 +427,14 @@ internal static int WaitMultipleIgnoringSyncContext(ReadOnlySpan handles // When tryNonblockingWaitFirst is true, we have a final wait result from the nonblocking wait above if (!tryNonblockingWaitFirst) -#endif { waitResult = WaitMultipleIgnoringSyncContextCore(handles, waitAll, millisecondsTimeout); } -#if !CORECLR // CoreCLR sends the wait events from the native side if (sendWaitEvents) { NativeRuntimeEventSource.Log.WaitHandleWaitStop(); } -#endif return waitResult; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs index bcc2c1ea08ae42..cad35f0dc4d378 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs @@ -44,9 +44,6 @@ namespace System.Threading /// ## Design goals /// /// Behave similarly to wait operations on Windows - /// - The design is similar to the one used by CoreCLR's PAL, but much simpler due to there being no need for supporting - /// process/thread waits, or cross-process multi-waits (which CoreCLR also does not support but there are many design - /// elements specific to it) /// - Waiting /// - A waiter keeps an array of objects on which it is waiting (see ). /// - The waiter registers a with each From 25bc429deb73cc52daa575656fbf823ba147c4e3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 15 Jul 2025 00:04:47 +0000 Subject: [PATCH 02/33] Remove wait subsystem qcall entrypoints --- src/coreclr/vm/qcallentrypoints.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 89d85bbd8ee37b..2400d89c714020 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -485,24 +485,12 @@ static const Entry s_QCall[] = #endif #if defined(TARGET_UNIX) DllImportEntry(CloseHandle) - DllImportEntry(CreateEventExW) - DllImportEntry(CreateMutexExW) - DllImportEntry(CreateSemaphoreExW) DllImportEntry(FormatMessageW) DllImportEntry(FreeEnvironmentStringsW) DllImportEntry(GetEnvironmentStringsW) DllImportEntry(GetEnvironmentVariableW) - DllImportEntry(OpenEventW) - DllImportEntry(OpenMutexW) - DllImportEntry(OpenSemaphoreW) DllImportEntry(OutputDebugStringW) - DllImportEntry(PAL_CreateMutexW) - DllImportEntry(PAL_OpenMutexW) - DllImportEntry(ReleaseMutex) - DllImportEntry(ReleaseSemaphore) - DllImportEntry(ResetEvent) DllImportEntry(SetEnvironmentVariableW) - DllImportEntry(SetEvent) #endif #if defined(TARGET_X86) || defined(TARGET_AMD64) DllImportEntry(X86BaseCpuId) From 293866c6513fb7ffc2ec067a8798c034fa3a1215 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 15 Jul 2025 00:33:04 +0000 Subject: [PATCH 03/33] Remove named mutex support from the CoreCLR PAL --- src/coreclr/pal/inc/pal.h | 31 - src/coreclr/pal/src/CMakeLists.txt | 1 - src/coreclr/pal/src/include/pal/context.h | 1 + src/coreclr/pal/src/include/pal/corunix.hpp | 1 - src/coreclr/pal/src/include/pal/mutex.hpp | 208 -- .../pal/src/include/pal/sharedmemory.h | 317 --- .../pal/src/include/pal/synchobjects.hpp | 8 - src/coreclr/pal/src/init/pal.cpp | 79 - src/coreclr/pal/src/misc/dbgmsg.cpp | 1 + .../pal/src/sharedmemory/sharedmemory.cpp | 1745 ----------------- src/coreclr/pal/src/synchmgr/synchmanager.cpp | 114 +- src/coreclr/pal/src/synchmgr/wait.cpp | 52 - src/coreclr/pal/src/synchobj/mutex.cpp | 1460 +------------- src/coreclr/pal/src/thread/process.cpp | 2 +- src/coreclr/pal/tests/palsuite/CMakeLists.txt | 4 - .../pal/tests/palsuite/compilableTests.txt | 1 - .../pal/tests/palsuite/paltestlist.txt | 1 - .../test2/CreateMutexW.cpp | 174 -- .../threading/NamedMutex/test1/namedmutex.cpp | 1361 ------------- .../threading/NamedMutex/test1/nopal.cpp | 85 - .../WaitForMultipleObjectsEx/test6/child6.cpp | 210 -- .../WaitForMultipleObjectsEx/test6/test6.cpp | 718 ------- 22 files changed, 72 insertions(+), 6502 deletions(-) delete mode 100644 src/coreclr/pal/src/include/pal/sharedmemory.h delete mode 100644 src/coreclr/pal/src/sharedmemory/sharedmemory.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/test6.cpp diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index d9f338c40e7e5c..11ef97bf2efcd3 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -759,42 +759,11 @@ CreateMutexExW( IN DWORD dwFlags, IN DWORD dwDesiredAccess); -PALIMPORT -HANDLE -PALAPI -PAL_CreateMutexW( - IN BOOL bInitialOwner, - IN LPCWSTR lpName, - IN BOOL bCurrentUserOnly, - IN LPSTR lpSystemCallErrors, - IN DWORD dwSystemCallErrorsBufferSize); - // CreateMutexExW: dwFlags #define CREATE_MUTEX_INITIAL_OWNER ((DWORD)0x1) #define CreateMutex CreateMutexW -PALIMPORT -HANDLE -PALAPI -OpenMutexW( - IN DWORD dwDesiredAccess, - IN BOOL bInheritHandle, - IN LPCWSTR lpName); - -PALIMPORT -HANDLE -PALAPI -PAL_OpenMutexW( - IN LPCWSTR lpName, - IN BOOL bCurrentUserOnly, - IN LPSTR lpSystemCallErrors, - IN DWORD dwSystemCallErrorsBufferSize); - -#ifdef UNICODE -#define OpenMutex OpenMutexW -#endif - PALIMPORT BOOL PALAPI diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index 1395013eb59601..00e0b770d51080 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -198,7 +198,6 @@ set(SOURCES safecrt/wcsncat_s.cpp safecrt/wcsncpy_s.cpp safecrt/wmakepath_s.cpp - sharedmemory/sharedmemory.cpp synchobj/event.cpp synchobj/semaphore.cpp synchobj/mutex.cpp diff --git a/src/coreclr/pal/src/include/pal/context.h b/src/coreclr/pal/src/include/pal/context.h index 2f5310c4d66195..717d91bbe31c60 100644 --- a/src/coreclr/pal/src/include/pal/context.h +++ b/src/coreclr/pal/src/include/pal/context.h @@ -29,6 +29,7 @@ extern "C" #include #include +#include /* A type to wrap the native context type, which is ucontext_t on some * platforms and another type elsewhere. */ diff --git a/src/coreclr/pal/src/include/pal/corunix.hpp b/src/coreclr/pal/src/include/pal/corunix.hpp index 7e606980dd3434..3bb843cdfa374f 100644 --- a/src/coreclr/pal/src/include/pal/corunix.hpp +++ b/src/coreclr/pal/src/include/pal/corunix.hpp @@ -160,7 +160,6 @@ namespace CorUnix otiAutoResetEvent = 0, otiManualResetEvent, otiMutex, - otiNamedMutex, otiSemaphore, otiFile, otiFileMapping, diff --git a/src/coreclr/pal/src/include/pal/mutex.hpp b/src/coreclr/pal/src/include/pal/mutex.hpp index 9ff179ca43d180..c1f9aab7b08cb9 100644 --- a/src/coreclr/pal/src/include/pal/mutex.hpp +++ b/src/coreclr/pal/src/include/pal/mutex.hpp @@ -21,23 +21,18 @@ Module Name: #define _PAL_MUTEX_H_ #include "corunix.hpp" -#include "sharedmemory.h" #include namespace CorUnix { extern CObjectType otMutex; - extern CObjectType otNamedMutex; PAL_ERROR InternalCreateMutex( - SharedMemorySystemCallErrors *errors, CPalThread *pThread, LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, - LPCSTR lpName, - BOOL bCurrentUserOnly, HANDLE *phMutex ); @@ -46,16 +41,6 @@ namespace CorUnix CPalThread *pThread, HANDLE hMutex ); - - PAL_ERROR - InternalOpenMutex( - SharedMemorySystemCallErrors *errors, - CPalThread *pThread, - LPCSTR lpName, - BOOL bCurrentUserOnly, - HANDLE *phMutex - ); - } #define SYNCSPINLOCK_F_ASYMMETRIC 1 @@ -66,197 +51,4 @@ namespace CorUnix void SPINLOCKAcquire (LONG * lock, unsigned int flags); void SPINLOCKRelease (LONG * lock); DWORD SPINLOCKTryAcquire (LONG * lock); - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Named mutex - -/* -Design - -- On systems that support pthread process-shared robust recursive mutexes, they will be used -- On other systems, file locks are used. File locks unfortunately don't have a timeout in the blocking wait call, and I didn't - find any other sync object with a timed wait with the necessary properties, so polling is done for timed waits. - -Shared memory files -- Session-scoped mutexes (name not prefixed, or prefixed with Local) go in /tmp/.dotnet/shm/session/ -- Globally-scoped mutexes (name prefixed with Global) go in /tmp/.dotnet/shm/global/ -- Contains shared state, and is mmap'ped into the process, see SharedMemorySharedDataHeader and NamedMutexSharedData for data - stored -- Creation and deletion is synchronized using an exclusive file lock on the shm directory -- Any process using the shared memory file holds a shared file lock on the shared memory file -- Upon creation, if the shared memory file already exists, an exclusive file lock is attempted on it, to see if the file data is - valid. If no other processes have the mutex open, the file is reinitialized. -- Upon releasing the last reference to a mutex in a process, it will try to get an exclusive lock on the shared memory file to - see if any other processes have the mutex opened. If not, the file is deleted, along with the session directory if it's empty. - The .dotnet and shm directories are not deleted. -- This allows managing the lifetime of mutex state based on active processes that have the mutex open. Depending on how the - process terminated, the file may still be left over in the tmp directory, I haven't found anything that can be done about - that. - -Lock files when using file locks: -- In addition to the shared memory file, we need another file for the actual synchronization file lock, since a file lock on the - shared memory file is used for lifetime purposes. -- These files go in /tmp/.dotnet/lockfiles/session|global/ -- The file is empty, and is only used for file locks - -Process data -- See SharedMemoryProcessDataHeader and NamedMutexProcessData for data stored -- Per mutex name, there is only one instance of process data that is ref-counted. They are currently stored in a linked list in - SharedMemoryManager. It should use a hash table, but of the many hash table implementations that are already there, none seem - to be easily usable in the PAL. I'll look into that and will fix later. -- Refers to the associated shared memory, and knows how to clean up both the process data and shared data -- When using file locks for synchronization, a process-local mutex is also created for synchronizing threads, since file locks - are owned at the file descriptor level and there is only one open file descriptor in the process per mutex name. The - process-local mutex is locked around the file lock, so that only one thread per process is ever trying to flock on a given - file descriptor. - -Abandon detection -- When a lock is acquired, the process data is added to a linked list on the owning thread -- When a thread exits, the list is walked, each mutex is flagged as abandoned and released -- For detecting process abruptly terminating, pthread robust mutexes give us that. When using file locks, the file lock is - automatically released by the system. Upon acquiring a lock, the lock owner info in the shared memory is checked to see if the - mutex was abandoned. - -Miscellaneous -- CreateMutex and OpenMutex both create new handles for each mutex opened. Each handle just refers to the process data header - for the mutex name. -- Some of the above features are already available in the PAL, but not quite in a way that I can use for this purpose. The - existing shared memory, naming, and waiting infrastructure is not suitable for this purpose, and is not used. -*/ - -// - On FreeBSD, pthread process-shared robust mutexes cannot be placed in shared memory mapped independently by the processes -// involved. See https://github.com/dotnet/runtime/issues/10519. -// - On OSX, pthread robust mutexes were/are not available at the time of this writing. In case they are made available in the -// future, their use is disabled for compatibility. -#if HAVE_FULLY_FEATURED_PTHREAD_MUTEXES && \ - HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES && \ - !(defined(__FreeBSD__) || defined(TARGET_OSX)) - - #define NAMED_MUTEX_USE_PTHREAD_MUTEX 1 -#else - #define NAMED_MUTEX_USE_PTHREAD_MUTEX 0 -#endif - -enum class NamedMutexError : DWORD -{ - MaximumRecursiveLocksReached = ERROR_NOT_ENOUGH_MEMORY, - ThreadHasNotAcquiredMutex = ERROR_NOT_OWNER, - Unknown = ERROR_NOT_ENOUGH_MEMORY -}; - -enum class MutexTryAcquireLockResult -{ - AcquiredLock, - AcquiredLockButMutexWasAbandoned, - TimedOut -}; - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX -class MutexHelpers -{ -public: - static void InitializeProcessSharedRobustRecursiveMutex(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex); - static void DestroyMutex(pthread_mutex_t *mutex); - - static MutexTryAcquireLockResult TryAcquireLock(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex, DWORD timeoutMilliseconds); - static void ReleaseLock(pthread_mutex_t *mutex); -}; -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX - -class NamedMutexSharedData -{ -private: -#if NAMED_MUTEX_USE_PTHREAD_MUTEX - pthread_mutex_t m_lock; -#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX - UINT32 m_timedWaiterCount; -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX - UINT32 m_lockOwnerProcessId; - UINT64 m_lockOwnerThreadId; - bool m_isAbandoned; - -public: - NamedMutexSharedData(SharedMemorySystemCallErrors *errors); - ~NamedMutexSharedData(); - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX -public: - pthread_mutex_t *GetLock(); -#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX -public: - bool HasAnyTimedWaiters() const; - void IncTimedWaiterCount(); - void DecTimedWaiterCount(); -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX - -public: - bool IsAbandoned() const; - void SetIsAbandoned(bool isAbandoned); - -public: - bool IsLockOwnedByAnyThread() const; - bool IsLockOwnedByCurrentThread() const; - void SetLockOwnerToCurrentThread(); - void ClearLockOwner(); -}; - -class NamedMutexProcessData : public SharedMemoryProcessDataBase -{ -private: - static const UINT8 SyncSystemVersion; - static const DWORD PollLoopMaximumSleepMilliseconds; - -private: - SharedMemoryProcessDataHeader *m_processDataHeader; - SIZE_T m_lockCount; -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - HANDLE m_processLockHandle; - int m_sharedLockFileDescriptor; -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - CorUnix::CPalThread *m_lockOwnerThread; - NamedMutexProcessData *m_nextInThreadOwnedNamedMutexList; - bool m_hasRefFromLockOwnerThread; - -public: - static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope, bool acquireLockIfCreated, bool *createdRef); - static SharedMemoryProcessDataHeader *Open(SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope); -private: - static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope, bool createIfNotExist, bool acquireLockIfCreated, bool *createdRef); - -public: - NamedMutexProcessData( - SharedMemoryProcessDataHeader *processDataHeader - #if !NAMED_MUTEX_USE_PTHREAD_MUTEX - , - int sharedLockFileDescriptor - #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - ); - -public: - virtual bool CanClose() const override; - virtual bool HasImplicitRef() const override; - virtual void SetHasImplicitRef(bool value) override; - virtual void Close(bool isAbruptShutdown, bool releaseSharedData) override; - -public: - bool IsLockOwnedByCurrentThread() const - { - return GetSharedData()->IsLockOwnedByCurrentThread(); - } - -private: - NamedMutexSharedData *GetSharedData() const; - void SetLockOwnerThread(CorUnix::CPalThread *lockOwnerThread); -public: - NamedMutexProcessData *GetNextInThreadOwnedNamedMutexList() const; - void SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next); - -public: - MutexTryAcquireLockResult TryAcquireLock(SharedMemorySystemCallErrors *errors, DWORD timeoutMilliseconds); - void ReleaseLock(); - void Abandon(); -private: - void ActuallyReleaseLock(); -}; - #endif //_PAL_MUTEX_H_ diff --git a/src/coreclr/pal/src/include/pal/sharedmemory.h b/src/coreclr/pal/src/include/pal/sharedmemory.h deleted file mode 100644 index 84a35d2b237dd6..00000000000000 --- a/src/coreclr/pal/src/include/pal/sharedmemory.h +++ /dev/null @@ -1,317 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#ifndef _PAL_SHARED_MEMORY_H_ -#define _PAL_SHARED_MEMORY_H_ - -#include "corunix.hpp" -#include - -#ifndef static_assert_no_msg -#define static_assert_no_msg( cond ) static_assert( cond, #cond ) -#endif // !static_assert_no_msg - -// The folder used for storing shared memory files and their lock files is defined in -// the gSharedFilesPath global variable. The value of the variable depends on which -// OS is being used, and if the application is running in a sandbox in Mac. -// gSharedFilesPath ends with '/' -// - Global shared memory files go in: -// {gSharedFilesPath}/.dotnet/shm/global/ -// - Session-scoped shared memory files go in: -// {gSharedFilesPath}/.dotnet/shm/session/ -// - Lock files associated with global shared memory files go in: -// {gSharedFilesPath}/.dotnet/lockfiles/global/ -// - Lock files associated with session-scoped shared memory files go in: -// {gSharedFilesPath}/.dotnet/lockfiles/session/ - -#define SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT (_MAX_FNAME - 1) -#define SHARED_MEMORY_MAX_NAME_CHAR_COUNT (STRING_LENGTH("Global\\") + SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT) - -#define SHARED_MEMORY_USER_UNSCOPED_RUNTIME_TEMP_DIRECTORY_NAME ".dotnet" -#define SHARED_MEMORY_USER_SCOPED_RUNTIME_TEMP_DIRECTORY_NAME_PREFIX ".dotnet-uid" -#define SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME "shm" -#define SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME "lockfiles" -static_assert_no_msg(STRING_LENGTH(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) >= STRING_LENGTH(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME)); - -#define SHARED_MEMORY_GLOBAL_DIRECTORY_NAME "global" -#define SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX "session" - -#define SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE ".dotnet.XXXXXX" - -// Note that this Max size does not include the prefix folder path size which is unknown (in the case of sandbox) until runtime -#define SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT \ - ( \ - STRING_LENGTH(SHARED_MEMORY_USER_SCOPED_RUNTIME_TEMP_DIRECTORY_NAME_PREFIX) + \ - 11 /* user ID, path separator */ + \ - STRING_LENGTH(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) + \ - 1 /* path separator */ + \ - STRING_LENGTH(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) + \ - 11 /* session ID, path separator */ + \ - SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT \ - ) - -class AutoFreeBuffer -{ -private: - void *m_buffer; - bool m_cancel; - -public: - AutoFreeBuffer(void *buffer); - ~AutoFreeBuffer(); - -public: - void Cancel(); -}; - -enum class SharedMemoryError : DWORD -{ - NameEmpty = ERROR_INVALID_PARAMETER, - NameTooLong = ERROR_FILENAME_EXCED_RANGE, - NameInvalid = ERROR_INVALID_NAME, - HeaderMismatch = ERROR_INVALID_HANDLE, - OutOfMemory = ERROR_NOT_ENOUGH_MEMORY, - IO = ERROR_OPEN_FAILED -}; - -class SharedMemoryException -{ -private: - DWORD m_errorCode; - -public: - SharedMemoryException(DWORD errorCode); - DWORD GetErrorCode() const; -}; - -class SharedMemorySystemCallErrors -{ -private: - char *m_buffer; - int m_bufferSize; - int m_length; - bool m_isTracking; - -public: - SharedMemorySystemCallErrors(char *buffer, int bufferSize); - void Append(LPCSTR format, ...); -}; - -class SharedMemoryId; - -class SharedMemoryHelpers -{ -private: - static const mode_t PermissionsMask_OwnerUser_ReadWrite; - static const mode_t PermissionsMask_OwnerUser_ReadWriteExecute; - static const mode_t PermissionsMask_NonOwnerUsers_Write; - static const mode_t PermissionsMask_AllUsers_ReadWrite; - static const mode_t PermissionsMask_AllUsers_ReadWriteExecute; - static const mode_t PermissionsMask_Sticky; -public: - static const UINT32 InvalidProcessId; - static const SIZE_T InvalidThreadId; - static const UINT64 InvalidSharedThreadId; - -public: - static SIZE_T AlignDown(SIZE_T value, SIZE_T alignment); - static SIZE_T AlignUp(SIZE_T value, SIZE_T alignment); - - static void *Alloc(SIZE_T byteCount); - static bool AppendUInt32String(PathCharString& destination, UINT32 value); - - static bool EnsureDirectoryExists(SharedMemorySystemCallErrors *errors, const char *path, const SharedMemoryId *id, bool isGlobalLockAcquired, bool createIfNotExist = true, bool isSystemDirectory = false); -private: - static int Open(SharedMemorySystemCallErrors *errors, LPCSTR path, int flags, mode_t mode = static_cast(0)); -public: - static int OpenDirectory(SharedMemorySystemCallErrors *errors, LPCSTR path); - static int CreateOrOpenFile(SharedMemorySystemCallErrors *errors, LPCSTR path, const SharedMemoryId *id, bool createIfNotExist = true, bool *createdRef = nullptr); - static void CloseFile(int fileDescriptor); - - static int ChangeMode(LPCSTR path, mode_t mode); - - static SIZE_T GetFileSize(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor); - static void SetFileSize(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor, SIZE_T byteCount); - - static void *MemoryMapFile(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor, SIZE_T byteCount); - - static bool TryAcquireFileLock(SharedMemorySystemCallErrors *errors, int fileDescriptor, int operation); - static void ReleaseFileLock(int fileDescriptor); - - static void VerifyStringOperation(bool success); - static void VerifyStringOperation(BOOL success) - { - VerifyStringOperation(success != FALSE); - } -}; - -class SharedMemoryId -{ -private: - LPCSTR m_name; - SIZE_T m_nameCharCount; - bool m_isSessionScope; // false indicates global scope - bool m_isUserScope; - uid_t m_userScopeUid; - -public: - SharedMemoryId(); - SharedMemoryId(LPCSTR name, bool isUserScope); - -public: - LPCSTR GetName() const; - SIZE_T GetNameCharCount() const; - void ReplaceNamePtr(LPCSTR name); - bool IsSessionScope() const; - bool IsUserScope() const; - uid_t GetUserScopeUid() const; - bool Equals(const SharedMemoryId *other) const; - -public: - bool AppendRuntimeTempDirectoryName(PathCharString& path) const; - bool AppendSessionDirectoryName(PathCharString& path) const; -}; - -enum class SharedMemoryType : UINT8 -{ - Mutex -}; - -class SharedMemorySharedDataHeader -{ -private: - union - { - struct - { - SharedMemoryType m_type; - UINT8 m_version; - }; - UINT64 _raw; // use the same size for the header on all archs, and align the data to a pointer - }; - -public: - static SIZE_T GetUsedByteCount(SIZE_T dataByteCount); - static SIZE_T GetTotalByteCount(SIZE_T dataByteCount); - -public: - SharedMemorySharedDataHeader(SharedMemoryType type, UINT8 version); - -public: - SharedMemoryType GetType() const; - UINT8 GetVersion() const; - void *GetData(); -}; - -class SharedMemoryProcessDataBase -{ -public: - virtual bool CanClose() const = 0; - virtual bool HasImplicitRef() const = 0; - virtual void SetHasImplicitRef(bool value) = 0; - virtual void Close(bool isAbruptShutdown, bool releaseSharedData) = 0; - - virtual ~SharedMemoryProcessDataBase() - { - } -}; - -class SharedMemoryProcessDataHeader -{ -private: - SIZE_T m_refCount; - SharedMemoryId m_id; - SharedMemoryProcessDataBase *m_data; - int m_fileDescriptor; - SharedMemorySharedDataHeader *m_sharedDataHeader; - SIZE_T m_sharedDataTotalByteCount; - SharedMemoryProcessDataHeader *m_nextInProcessDataHeaderList; - -public: - static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope, SharedMemorySharedDataHeader requiredSharedDataHeader, SIZE_T sharedDataByteCount, bool createIfNotExist, bool *createdRef); - -public: - static SharedMemoryProcessDataHeader *PalObject_GetProcessDataHeader(CorUnix::IPalObject *object); - static void PalObject_SetProcessDataHeader(CorUnix::IPalObject *object, SharedMemoryProcessDataHeader *processDataHeader); - static void PalObject_Close(CorUnix::CPalThread *thread, CorUnix::IPalObject *object, bool isShuttingDown); - -private: - SharedMemoryProcessDataHeader(const SharedMemoryId *id, int fileDescriptor, SharedMemorySharedDataHeader *sharedDataHeader, SIZE_T sharedDataTotalByteCount); -public: - static SharedMemoryProcessDataHeader *New(const SharedMemoryId *id, int fileDescriptor, SharedMemorySharedDataHeader *sharedDataHeader, SIZE_T sharedDataTotalByteCount); - ~SharedMemoryProcessDataHeader(); - void Close(); - -public: - const SharedMemoryId *GetId() const; - SharedMemoryProcessDataBase *GetData() const; - void SetData(SharedMemoryProcessDataBase *data); - SharedMemorySharedDataHeader *GetSharedDataHeader() const; - SIZE_T GetSharedDataTotalByteCount() const; - SharedMemoryProcessDataHeader *GetNextInProcessDataHeaderList() const; - void SetNextInProcessDataHeaderList(SharedMemoryProcessDataHeader *next); - -public: - void IncRefCount(); - void DecRefCount(); -}; - -class SharedMemoryManager -{ -private: - static minipal_mutex s_creationDeletionProcessLock; - static int s_creationDeletionLockFileDescriptor; - - struct UserScopeUidAndFileDescriptor - { - uid_t userScopeUid; - int fileDescriptor; - - UserScopeUidAndFileDescriptor() : userScopeUid((uid_t)0), fileDescriptor(-1) - { - } - - UserScopeUidAndFileDescriptor(uid_t userScopeUid, int fileDescriptor) - : userScopeUid(userScopeUid), fileDescriptor(fileDescriptor) - { - } - }; - - static UserScopeUidAndFileDescriptor *s_userScopeUidToCreationDeletionLockFDs; - static int s_userScopeUidToCreationDeletionLockFDsCount; - static int s_userScopeUidToCreationDeletionLockFDsCapacity; - -private: - static SharedMemoryProcessDataHeader *s_processDataHeaderListHead; - -#ifdef _DEBUG -private: - static SIZE_T s_creationDeletionProcessLockOwnerThreadId; - static SIZE_T s_creationDeletionFileLockOwnerThreadId; -#endif // _DEBUG - -public: - static void StaticInitialize(); - static void StaticClose(); - -public: - static void AcquireCreationDeletionProcessLock(); - static void ReleaseCreationDeletionProcessLock(); - static void AcquireCreationDeletionFileLock(SharedMemorySystemCallErrors *errors, const SharedMemoryId *id); - static void ReleaseCreationDeletionFileLock(const SharedMemoryId *id); - static void AddUserScopeUidCreationDeletionLockFD(uid_t userScopeUid, int creationDeletionLockFD); - static int FindUserScopeCreationDeletionLockFD(uid_t userScopeUid); - -#ifdef _DEBUG -public: - static bool IsCreationDeletionProcessLockAcquired(); - static bool IsCreationDeletionFileLockAcquired(); -#endif // _DEBUG - -public: - static void AddProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader); - static void RemoveProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader); - static SharedMemoryProcessDataHeader *FindProcessDataHeader(const SharedMemoryId *id); -}; - -#endif // !_PAL_SHARED_MEMORY_H_ diff --git a/src/coreclr/pal/src/include/pal/synchobjects.hpp b/src/coreclr/pal/src/include/pal/synchobjects.hpp index 089c5bbf0ac27d..5adb4ca34d7e23 100644 --- a/src/coreclr/pal/src/include/pal/synchobjects.hpp +++ b/src/coreclr/pal/src/include/pal/synchobjects.hpp @@ -113,8 +113,6 @@ namespace CorUnix Volatile m_lLocalSynchLockCount; LIST_ENTRY m_leOwnedObjsList; - NamedMutexProcessData *m_ownedNamedMutexListHead; - ThreadNativeWaitData m_tnwdNativeData; ThreadWaitInfo m_twiWaitInfo; @@ -168,12 +166,6 @@ namespace CorUnix void RemoveObjectFromOwnedList(POwnedObjectsListNode pooln); POwnedObjectsListNode RemoveFirstObjectFromOwnedList(void); - void AddOwnedNamedMutex(NamedMutexProcessData *processData); - void RemoveOwnedNamedMutex(NamedMutexProcessData *processData); - NamedMutexProcessData *RemoveFirstOwnedNamedMutex(); - bool OwnsNamedMutex(NamedMutexProcessData *processData); - bool OwnsAnyNamedMutex() const; - // The following methods provide access to the native wait lock for // those implementations that need a lock to protect the support for // native thread blocking (e.g.: pthread conditions) diff --git a/src/coreclr/pal/src/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp index ea24c25d6e5252..a1d5006a220050 100644 --- a/src/coreclr/pal/src/init/pal.cpp +++ b/src/coreclr/pal/src/init/pal.cpp @@ -24,7 +24,6 @@ SET_DEFAULT_DEBUG_CHANNEL(PAL); // some headers have code with asserts, so do th #include "../objmgr/listedobjectmanager.hpp" #include "pal/seh.hpp" #include "pal/palinternal.h" -#include "pal/sharedmemory.h" #include "pal/process.h" #include "../thread/procprivate.hpp" #include "pal/module.h" @@ -354,20 +353,6 @@ Initialize( goto CLEANUP0a; } - // The gSharedFilesPath is allocated dynamically so its destructor does not get - // called unexpectedly during cleanup - gSharedFilesPath = new(std::nothrow) PathCharString(); - if (gSharedFilesPath == nullptr) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - goto CLEANUP0a; - } - - if (INIT_SharedFilesPath() == FALSE) - { - goto CLEANUP0a; - } - fFirstTimeInit = true; InitializeDefaultStackSize(); @@ -407,8 +392,6 @@ Initialize( // we use large numbers of threads or have many open files. } - SharedMemoryManager::StaticInitialize(); - // // Initialize global process data // @@ -835,8 +818,6 @@ PALCommonCleanup() // CPalSynchMgrController::PrepareForShutdown(); - SharedMemoryManager::StaticClose(); - #ifdef _DEBUG PROCDumpThreadList(); #endif @@ -1089,63 +1070,3 @@ static LPWSTR INIT_GetCurrentEXEPath() return return_value; } - -/*++ -Function: - INIT_SharedFilesPath - -Abstract: - Initializes the shared application ---*/ -static BOOL INIT_SharedFilesPath(void) -{ -#ifdef __APPLE__ - // Store application group Id. It will be null if not set - gApplicationGroupId = getenv("DOTNET_SANDBOX_APPLICATION_GROUP_ID"); - - if (nullptr != gApplicationGroupId) - { - // Verify the length of the application group ID - gApplicationGroupIdLength = strlen(gApplicationGroupId); - if (gApplicationGroupIdLength > MAX_APPLICATION_GROUP_ID_LENGTH) - { - SetLastError(ERROR_BAD_LENGTH); - return FALSE; - } - - // In sandbox, all IPC files (locks, pipes) should be written to the application group - // container. There will be no write permissions to TEMP_DIRECTORY_PATH - if (!GetApplicationContainerFolder(*gSharedFilesPath, gApplicationGroupId, gApplicationGroupIdLength)) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } - - // Verify the size of the path won't exceed maximum allowed size - if (gSharedFilesPath->GetCount() + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ > MAX_LONGPATH) - { - SetLastError(ERROR_FILENAME_EXCED_RANGE); - return FALSE; - } - - // Check if the path already exists and it's a directory - struct stat statInfo; - int statResult = stat(*gSharedFilesPath, &statInfo); - - // If the path exists, check that it's a directory - if (statResult != 0 || !(statInfo.st_mode & S_IFDIR)) - { - SetLastError(ERROR_PATH_NOT_FOUND); - return FALSE; - } - - return TRUE; - } -#endif // __APPLE__ - - // If we are here, then we are not in sandbox mode, resort to TEMP_DIRECTORY_PATH as shared files path - return gSharedFilesPath->Set(TEMP_DIRECTORY_PATH); - - // We can verify statically the non sandboxed case, since the size is known during compile time - static_assert_no_msg(STRING_LENGTH(TEMP_DIRECTORY_PATH) + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH); -} diff --git a/src/coreclr/pal/src/misc/dbgmsg.cpp b/src/coreclr/pal/src/misc/dbgmsg.cpp index cc1213e8e73a6d..65b0539c80c093 100644 --- a/src/coreclr/pal/src/misc/dbgmsg.cpp +++ b/src/coreclr/pal/src/misc/dbgmsg.cpp @@ -23,6 +23,7 @@ Module Name: #include "pal/cruntime.h" #include "pal/file.h" #include "pal/environ.h" +#include /* standard headers */ diff --git a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp deleted file mode 100644 index 1ec85658594d5f..00000000000000 --- a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp +++ /dev/null @@ -1,1745 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "pal/dbgmsg.h" -SET_DEFAULT_DEBUG_CHANNEL(SHMEM); // some headers have code with asserts, so do this first - -#include "pal/sharedmemory.h" - -#include "pal/file.hpp" -#include "pal/thread.hpp" -#include "pal/virtual.h" -#include "pal/process.h" -#include "pal/utils.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace CorUnix; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// AutoFreeBuffer - -AutoFreeBuffer::AutoFreeBuffer(void *buffer) : m_buffer(buffer), m_cancel(false) -{ -} - -AutoFreeBuffer::~AutoFreeBuffer() -{ - if (!m_cancel && m_buffer != nullptr) - { - free(m_buffer); - } -} - -void AutoFreeBuffer::Cancel() -{ - m_cancel = true; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SharedMemoryException - -SharedMemoryException::SharedMemoryException(DWORD errorCode) : m_errorCode(errorCode) -{ -} - -DWORD SharedMemoryException::GetErrorCode() const -{ - return m_errorCode; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SharedMemorySystemCallErrors - -SharedMemorySystemCallErrors::SharedMemorySystemCallErrors(char *buffer, int bufferSize) - : m_buffer(buffer), m_bufferSize(bufferSize), m_length(0), m_isTracking(bufferSize != 0) -{ - _ASSERTE((buffer == nullptr) == (bufferSize == 0)); - _ASSERTE(bufferSize >= 0); -} - -void SharedMemorySystemCallErrors::Append(LPCSTR format, ...) -{ - if (!m_isTracking) - { - return; - } - - char *buffer = m_buffer; - _ASSERTE(buffer != nullptr); - int bufferSize = m_bufferSize; - _ASSERTE(bufferSize != 0); - int length = m_length; - _ASSERTE(length < bufferSize); - _ASSERTE(buffer[length] == '\0'); - if (length >= bufferSize - 1) - { - return; - } - - if (length != 0) - { - length++; // the previous null terminator will be changed to a space if the append succeeds - } - - va_list args; - va_start(args, format); - int result = _vsnprintf_s(buffer + length, bufferSize - length, bufferSize - 1 - length, format, args); - va_end(args); - - if (result == 0) - { - return; - } - - if (result < 0 || result >= bufferSize - length) - { - // There's not enough space to append this error, discard the append and stop tracking - if (length == 0) - { - buffer[0] = '\0'; - } - m_isTracking = false; - return; - } - - if (length != 0) - { - buffer[length - 1] = ' '; // change the previous null terminator to a space - } - - length += result; - _ASSERTE(buffer[length] == '\0'); - m_length = length; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SharedMemoryHelpers - -const mode_t SharedMemoryHelpers::PermissionsMask_OwnerUser_ReadWrite = S_IRUSR | S_IWUSR; -const mode_t SharedMemoryHelpers::PermissionsMask_OwnerUser_ReadWriteExecute = S_IRUSR | S_IWUSR | S_IXUSR; -const mode_t SharedMemoryHelpers::PermissionsMask_NonOwnerUsers_Write = S_IWGRP | S_IWOTH; -const mode_t SharedMemoryHelpers::PermissionsMask_AllUsers_ReadWrite = - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; -const mode_t SharedMemoryHelpers::PermissionsMask_AllUsers_ReadWriteExecute = - PermissionsMask_AllUsers_ReadWrite | (S_IXUSR | S_IXGRP | S_IXOTH); -const mode_t SharedMemoryHelpers::PermissionsMask_Sticky = S_ISVTX; -const UINT32 SharedMemoryHelpers::InvalidProcessId = static_cast(-1); -const SIZE_T SharedMemoryHelpers::InvalidThreadId = static_cast(-1); -const UINT64 SharedMemoryHelpers::InvalidSharedThreadId = static_cast(-1); - -void *SharedMemoryHelpers::Alloc(SIZE_T byteCount) -{ - void *buffer = malloc(byteCount != 0 ? byteCount : 1); - if (buffer == nullptr) - { - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); - } - return buffer; -} - -SIZE_T SharedMemoryHelpers::AlignDown(SIZE_T value, SIZE_T alignment) -{ - _ASSERTE((alignment & (alignment - 1)) == 0); // must be a power of 2 - return value & ~(alignment - 1); -} - -SIZE_T SharedMemoryHelpers::AlignUp(SIZE_T value, SIZE_T alignment) -{ - _ASSERTE((alignment & (alignment - 1)) == 0); // must be a power of 2 - return AlignDown(value + (alignment - 1), alignment); -} - -bool SharedMemoryHelpers::EnsureDirectoryExists( - SharedMemorySystemCallErrors *errors, - const char *path, - const SharedMemoryId *id, - bool isGlobalLockAcquired, - bool createIfNotExist, - bool isSystemDirectory) -{ - _ASSERTE(path != nullptr); - _ASSERTE(id != nullptr); - _ASSERTE(!(isSystemDirectory && createIfNotExist)); // should not create or change permissions on system directories - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!isGlobalLockAcquired || SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - - mode_t permissionsMask = - id->IsUserScope() ? PermissionsMask_OwnerUser_ReadWriteExecute : PermissionsMask_AllUsers_ReadWriteExecute; - - // Check if the path already exists - struct stat statInfo; - int statResult = stat(path, &statInfo); - if (statResult != 0 && errno == ENOENT) - { - if (!createIfNotExist) - { - return false; - } - - // The path does not exist, create the directory. The permissions mask passed to mkdir() is filtered by the process' - // permissions umask, so mkdir() may not set all of the requested permissions. We need to use chmod() to set the proper - // permissions. That creates a race when there is no global lock acquired when creating the directory. Another user's - // process may create the directory and this user's process may try to use it before the other process sets the full - // permissions. In that case, create a temporary directory first, set the permissions, and rename it to the actual - // directory name. - - if (isGlobalLockAcquired) - { - int operationResult = mkdir(path, permissionsMask); - if (operationResult != 0) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "mkdir(\"%s\", %s_ReadWriteExecute) == %d; errno == %s;", - path, - id->IsUserScope() ? "OwnerUser" : "AllUsers", - operationResult, - GetFriendlyErrorCodeString(errorCode)); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - operationResult = ChangeMode(path, permissionsMask); - if (operationResult != 0) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "chmod(\"%s\", %s_ReadWriteExecute) == %d; errno == %s;", - path, - id->IsUserScope() ? "OwnerUser" : "AllUsers", - operationResult, - GetFriendlyErrorCodeString(errorCode)); - } - - rmdir(path); - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - return true; - } - - PathCharString tempPath; - VerifyStringOperation(tempPath.Set(*gSharedFilesPath) && tempPath.Append(SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE)); - - if (mkdtemp(tempPath.OpenStringBuffer()) == nullptr) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "mkdtemp(\"%s\") == nullptr; errno == %s;", - (const char *)tempPath, - GetFriendlyErrorCodeString(errorCode)); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - int operationResult = ChangeMode(tempPath, permissionsMask); - if (operationResult != 0) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "chmod(\"%s\", %s_ReadWriteExecute) == %d; errno == %s;", - (const char *)tempPath, - id->IsUserScope() ? "OwnerUser" : "AllUsers", - operationResult, - GetFriendlyErrorCodeString(errorCode)); - } - - rmdir(tempPath); - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - if (rename(tempPath, path) == 0) - { - return true; - } - - // Another process may have beaten us to it. Delete the temp directory and continue to check the requested directory to - // see if it meets our needs. - rmdir(tempPath); - statResult = stat(path, &statInfo); - } - - // If the path exists, check that it's a directory - if (statResult != 0 || !(statInfo.st_mode & S_IFDIR)) - { - if (errors != nullptr) - { - if (statResult != 0) - { - int errorCode = errno; - errors->Append( - "stat(\"%s\", ...) == %d; errno == %s;", - path, - statResult, - GetFriendlyErrorCodeString(errorCode)); - } - else - { - errors->Append( - "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & 0x%x) == 0;", - path, - (int)statInfo.st_mode, - (int)S_IFDIR); - } - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - if (isSystemDirectory) - { - // For system directories (such as TEMP_DIRECTORY_PATH), require sufficient permissions only for the - // owner user. For instance, "docker run --mount ..." to mount /tmp to some directory on the host mounts the - // destination directory with the same permissions as the source directory, which may not include some permissions for - // other users. In the docker container, other user permissions are typically not relevant and relaxing the permissions - // requirement allows for that scenario to work without having to work around it by first giving sufficient permissions - // for all users. - // - // If the directory is being used for user-scoped shared memory data, also ensure that either it has the sticky bit or - // it's owned by the current user and without write access for other users. - permissionsMask = PermissionsMask_OwnerUser_ReadWriteExecute; - if ((statInfo.st_mode & permissionsMask) == permissionsMask && - ( - !id->IsUserScope() || - statInfo.st_mode & PermissionsMask_Sticky || - (statInfo.st_uid == id->GetUserScopeUid() && !(statInfo.st_mode & PermissionsMask_NonOwnerUsers_Write)) - )) - { - return true; - } - - if (errors != nullptr) - { - errors->Append( - "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; info.st_uid == %u; info.st_mode || info.st_uid;", - path, - (int)statInfo.st_mode, - (int)statInfo.st_uid); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - // For non-system directories (such as gSharedFilesPath/SHARED_MEMORY_USER_UNSCOPED_RUNTIME_TEMP_DIRECTORY_NAME), - // require the sufficient permissions and try to update them if requested to create the directory, so that - // shared memory files may be shared according to its scope. - - // For user-scoped directories, verify the owner UID - if (id->IsUserScope() && statInfo.st_uid != id->GetUserScopeUid()) - { - if (errors != nullptr) - { - errors->Append( - "stat(\"%s\", &info) == 0; info.st_uid == %u; info.st_uid != %u;", - path, - (int)statInfo.st_uid, - (int)id->GetUserScopeUid()); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - // Verify the permissions, or try to change them if possible - if ((statInfo.st_mode & PermissionsMask_AllUsers_ReadWriteExecute) == permissionsMask || - (createIfNotExist && ChangeMode(path, permissionsMask) == 0)) - { - return true; - } - - // We were not able to verify or set the necessary permissions. For user-scoped directories, this is treated as a failure - // since other users aren't sufficiently restricted in permissions. - if (id->IsUserScope()) - { - if (errors != nullptr) - { - errors->Append( - "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & AllUsers_ReadWriteExecute) != OwnerUser_ReadWriteExecute;", - path, - (int)statInfo.st_mode); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - // For user-unscoped directories, as a last resort, check that at least the owner user has full access. - permissionsMask = PermissionsMask_OwnerUser_ReadWriteExecute; - if ((statInfo.st_mode & permissionsMask) != permissionsMask) - { - if (errors != nullptr) - { - errors->Append( - "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & OwnerUser_ReadWriteExecute) != OwnerUser_ReadWriteExecute;", - path, - (int)statInfo.st_mode); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - return true; -} - -int SharedMemoryHelpers::Open(SharedMemorySystemCallErrors *errors, LPCSTR path, int flags, mode_t mode) -{ - int openErrorCode; - - flags |= O_CLOEXEC; - do - { - int fileDescriptor = InternalOpen(path, flags, mode); - if (fileDescriptor != -1) - { - return fileDescriptor; - } - openErrorCode = errno; - } while (openErrorCode == EINTR); - - SharedMemoryError sharedMemoryError; - switch (openErrorCode) - { - case ENOENT: - _ASSERTE(!(flags & O_CREAT)); - errno = openErrorCode; - return -1; - - case ENAMETOOLONG: - sharedMemoryError = SharedMemoryError::NameTooLong; - break; - - case EMFILE: - case ENFILE: - case ENOMEM: - sharedMemoryError = SharedMemoryError::OutOfMemory; - break; - - default: - sharedMemoryError = SharedMemoryError::IO; - break; - } - - if (sharedMemoryError != SharedMemoryError::NameTooLong && errors != nullptr) - { - errors->Append( - "open(\"%s\", 0x%x, 0x%x) == -1; errno == %s;", - path, - flags, - (int)mode, - GetFriendlyErrorCodeString(openErrorCode)); - } - - throw SharedMemoryException(static_cast(sharedMemoryError)); -} - -int SharedMemoryHelpers::OpenDirectory(SharedMemorySystemCallErrors *errors, LPCSTR path) -{ - _ASSERTE(path != nullptr); - _ASSERTE(path[0] != '\0'); - - int fileDescriptor = Open(errors, path, O_RDONLY); - _ASSERTE(fileDescriptor != -1 || errno == ENOENT); - return fileDescriptor; -} - -int SharedMemoryHelpers::CreateOrOpenFile( - SharedMemorySystemCallErrors *errors, - LPCSTR path, - const SharedMemoryId *id, - bool createIfNotExist, - bool *createdRef) -{ - _ASSERTE(path != nullptr); - _ASSERTE(path[0] != '\0'); - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!createIfNotExist || SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - - // Try to open the file - int openFlags = O_RDWR; - int fileDescriptor = Open(errors, path, openFlags); - if (fileDescriptor != -1) - { - // For user-scoped files, verify the owner UID and permissions - if (id->IsUserScope()) - { - struct stat statInfo; - int statResult = fstat(fileDescriptor, &statInfo); - if (statResult != 0) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "fstat(\"%s\", ...) == %d; errno == %s;", - path, - statResult, - GetFriendlyErrorCodeString(errorCode)); - } - - CloseFile(fileDescriptor); - throw SharedMemoryException((DWORD)SharedMemoryError::IO); - } - - if (statInfo.st_uid != id->GetUserScopeUid()) - { - if (errors != nullptr) - { - errors->Append( - "fstat(\"%s\", &info) == 0; info.st_uid == %u; info.st_uid != %u;", - path, - (int)statInfo.st_uid, - (int)id->GetUserScopeUid()); - } - - CloseFile(fileDescriptor); - throw SharedMemoryException((DWORD)SharedMemoryError::IO); - } - - if ((statInfo.st_mode & PermissionsMask_AllUsers_ReadWriteExecute) != PermissionsMask_OwnerUser_ReadWrite) - { - if (errors != nullptr) - { - errors->Append( - "fstat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & AllUsers_ReadWriteExecute) != OwnerUser_ReadWrite;", - path, - (int)statInfo.st_mode); - } - - CloseFile(fileDescriptor); - throw SharedMemoryException((DWORD)SharedMemoryError::IO); - } - } - - if (createdRef != nullptr) - { - *createdRef = false; - } - return fileDescriptor; - } - - _ASSERTE(errno == ENOENT); - if (!createIfNotExist) - { - if (createdRef != nullptr) - { - *createdRef = false; - } - return -1; - } - - // File does not exist, create the file - openFlags |= O_CREAT | O_EXCL; - mode_t permissionsMask = id->IsUserScope() ? PermissionsMask_OwnerUser_ReadWrite : PermissionsMask_AllUsers_ReadWrite; - fileDescriptor = Open(errors, path, openFlags, permissionsMask); - _ASSERTE(fileDescriptor != -1); - - // The permissions mask passed to open() is filtered by the process' permissions umask, so open() may not set all of - // the requested permissions. Use chmod() to set the proper permissions. - int operationResult = ChangeMode(path, permissionsMask); - if (operationResult != 0) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "chmod(\"%s\", %s_ReadWrite) == %d; errno == %s;", - path, - id->IsUserScope() ? "OwnerUser" : "AllUsers", - operationResult, - GetFriendlyErrorCodeString(errorCode)); - } - - CloseFile(fileDescriptor); - unlink(path); - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - if (createdRef != nullptr) - { - *createdRef = true; - } - return fileDescriptor; -} - -void SharedMemoryHelpers::CloseFile(int fileDescriptor) -{ - _ASSERTE(fileDescriptor != -1); - close(fileDescriptor); -} - -int SharedMemoryHelpers::ChangeMode(LPCSTR path, mode_t mode) -{ - _ASSERTE(path != nullptr); - _ASSERTE(path[0] != '\0'); - - int chmodResult; - do - { - chmodResult = chmod(path, mode); - } while (chmodResult != 0 && errno == EINTR); - - return chmodResult; -} - -SIZE_T SharedMemoryHelpers::GetFileSize(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor) -{ - _ASSERTE(filePath != nullptr); - _ASSERTE(filePath[0] != '\0'); - _ASSERTE(fileDescriptor != -1); - - off_t endOffset = lseek(fileDescriptor, 0, SEEK_END); - if (endOffset == static_cast(-1) || - lseek(fileDescriptor, 0, SEEK_SET) == static_cast(-1)) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "lseek(\"%s\", 0, %s) == -1; errno == %s;", - filePath, - endOffset == (off_t)-1 ? "SEEK_END" : "SEEK_SET", - GetFriendlyErrorCodeString(errorCode)); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - return endOffset; -} - -void SharedMemoryHelpers::SetFileSize( - SharedMemorySystemCallErrors *errors, - LPCSTR filePath, - int fileDescriptor, - SIZE_T byteCount) -{ - _ASSERTE(filePath != nullptr); - _ASSERTE(filePath[0] != '\0'); - _ASSERTE(fileDescriptor != -1); - _ASSERTE(static_cast(byteCount) == byteCount); - - while (true) - { - int ftruncateResult = ftruncate(fileDescriptor, static_cast(byteCount)); - if (ftruncateResult == 0) - { - break; - } - - int errorCode = errno; - if (errorCode != EINTR) - { - if (errors != nullptr) - { - errors->Append( - "ftruncate(\"%s\", %zu) == %d; errno == %s;", - filePath, - byteCount, - ftruncateResult, - GetFriendlyErrorCodeString(errorCode)); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - } -} - -void *SharedMemoryHelpers::MemoryMapFile( - SharedMemorySystemCallErrors *errors, - LPCSTR filePath, - int fileDescriptor, - SIZE_T byteCount) -{ - _ASSERTE(filePath != nullptr); - _ASSERTE(filePath[0] != '\0'); - _ASSERTE(fileDescriptor != -1); - _ASSERTE(byteCount > sizeof(SharedMemorySharedDataHeader)); - _ASSERTE(AlignDown(byteCount, GetVirtualPageSize()) == byteCount); - - void *sharedMemoryBuffer = mmap(nullptr, byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fileDescriptor, 0); - if (sharedMemoryBuffer != MAP_FAILED) - { - return sharedMemoryBuffer; - } - - int errorCode = errno; - SharedMemoryError sharedMemoryError; - switch (errorCode) - { - case EMFILE: - case ENFILE: - case ENOMEM: - sharedMemoryError = SharedMemoryError::OutOfMemory; - break; - - default: - sharedMemoryError = SharedMemoryError::IO; - break; - } - - if (errors != nullptr) - { - errors->Append( - "mmap(nullptr, %zu, PROT_READ | PROT_WRITE, MAP_SHARED, \"%s\", 0) == MAP_FAILED; errno == %s;", - byteCount, - filePath, - GetFriendlyErrorCodeString(errorCode)); - } - - throw SharedMemoryException(static_cast(sharedMemoryError)); -} - -bool SharedMemoryHelpers::TryAcquireFileLock(SharedMemorySystemCallErrors *errors, int fileDescriptor, int operation) -{ - // A file lock is acquired once per file descriptor, so the caller will need to synchronize threads of this process - - _ASSERTE(fileDescriptor != -1); - _ASSERTE((operation & LOCK_EX) ^ (operation & LOCK_SH)); - _ASSERTE(!(operation & LOCK_UN)); - - while (true) - { - int flockResult = flock(fileDescriptor, operation); - if (flockResult == 0) - { - return true; - } - - int flockError = errno; - SharedMemoryError sharedMemoryError = SharedMemoryError::IO; - switch (flockError) - { - case EWOULDBLOCK: - return false; - - case EINTR: - continue; - - case ENOLCK: - sharedMemoryError = SharedMemoryError::OutOfMemory; - break; - } - - if (errors != nullptr) - { - errors->Append( - "flock(%d, %s%s) == %d; errno == %s;", - fileDescriptor, - operation & LOCK_EX ? "LOCK_EX" : "LOCK_SH", - operation & LOCK_NB ? " | LOCK_NB" : "", - flockResult, - GetFriendlyErrorCodeString(flockError)); - } - - throw SharedMemoryException(static_cast(sharedMemoryError)); - } -} - -void SharedMemoryHelpers::ReleaseFileLock(int fileDescriptor) -{ - _ASSERTE(fileDescriptor != -1); - - int flockResult; - do - { - flockResult = flock(fileDescriptor, LOCK_UN); - } while (flockResult != 0 && errno == EINTR); -} - -bool SharedMemoryHelpers::AppendUInt32String( - PathCharString& destination, - UINT32 value) -{ - char int32String[16]; - - int valueCharCount = - sprintf_s(int32String, sizeof(int32String), "%u", value); - _ASSERTE(valueCharCount > 0); - return destination.Append(int32String, valueCharCount) != FALSE; -} - -void SharedMemoryHelpers::VerifyStringOperation(bool success) -{ - if (!success) - { - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SharedMemoryId - -SharedMemoryId::SharedMemoryId() - : m_name(nullptr), m_nameCharCount(0), m_isSessionScope(false), m_isUserScope(false), m_userScopeUid((uid_t)0) -{ -} - -SharedMemoryId::SharedMemoryId(LPCSTR name, bool isUserScope) -{ - _ASSERTE(name != nullptr); - - // Look for "Global\" and "Local\" prefixes in the name, and determine the session ID - if (strncmp(name, "Global\\", 7) == 0) - { - m_isSessionScope = false; - name += STRING_LENGTH("Global\\"); - } - else - { - if (strncmp(name, "Local\\", 6) == 0) - { - name += STRING_LENGTH("Local\\"); - } - m_isSessionScope = true; - } - m_name = name; - - m_nameCharCount = strlen(name); - if (m_nameCharCount == 0) - { - throw SharedMemoryException(static_cast(SharedMemoryError::NameEmpty)); - } - if (m_nameCharCount > SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT) - { - throw SharedMemoryException(static_cast(SharedMemoryError::NameTooLong)); - } - - // Look for invalid characters '\' and '/' in the name - for (SIZE_T i = 0; i < m_nameCharCount; ++i) - { - char c = name[i]; - if (c == '\\' || c == '/') - { - throw SharedMemoryException(static_cast(SharedMemoryError::NameInvalid)); - } - } - - m_isUserScope = isUserScope; - m_userScopeUid = isUserScope ? geteuid() : (uid_t)0; - - // The uid_t is converted to UINT32 to create a directory name, verify that it's valid - static_assert_no_msg(sizeof(uid_t) <= sizeof(UINT32)); - if ((uid_t)(UINT32)m_userScopeUid != m_userScopeUid) - { - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } -} - -LPCSTR SharedMemoryId::GetName() const -{ - _ASSERTE(m_name != nullptr); - return m_name; -} - -SIZE_T SharedMemoryId::GetNameCharCount() const -{ - _ASSERTE(m_name != nullptr); - return m_nameCharCount; -} - -void SharedMemoryId::ReplaceNamePtr(LPCSTR name) -{ - _ASSERTE(name != nullptr); - _ASSERTE(m_nameCharCount != 0); - _ASSERTE(strlen(name) == m_nameCharCount); - - m_name = name; -} - -bool SharedMemoryId::IsSessionScope() const -{ - _ASSERTE(m_name != nullptr); - return m_isSessionScope; -} - -bool SharedMemoryId::IsUserScope() const -{ - _ASSERTE(m_name != nullptr); - return m_isUserScope; -} - -uid_t SharedMemoryId::GetUserScopeUid() const -{ - _ASSERTE(m_name != nullptr); - _ASSERTE(m_isUserScope); - return m_userScopeUid; -} - -bool SharedMemoryId::Equals(const SharedMemoryId *other) const -{ - return - GetNameCharCount() == other->GetNameCharCount() && - IsSessionScope() == other->IsSessionScope() && - IsUserScope() == other->IsUserScope() && - (!IsUserScope() || GetUserScopeUid() == other->GetUserScopeUid()) && - strcmp(GetName(), other->GetName()) == 0; -} - -bool SharedMemoryId::AppendRuntimeTempDirectoryName(PathCharString& path) const -{ - if (IsUserScope()) - { - return - path.Append(SHARED_MEMORY_USER_SCOPED_RUNTIME_TEMP_DIRECTORY_NAME_PREFIX) && - SharedMemoryHelpers::AppendUInt32String(path, (UINT32)GetUserScopeUid()); - } - - return path.Append(SHARED_MEMORY_USER_UNSCOPED_RUNTIME_TEMP_DIRECTORY_NAME); -} - -bool SharedMemoryId::AppendSessionDirectoryName(PathCharString& path) const -{ - if (IsSessionScope()) - { - return path.Append(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) != FALSE - && SharedMemoryHelpers::AppendUInt32String(path, GetCurrentSessionId()); - } - else - { - return path.Append(SHARED_MEMORY_GLOBAL_DIRECTORY_NAME) != FALSE; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SharedMemorySharedDataHeader - -SIZE_T SharedMemorySharedDataHeader::GetUsedByteCount(SIZE_T dataByteCount) -{ - return sizeof(SharedMemorySharedDataHeader) + dataByteCount; -} - -SIZE_T SharedMemorySharedDataHeader::GetTotalByteCount(SIZE_T dataByteCount) -{ - return SharedMemoryHelpers::AlignUp(GetUsedByteCount(dataByteCount), GetVirtualPageSize()); -} - -SharedMemorySharedDataHeader::SharedMemorySharedDataHeader(SharedMemoryType type, UINT8 version) - : m_type(type), m_version(version) -{ -} - -SharedMemoryType SharedMemorySharedDataHeader::GetType() const -{ - return m_type; -} - -UINT8 SharedMemorySharedDataHeader::GetVersion() const -{ - return m_version; -} - -void *SharedMemorySharedDataHeader::GetData() -{ - return this + 1; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SharedMemoryProcessDataHeader - -SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen( - SharedMemorySystemCallErrors *errors, - LPCSTR name, - bool isUserScope, - SharedMemorySharedDataHeader requiredSharedDataHeader, - SIZE_T sharedDataByteCount, - bool createIfNotExist, - bool *createdRef) -{ - _ASSERTE(name != nullptr); - _ASSERTE(sharedDataByteCount != 0); - _ASSERTE(!createIfNotExist || createdRef != nullptr); - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - - if (createdRef != nullptr) - { - *createdRef = false; - } - - PathCharString filePath; - SharedMemoryId id(name, isUserScope); - - struct AutoCleanup - { - const SharedMemoryId *m_acquiredCreationDeletionFileLockForId; - PathCharString *m_filePath; - SIZE_T m_sessionDirectoryPathCharCount; - bool m_createdFile; - int m_fileDescriptor; - bool m_acquiredFileLock; - void *m_mappedBuffer; - SIZE_T m_mappedBufferByteCount; - bool m_cancel; - - AutoCleanup() - : m_acquiredCreationDeletionFileLockForId(nullptr), - m_filePath(nullptr), - m_sessionDirectoryPathCharCount(0), - m_createdFile(false), - m_fileDescriptor(-1), - m_acquiredFileLock(false), - m_mappedBuffer(nullptr), - m_mappedBufferByteCount(0), - m_cancel(false) - { - } - - ~AutoCleanup() - { - if (m_cancel) - { - return; - } - - if (m_mappedBuffer != nullptr) - { - _ASSERTE(m_mappedBufferByteCount != 0); - munmap(m_mappedBuffer, m_mappedBufferByteCount); - } - - if (m_acquiredFileLock) - { - _ASSERTE(m_fileDescriptor != -1); - SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor); - } - - if (m_fileDescriptor != -1) - { - SharedMemoryHelpers::CloseFile(m_fileDescriptor); - } - - if (m_createdFile) - { - _ASSERTE(m_filePath != nullptr); - unlink(*m_filePath); - } - - if (m_sessionDirectoryPathCharCount != 0) - { - _ASSERTE(*m_filePath != nullptr); - m_filePath->CloseBuffer(m_sessionDirectoryPathCharCount); - rmdir(*m_filePath); - } - - if (m_acquiredCreationDeletionFileLockForId != nullptr) - { - SharedMemoryManager::ReleaseCreationDeletionFileLock(m_acquiredCreationDeletionFileLockForId); - } - } - } autoCleanup; - - SharedMemoryProcessDataHeader *processDataHeader = SharedMemoryManager::FindProcessDataHeader(&id); - if (processDataHeader != nullptr) - { - _ASSERTE( - processDataHeader->GetSharedDataTotalByteCount() == - SharedMemorySharedDataHeader::GetTotalByteCount(sharedDataByteCount)); - processDataHeader->IncRefCount(); - return processDataHeader; - } - - SharedMemoryManager::AcquireCreationDeletionFileLock(errors, &id); - autoCleanup.m_acquiredCreationDeletionFileLockForId = &id; - - // Create the session directory - SharedMemoryHelpers::VerifyStringOperation( - filePath.Set(*gSharedFilesPath) && - id.AppendRuntimeTempDirectoryName(filePath) && - filePath.Append('/') && filePath.Append(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME) && - filePath.Append('/') && id.AppendSessionDirectoryName(filePath)); - if (!SharedMemoryHelpers::EnsureDirectoryExists(errors, filePath, &id, true /* isGlobalLockAcquired */, createIfNotExist)) - { - _ASSERTE(!createIfNotExist); - return nullptr; - } - autoCleanup.m_filePath = &filePath; - autoCleanup.m_sessionDirectoryPathCharCount = filePath.GetCount(); - - // Create or open the shared memory file - SharedMemoryHelpers::VerifyStringOperation(filePath.Append('/') && filePath.Append(id.GetName(), id.GetNameCharCount())); - bool createdFile; - int fileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(errors, filePath, &id, createIfNotExist, &createdFile); - if (fileDescriptor == -1) - { - _ASSERTE(!createIfNotExist); - return nullptr; - } - autoCleanup.m_createdFile = createdFile; - autoCleanup.m_fileDescriptor = fileDescriptor; - - bool clearContents = false; - if (!createdFile) - { - // A shared file lock on the shared memory file would be held by any process that has opened the same file. Try to take - // an exclusive lock on the file. Successfully acquiring an exclusive lock indicates that no process has a reference to - // the shared memory file, and this process can reinitialize its contents. - if (SharedMemoryHelpers::TryAcquireFileLock(errors, fileDescriptor, LOCK_EX | LOCK_NB)) - { - // The shared memory file is not being used, flag it as created so that its contents will be reinitialized - SharedMemoryHelpers::ReleaseFileLock(fileDescriptor); - autoCleanup.m_createdFile = true; - if (!createIfNotExist) - { - return nullptr; - } - createdFile = true; - clearContents = true; - } - } - - // Set or validate the file length - SIZE_T sharedDataUsedByteCount = SharedMemorySharedDataHeader::GetUsedByteCount(sharedDataByteCount); - SIZE_T sharedDataTotalByteCount = SharedMemorySharedDataHeader::GetTotalByteCount(sharedDataByteCount); - if (createdFile) - { - SharedMemoryHelpers::SetFileSize(errors, filePath, fileDescriptor, sharedDataTotalByteCount); - } - else - { - SIZE_T currentFileSize = SharedMemoryHelpers::GetFileSize(errors, filePath, fileDescriptor); - if (currentFileSize < sharedDataUsedByteCount) - { - throw SharedMemoryException(static_cast(SharedMemoryError::HeaderMismatch)); - } - if (currentFileSize < sharedDataTotalByteCount) - { - SharedMemoryHelpers::SetFileSize(errors, filePath, fileDescriptor, sharedDataTotalByteCount); - } - } - - // Acquire and hold a shared file lock on the shared memory file as long as it is open, to indicate that this process is - // using the file. An exclusive file lock is attempted above to detect whether the file contents are valid, for the case - // where a process crashes or is killed after the file is created. Since we already hold the creation/deletion locks, a - // non-blocking file lock should succeed. - if (!SharedMemoryHelpers::TryAcquireFileLock(errors, fileDescriptor, LOCK_SH | LOCK_NB)) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "flock(\"%s\", LOCK_SH | LOCK_NB) == -1; errno == %s;", - (const char *)filePath, - GetFriendlyErrorCodeString(errorCode)); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - autoCleanup.m_acquiredFileLock = true; - - // Map the file into memory, and initialize or validate the header - void *mappedBuffer = SharedMemoryHelpers::MemoryMapFile(errors, filePath, fileDescriptor, sharedDataTotalByteCount); - autoCleanup.m_mappedBuffer = mappedBuffer; - autoCleanup.m_mappedBufferByteCount = sharedDataTotalByteCount; - SharedMemorySharedDataHeader *sharedDataHeader; - if (createdFile) - { - if (clearContents) - { - memset(mappedBuffer, 0, sharedDataUsedByteCount); - } - sharedDataHeader = new(mappedBuffer) SharedMemorySharedDataHeader(requiredSharedDataHeader); - } - else - { - sharedDataHeader = reinterpret_cast(mappedBuffer); - if (sharedDataHeader->GetType() != requiredSharedDataHeader.GetType() || - sharedDataHeader->GetVersion() != requiredSharedDataHeader.GetVersion()) - { - throw SharedMemoryException(static_cast(SharedMemoryError::HeaderMismatch)); - } - } - - // When *createdRef is true, the creation/deletion file lock will remain locked upon returning for the caller to initialize - // the shared data. The caller must release the file lock afterwards. - if (!createdFile) - { - autoCleanup.m_acquiredCreationDeletionFileLockForId = nullptr; - SharedMemoryManager::ReleaseCreationDeletionFileLock(&id); - } - - processDataHeader = SharedMemoryProcessDataHeader::New(&id, fileDescriptor, sharedDataHeader, sharedDataTotalByteCount); - - autoCleanup.m_cancel = true; - if (createdFile) - { - _ASSERTE(createIfNotExist); - _ASSERTE(createdRef != nullptr); - *createdRef = true; - } - return processDataHeader; -} - -SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(CorUnix::IPalObject *object) -{ - _ASSERTE(object != nullptr); - _ASSERTE(object->GetObjectType()->GetId() == otiNamedMutex); - _ASSERTE(object->GetObjectType()->GetImmutableDataSize() == sizeof(SharedMemoryProcessDataHeader *)); - - void *immutableDataBuffer; - PAL_ERROR errorCode = object->GetImmutableData(&immutableDataBuffer); - _ASSERTE(errorCode == NO_ERROR); - _ASSERTE(immutableDataBuffer != nullptr); - return *reinterpret_cast(immutableDataBuffer); -} - -void SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader( - CorUnix::IPalObject *object, - SharedMemoryProcessDataHeader *processDataHeader) -{ - _ASSERTE(object != nullptr); - _ASSERTE(object->GetObjectType()->GetId() == otiNamedMutex); - _ASSERTE(object->GetObjectType()->GetImmutableDataSize() == sizeof(SharedMemoryProcessDataHeader *)); - _ASSERTE(processDataHeader != nullptr); - - void *immutableDataBuffer; - PAL_ERROR errorCode = object->GetImmutableData(&immutableDataBuffer); - _ASSERTE(errorCode == NO_ERROR); - _ASSERTE(immutableDataBuffer != nullptr); - *reinterpret_cast(immutableDataBuffer) = processDataHeader; -} - -void SharedMemoryProcessDataHeader::PalObject_Close( - CPalThread *thread, - IPalObject *object, - bool isShuttingDown) -{ - // This function's signature matches OBJECTCLEANUPROUTINE - _ASSERTE(thread != nullptr); - _ASSERTE(object != nullptr); - _ASSERTE(object->GetObjectType()->GetId() == otiNamedMutex); - _ASSERTE(object->GetObjectType()->GetImmutableDataSize() == sizeof(SharedMemoryProcessDataHeader *)); - - SharedMemoryProcessDataHeader *processDataHeader = PalObject_GetProcessDataHeader(object); - if (processDataHeader == nullptr) - { - // The object was created, but an error must have occurred before the process data was initialized - return; - } - - SharedMemoryManager::AcquireCreationDeletionProcessLock(); - processDataHeader->DecRefCount(); - SharedMemoryManager::ReleaseCreationDeletionProcessLock(); -} - -SharedMemoryProcessDataHeader::SharedMemoryProcessDataHeader( - const SharedMemoryId *id, - int fileDescriptor, - SharedMemorySharedDataHeader *sharedDataHeader, - SIZE_T sharedDataTotalByteCount) - : - m_refCount(1), - m_id(*id), - m_data(nullptr), - m_fileDescriptor(fileDescriptor), - m_sharedDataHeader(sharedDataHeader), - m_sharedDataTotalByteCount(sharedDataTotalByteCount), - m_nextInProcessDataHeaderList(nullptr) -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(id != nullptr); - _ASSERTE(fileDescriptor != -1); - _ASSERTE(sharedDataHeader != nullptr); - _ASSERTE(sharedDataTotalByteCount > sizeof(SharedMemorySharedDataHeader)); - _ASSERTE(SharedMemoryHelpers::AlignDown(sharedDataTotalByteCount, GetVirtualPageSize()) == sharedDataTotalByteCount); - - // Copy the name and initialize the ID - char *nameCopy = reinterpret_cast(this + 1); - SIZE_T nameByteCount = id->GetNameCharCount() + 1; - memcpy_s(nameCopy, nameByteCount, id->GetName(), nameByteCount); - m_id.ReplaceNamePtr(nameCopy); - - SharedMemoryManager::AddProcessDataHeader(this); -} - -SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::New( - const SharedMemoryId *id, - int fileDescriptor, - SharedMemorySharedDataHeader *sharedDataHeader, - SIZE_T sharedDataTotalByteCount) -{ - _ASSERTE(id != nullptr); - - // Allocate space for the header and a copy of the name - SIZE_T nameByteCount = id->GetNameCharCount() + 1; - SIZE_T totalByteCount = sizeof(SharedMemoryProcessDataHeader) + nameByteCount; - void *buffer = SharedMemoryHelpers::Alloc(totalByteCount); - AutoFreeBuffer autoFreeBuffer(buffer); - SharedMemoryProcessDataHeader *processDataHeader = - new(buffer) SharedMemoryProcessDataHeader(id, fileDescriptor, sharedDataHeader, sharedDataTotalByteCount); - autoFreeBuffer.Cancel(); - return processDataHeader; -} - -SharedMemoryProcessDataHeader::~SharedMemoryProcessDataHeader() -{ - _ASSERTE(m_refCount == 0); - Close(); -} - -void SharedMemoryProcessDataHeader::Close() -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - - // If the ref count is nonzero, we are shutting down the process abruptly without having closed some shared memory objects. - // There could still be threads running with active references to the shared memory object. So when the ref count is - // nonzero, don't clean up any object or global process-local state. - if (m_refCount == 0) - { - _ASSERTE(m_data == nullptr || m_data->CanClose()); - SharedMemoryManager::RemoveProcessDataHeader(this); - } - - struct AutoReleaseCreationDeletionFileLock - { - const SharedMemoryId *m_acquiredForId; - - AutoReleaseCreationDeletionFileLock() : m_acquiredForId(nullptr) - { - } - - ~AutoReleaseCreationDeletionFileLock() - { - if (m_acquiredForId != nullptr) - { - SharedMemoryManager::ReleaseCreationDeletionFileLock(m_acquiredForId); - } - } - } autoReleaseCreationDeletionFileLock; - - // A shared file lock on the shared memory file would be held by any process that has opened the same file. Try to take - // an exclusive lock on the file. Successfully acquiring an exclusive lock indicates that no process has a reference to - // the shared memory file, and this process can delete the file. File locks on the shared memory file are only ever acquired - // or released while holding the creation/deletion locks, so holding the creation/deletion locks while trying an exclusive - // lock on the shared memory file guarantees that another process cannot start using the shared memory file after this - // process has decided to delete the file. - bool releaseSharedData = false; - try - { - SharedMemoryManager::AcquireCreationDeletionFileLock(nullptr, GetId()); - autoReleaseCreationDeletionFileLock.m_acquiredForId = GetId(); - - SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor); - if (SharedMemoryHelpers::TryAcquireFileLock(nullptr, m_fileDescriptor, LOCK_EX | LOCK_NB)) - { - SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor); - releaseSharedData = true; - } - } - catch (SharedMemoryException) - { - // Ignore the error, just don't release shared data - } - - if (m_data != nullptr) - { - m_data->Close(m_refCount != 0 /* isAbruptShutdown */, releaseSharedData); - } - - if (m_refCount == 0) - { - if (m_data != nullptr) - { - delete m_data; - } - - if (releaseSharedData) - { - m_sharedDataHeader->~SharedMemorySharedDataHeader(); - } - - munmap(m_sharedDataHeader, m_sharedDataTotalByteCount); - SharedMemoryHelpers::CloseFile(m_fileDescriptor); - } - - if (!releaseSharedData) - { - return; - } - - try - { - // Delete the shared memory file, and the session directory if it's not empty - PathCharString path; - SharedMemoryHelpers::VerifyStringOperation( - path.Set(*gSharedFilesPath) && - m_id.AppendRuntimeTempDirectoryName(path) && - path.Append('/') && path.Append(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME) && - path.Append('/') && m_id.AppendSessionDirectoryName(path) && - path.Append('/')); - SIZE_T sessionDirectoryPathCharCount = path.GetCount(); - SharedMemoryHelpers::VerifyStringOperation(path.Append(m_id.GetName(), m_id.GetNameCharCount())); - unlink(path); - path.CloseBuffer(sessionDirectoryPathCharCount); - rmdir(path); - } - catch (SharedMemoryException) - { - // Ignore the error, just don't release shared data - } -} - -const SharedMemoryId *SharedMemoryProcessDataHeader::GetId() const -{ - return &m_id; -} - -SharedMemoryProcessDataBase *SharedMemoryProcessDataHeader::GetData() const -{ - return m_data; -} - -void SharedMemoryProcessDataHeader::SetData(SharedMemoryProcessDataBase *data) -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(m_data == nullptr); - _ASSERTE(data != nullptr); - - m_data = data; -} - -SharedMemorySharedDataHeader *SharedMemoryProcessDataHeader::GetSharedDataHeader() const -{ - return m_sharedDataHeader; -} - -SIZE_T SharedMemoryProcessDataHeader::GetSharedDataTotalByteCount() const -{ - return m_sharedDataTotalByteCount; -} - -SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::GetNextInProcessDataHeaderList() const -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - return m_nextInProcessDataHeaderList; -} - -void SharedMemoryProcessDataHeader::SetNextInProcessDataHeaderList(SharedMemoryProcessDataHeader *next) -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - m_nextInProcessDataHeaderList = next; -} - -void SharedMemoryProcessDataHeader::IncRefCount() -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(m_refCount != 0); - - if (++m_refCount == 2 && m_data != nullptr && m_data->HasImplicitRef()) - { - // The synchronization object got an explicit ref that will govern its lifetime, remove the implicit ref - --m_refCount; - m_data->SetHasImplicitRef(false); - } -} - -void SharedMemoryProcessDataHeader::DecRefCount() -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(m_refCount != 0); - - if (--m_refCount != 0) - { - return; - } - - if (m_data != nullptr && !m_data->CanClose()) - { - // Extend the lifetime of the synchronization object. The process data object is responsible for removing this extra ref - // when the synchronization object transitions into a state where it can be closed. - ++m_refCount; - m_data->SetHasImplicitRef(true); - return; - } - - delete this; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// SharedMemoryManager - -minipal_mutex SharedMemoryManager::s_creationDeletionProcessLock; -int SharedMemoryManager::s_creationDeletionLockFileDescriptor = -1; - -SharedMemoryManager::UserScopeUidAndFileDescriptor *SharedMemoryManager::s_userScopeUidToCreationDeletionLockFDs; -int SharedMemoryManager::s_userScopeUidToCreationDeletionLockFDsCount; -int SharedMemoryManager::s_userScopeUidToCreationDeletionLockFDsCapacity; - -SharedMemoryProcessDataHeader *SharedMemoryManager::s_processDataHeaderListHead = nullptr; - -#ifdef _DEBUG -SIZE_T SharedMemoryManager::s_creationDeletionProcessLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId; -SIZE_T SharedMemoryManager::s_creationDeletionFileLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId; -#endif // _DEBUG - -void SharedMemoryManager::StaticInitialize() -{ - minipal_mutex_init(&s_creationDeletionProcessLock); -} - -void SharedMemoryManager::StaticClose() -{ - // This function could very well be running during abrupt shutdown, and there could still be user threads running. - // Synchronize the deletion, and don't remove or delete items in the linked list. - AcquireCreationDeletionProcessLock(); - for (SharedMemoryProcessDataHeader *current = s_processDataHeaderListHead; - current != nullptr; - current = current->GetNextInProcessDataHeaderList()) - { - current->Close(); - } - ReleaseCreationDeletionProcessLock(); - - // This function could very well be running during abrupt shutdown, and there could still be user threads running. Don't - // delete the creation/deletion process lock, the process is shutting down anyway. -} - -void SharedMemoryManager::AcquireCreationDeletionProcessLock() -{ - _ASSERTE(!IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!IsCreationDeletionFileLockAcquired()); - - minipal_mutex_enter(&s_creationDeletionProcessLock); -#ifdef _DEBUG - s_creationDeletionProcessLockOwnerThreadId = THREADSilentGetCurrentThreadId(); -#endif // _DEBUG -} - -void SharedMemoryManager::ReleaseCreationDeletionProcessLock() -{ - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!IsCreationDeletionFileLockAcquired()); - -#ifdef _DEBUG - s_creationDeletionProcessLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId; -#endif // _DEBUG - minipal_mutex_leave(&s_creationDeletionProcessLock); -} - -void SharedMemoryManager::AcquireCreationDeletionFileLock(SharedMemorySystemCallErrors *errors, const SharedMemoryId *id) -{ - _ASSERTE(id != nullptr); - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!IsCreationDeletionFileLockAcquired()); - - int creationDeletionLockFD = - id->IsUserScope() ? FindUserScopeCreationDeletionLockFD(id->GetUserScopeUid()) : s_creationDeletionLockFileDescriptor; - if (creationDeletionLockFD == -1) - { - // Create the shared files directory - PathCharString dirPath; - SharedMemoryHelpers::VerifyStringOperation(dirPath.Set(*gSharedFilesPath)); - if (!SharedMemoryHelpers::EnsureDirectoryExists( - errors, - dirPath, - id, - false /* isGlobalLockAcquired */, - false /* createIfNotExist */, - true /* isSystemDirectory */)) - { - _ASSERTE(errno == ENOENT); - if (errors != nullptr) - { - errors->Append("stat(\"%s\", ...) == -1; errno == ENOENT;", (const char *)*gSharedFilesPath); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - // Create the runtime temp directory - SharedMemoryHelpers::VerifyStringOperation(id->AppendRuntimeTempDirectoryName(dirPath)); - SharedMemoryHelpers::EnsureDirectoryExists(errors, dirPath, id, false /* isGlobalLockAcquired */); - - // Create the shared memory directory - SharedMemoryHelpers::VerifyStringOperation( - dirPath.Append('/') && dirPath.Append(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME)); - SharedMemoryHelpers::EnsureDirectoryExists(errors, dirPath, id, false /* isGlobalLockAcquired */); - - // Open the shared memory directory - creationDeletionLockFD = SharedMemoryHelpers::OpenDirectory(errors, dirPath); - if (creationDeletionLockFD == -1) - { - if (errors != nullptr) - { - int errorCode = errno; - errors->Append( - "open(\"%s\", O_RDONLY | O_CLOEXEC, 0) == -1; errno == %s;", - (const char *)dirPath, - GetFriendlyErrorCodeString(errorCode)); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - if (id->IsUserScope()) - { - AddUserScopeUidCreationDeletionLockFD(id->GetUserScopeUid(), creationDeletionLockFD); - } - else - { - s_creationDeletionLockFileDescriptor = creationDeletionLockFD; - } - } - - bool acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(errors, creationDeletionLockFD, LOCK_EX); - _ASSERTE(acquiredFileLock); -#ifdef _DEBUG - s_creationDeletionFileLockOwnerThreadId = THREADSilentGetCurrentThreadId(); -#endif // _DEBUG -} - -void SharedMemoryManager::ReleaseCreationDeletionFileLock(const SharedMemoryId *id) -{ - _ASSERTE(id != nullptr); - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - _ASSERTE(IsCreationDeletionFileLockAcquired()); - - int creationDeletionLockFD = - id->IsUserScope() ? FindUserScopeCreationDeletionLockFD(id->GetUserScopeUid()) : s_creationDeletionLockFileDescriptor; - _ASSERTE(creationDeletionLockFD != -1); - -#ifdef _DEBUG - s_creationDeletionFileLockOwnerThreadId = SharedMemoryHelpers::InvalidThreadId; -#endif // _DEBUG - SharedMemoryHelpers::ReleaseFileLock(creationDeletionLockFD); -} - -void SharedMemoryManager::AddUserScopeUidCreationDeletionLockFD(uid_t userScopeUid, int creationDeletionLockFD) -{ - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - _ASSERTE(creationDeletionLockFD != -1); - _ASSERTE(FindUserScopeCreationDeletionLockFD(userScopeUid) == -1); - - int count = s_userScopeUidToCreationDeletionLockFDsCount; - int capacity = s_userScopeUidToCreationDeletionLockFDsCapacity; - if (count >= capacity) - { - int newCapacity = capacity == 0 ? 1 : capacity * 2; - if (newCapacity <= capacity || - newCapacity * sizeof(UserScopeUidAndFileDescriptor) / sizeof(UserScopeUidAndFileDescriptor) != (SIZE_T)newCapacity) - { - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); - } - - UserScopeUidAndFileDescriptor *newArray = new(std::nothrow) UserScopeUidAndFileDescriptor[newCapacity]; - if (newArray == nullptr) - { - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); - } - - if (count != 0) - { - UserScopeUidAndFileDescriptor *oldArray = s_userScopeUidToCreationDeletionLockFDs; - CopyMemory(newArray, oldArray, count * sizeof(newArray[0])); - delete[] oldArray; - } - - s_userScopeUidToCreationDeletionLockFDs = newArray; - s_userScopeUidToCreationDeletionLockFDsCapacity = newCapacity; - } - - s_userScopeUidToCreationDeletionLockFDs[count] = UserScopeUidAndFileDescriptor(userScopeUid, creationDeletionLockFD); - s_userScopeUidToCreationDeletionLockFDsCount = count + 1; -} - -int SharedMemoryManager::FindUserScopeCreationDeletionLockFD(uid_t userScopeUid) -{ - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - - UserScopeUidAndFileDescriptor *arr = s_userScopeUidToCreationDeletionLockFDs; - for (int i = 0; i < s_userScopeUidToCreationDeletionLockFDsCount; i++) - { - _ASSERTE(arr[i].fileDescriptor != -1); - if (arr[i].userScopeUid == userScopeUid) - { - return arr[i].fileDescriptor; - } - } - - return -1; -} - -#ifdef _DEBUG -bool SharedMemoryManager::IsCreationDeletionProcessLockAcquired() -{ - return s_creationDeletionProcessLockOwnerThreadId == THREADSilentGetCurrentThreadId(); -} - -bool SharedMemoryManager::IsCreationDeletionFileLockAcquired() -{ - return s_creationDeletionFileLockOwnerThreadId == THREADSilentGetCurrentThreadId(); -} -#endif // _DEBUG - -void SharedMemoryManager::AddProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader) -{ - _ASSERTE(processDataHeader != nullptr); - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - _ASSERTE(processDataHeader->GetNextInProcessDataHeaderList() == nullptr); - _ASSERTE(FindProcessDataHeader(processDataHeader->GetId()) == nullptr); - - processDataHeader->SetNextInProcessDataHeaderList(s_processDataHeaderListHead); - s_processDataHeaderListHead = processDataHeader; -} - -void SharedMemoryManager::RemoveProcessDataHeader(SharedMemoryProcessDataHeader *processDataHeader) -{ - _ASSERTE(processDataHeader != nullptr); - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - - if (s_processDataHeaderListHead == processDataHeader) - { - s_processDataHeaderListHead = processDataHeader->GetNextInProcessDataHeaderList(); - processDataHeader->SetNextInProcessDataHeaderList(nullptr); - return; - } - for (SharedMemoryProcessDataHeader - *previous = s_processDataHeaderListHead, - *current = previous->GetNextInProcessDataHeaderList(); - current != nullptr; - previous = current, current = current->GetNextInProcessDataHeaderList()) - { - if (current == processDataHeader) - { - previous->SetNextInProcessDataHeaderList(current->GetNextInProcessDataHeaderList()); - current->SetNextInProcessDataHeaderList(nullptr); - return; - } - } - _ASSERTE(false); -} - -SharedMemoryProcessDataHeader *SharedMemoryManager::FindProcessDataHeader(const SharedMemoryId *id) -{ - _ASSERTE(IsCreationDeletionProcessLockAcquired()); - - // TODO: Use a hash table - for (SharedMemoryProcessDataHeader *current = s_processDataHeaderListHead; - current != nullptr; - current = current->GetNextInProcessDataHeaderList()) - { - if (current->GetId()->Equals(id)) - { - return current; - } - } - return nullptr; -} diff --git a/src/coreclr/pal/src/synchmgr/synchmanager.cpp b/src/coreclr/pal/src/synchmgr/synchmanager.cpp index 0665df179caa04..a2fa13c742cedd 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.cpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.cpp @@ -552,17 +552,6 @@ namespace CorUnix CThreadSynchronizationInfo * pSynchInfo = &pthrTarget->synchronizationInfo; CPalSynchronizationManager * pSynchManager = GetInstance(); - // The shared memory manager's process lock is acquired before calling into some PAL synchronization primitives that may - // take the PAL synchronization manager's synch lock (acquired below). For example, when using a file lock - // implementation for a named mutex (see NamedMutexProcessData::NamedMutexProcessData()), under the shared memory - // manager's process lock, CreateMutex is called, which acquires the PAL synchronization manager's synch lock. The same - // lock order needs to be maintained here to avoid a deadlock. - bool abandonNamedMutexes = pSynchInfo->OwnsAnyNamedMutex(); - if (abandonNamedMutexes) - { - SharedMemoryManager::AcquireCreationDeletionProcessLock(); - } - // Local lock AcquireLocalSynchLock(pthrCurrent); @@ -598,20 +587,6 @@ namespace CorUnix pSynchManager->m_cacheOwnedObjectsListNodes.Add(pthrCurrent, poolnItem); } - if (abandonNamedMutexes) - { - // Abandon owned named mutexes - while (true) - { - NamedMutexProcessData *processData = pSynchInfo->RemoveFirstOwnedNamedMutex(); - if (processData == nullptr) - { - break; - } - processData->Abandon(); - } - } - if (pthrTarget != pthrCurrent) { // If the target thead is not the current one, we are being called @@ -645,11 +620,6 @@ namespace CorUnix // Unlock ReleaseLocalSynchLock(pthrCurrent); - if (abandonNamedMutexes) - { - SharedMemoryManager::ReleaseCreationDeletionProcessLock(); - } - DiscardAllPendingAPCs(pthrCurrent, pthrTarget); return palErr; @@ -2989,8 +2959,7 @@ namespace CorUnix CThreadSynchronizationInfo::CThreadSynchronizationInfo() : m_tsThreadState(TS_IDLE), m_shridWaitAwakened(NULL), - m_lLocalSynchLockCount(0), - m_ownedNamedMutexListHead(nullptr) + m_lLocalSynchLockCount(0) { InitializeListHead(&m_leOwnedObjsList); @@ -3236,87 +3205,6 @@ namespace CorUnix return poolnItem; } - void CThreadSynchronizationInfo::AddOwnedNamedMutex(NamedMutexProcessData *processData) - { - _ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo); - _ASSERTE(processData != nullptr); - _ASSERTE(processData->IsLockOwnedByCurrentThread()); - _ASSERTE(processData->GetNextInThreadOwnedNamedMutexList() == nullptr); - - processData->SetNextInThreadOwnedNamedMutexList(m_ownedNamedMutexListHead); - m_ownedNamedMutexListHead = processData; - } - - void CThreadSynchronizationInfo::RemoveOwnedNamedMutex(NamedMutexProcessData *processData) - { - _ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo); - _ASSERTE(processData != nullptr); - _ASSERTE(processData->IsLockOwnedByCurrentThread()); - - if (m_ownedNamedMutexListHead == processData) - { - m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList(); - processData->SetNextInThreadOwnedNamedMutexList(nullptr); - } - else - { - bool found = false; - for (NamedMutexProcessData - *previous = m_ownedNamedMutexListHead, - *current = previous->GetNextInThreadOwnedNamedMutexList(); - current != nullptr; - previous = current, current = current->GetNextInThreadOwnedNamedMutexList()) - { - if (current == processData) - { - found = true; - previous->SetNextInThreadOwnedNamedMutexList(current->GetNextInThreadOwnedNamedMutexList()); - current->SetNextInThreadOwnedNamedMutexList(nullptr); - break; - } - } - _ASSERTE(found); - } - } - - NamedMutexProcessData *CThreadSynchronizationInfo::RemoveFirstOwnedNamedMutex() - { - _ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo); - - NamedMutexProcessData *processData = m_ownedNamedMutexListHead; - if (processData != nullptr) - { - _ASSERTE(processData->IsLockOwnedByCurrentThread()); - m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList(); - processData->SetNextInThreadOwnedNamedMutexList(nullptr); - } - return processData; - } - - bool CThreadSynchronizationInfo::OwnsNamedMutex(NamedMutexProcessData *processData) - { - _ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo); - - for (NamedMutexProcessData *current = m_ownedNamedMutexListHead; - current != nullptr; - current = current->GetNextInThreadOwnedNamedMutexList()) - { - _ASSERTE(current->IsLockOwnedByCurrentThread()); - if (current == processData) - { - return true; - } - } - - return false; - } - - bool CThreadSynchronizationInfo::OwnsAnyNamedMutex() const - { - _ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo); - return m_ownedNamedMutexListHead != nullptr; - } - #if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING /*++ diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index 495d86b1cd5cd3..1a3aa8c4df18df 100644 --- a/src/coreclr/pal/src/synchmgr/wait.cpp +++ b/src/coreclr/pal/src/synchmgr/wait.cpp @@ -40,7 +40,6 @@ static PalObjectTypeId sg_rgWaitObjectsIds[] = otiAutoResetEvent, otiManualResetEvent, otiMutex, - otiNamedMutex, otiSemaphore, otiProcess, otiThread @@ -53,7 +52,6 @@ static PalObjectTypeId sg_rgSignalableObjectIds[] = otiAutoResetEvent, otiManualResetEvent, otiMutex, - otiNamedMutex, otiSemaphore }; static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, ARRAY_SIZE(sg_rgSignalableObjectIds)); @@ -417,55 +415,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( goto WFMOExIntExit; } - if (nCount > 1) - { - // Check for any cross-process sync objects. "Wait for any" and "wait for all" operations are not supported on - // cross-process sync objects in the PAL. - for (DWORD i = 0; i < nCount; ++i) - { - if (ppIPalObjs[i]->GetObjectType()->GetId() == otiNamedMutex) - { - ERROR("Attempt to wait for any or all handles including a cross-process sync object", ERROR_NOT_SUPPORTED); - pThread->SetLastError(ERROR_NOT_SUPPORTED); - goto WFMOExIntCleanup; - } - } - } - else if (ppIPalObjs[0]->GetObjectType()->GetId() == otiNamedMutex) - { - SharedMemoryProcessDataHeader *processDataHeader = - SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(ppIPalObjs[0]); - _ASSERTE(processDataHeader != nullptr); - try - { - MutexTryAcquireLockResult tryAcquireLockResult = - static_cast(processDataHeader->GetData())->TryAcquireLock(nullptr, dwMilliseconds); - switch (tryAcquireLockResult) - { - case MutexTryAcquireLockResult::AcquiredLock: - dwRet = WAIT_OBJECT_0; - break; - - case MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned: - dwRet = WAIT_ABANDONED_0; - break; - - case MutexTryAcquireLockResult::TimedOut: - dwRet = WAIT_TIMEOUT; - break; - - default: - _ASSERTE(false); - break; - } - } - catch (SharedMemoryException ex) - { - pThread->SetLastError(ex.GetErrorCode()); - } - goto WFMOExIntCleanup; - } - if (fWAll) { // For a wait-all operation, check for duplicate wait objects in the array. This just uses a brute-force O(n^2) @@ -761,7 +710,6 @@ DWORD CorUnix::InternalSignalObjectAndWait( break; case otiMutex: - case otiNamedMutex: palError = InternalReleaseMutex(thread, hObjectToSignal); break; diff --git a/src/coreclr/pal/src/synchobj/mutex.cpp b/src/coreclr/pal/src/synchobj/mutex.cpp index f23254612c555c..b189f7269d6879 100644 --- a/src/coreclr/pal/src/synchobj/mutex.cpp +++ b/src/coreclr/pal/src/synchobj/mutex.cpp @@ -55,128 +55,47 @@ CObjectType CorUnix::otMutex( static CAllowedObjectTypes aotMutex(otiMutex); -CObjectType CorUnix::otNamedMutex( - otiNamedMutex, - &SharedMemoryProcessDataHeader::PalObject_Close, // Cleanup routine - sizeof(SharedMemoryProcessDataHeader *), // Immutable data - NULL, // No immutable data copy routine - NULL, // No immutable data cleanup routine - 0, // No process local data - NULL, // No process local data cleanup routine - CObjectType::UnwaitableObject, // PAL's waiting infrastructure is not used - CObjectType::SignalingNotApplicable, // PAL's signaling infrastructure is not used - CObjectType::ThreadReleaseNotApplicable, // PAL's signaling infrastructure is not used - CObjectType::OwnershipNotApplicable // PAL's ownership infrastructure is not used - ); - -static CAllowedObjectTypes aotNamedMutex(otiNamedMutex); - -static PalObjectTypeId anyMutexTypeIds[] = {otiMutex, otiNamedMutex}; -static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, ARRAY_SIZE(anyMutexTypeIds)); - /*++ Function: CreateMutexW - See doc for PAL_CreateMutexW. ---*/ - -HANDLE -PALAPI -CreateMutexW( - IN LPSECURITY_ATTRIBUTES lpMutexAttributes, - IN BOOL bInitialOwner, - IN LPCWSTR lpName) -{ - return PAL_CreateMutexW(bInitialOwner, lpName, false /* bCurrentUserOnly */, nullptr, 0); -} - -/*++ -Function: - PAL_CreateMutexW - Note: lpMutexAttributes currently ignored: -- Win32 object security not supported -- handles to mutex objects are not inheritable -Parameters: - lpSystemCallErrors -- An optional buffer into which system call errors are written, for more detailed error information. - dwSystemCallErrorsBufferSize -- Size of the buffer pointed to by lpSystemCallErrors in bytes. - See MSDN docs on CreateMutexW for all other parameters. --*/ HANDLE PALAPI -PAL_CreateMutexW( +CreateMutexW( + IN LPSECURITY_ATTRIBUTES lpMutexAttributes, IN BOOL bInitialOwner, - IN LPCWSTR lpName, - IN BOOL bCurrentUserOnly, - IN LPSTR lpSystemCallErrors, - IN DWORD dwSystemCallErrorsBufferSize) + IN LPCWSTR lpName) { + _ASSERTE(lpName == nullptr); HANDLE hMutex = NULL; PAL_ERROR palError; CPalThread *pthr = NULL; - char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1]; - PERF_ENTRY(PAL_CreateMutexW); - ENTRY("PAL_CreateMutexW(bInitialOwner=%d, lpName=%p (%S), lpSystemCallErrors=%p, dwSystemCallErrorsBufferSize=%d\n", - bInitialOwner, - lpName, - lpName?lpName:W16_NULLSTRING, - lpSystemCallErrors, - dwSystemCallErrorsBufferSize); + PERF_ENTRY(CreateMutexW); + ENTRY("CreateMutexW(lpMutexAttributes=%p, bInitialOwner=%d\n", + lpMutexAttributes, + bInitialOwner + ); pthr = InternalGetCurrentThread(); - /* validate parameters */ - if ((int)dwSystemCallErrorsBufferSize < 0 || (lpSystemCallErrors == nullptr) != (dwSystemCallErrorsBufferSize == 0)) - { - ERROR("One or more parameters are invalid\n"); - palError = ERROR_INVALID_PARAMETER; - goto CreateMutexWExit; - } - - if (lpSystemCallErrors != nullptr) - { - lpSystemCallErrors[0] = '\0'; - } - - if (lpName != nullptr) - { - int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr); - if (bytesWritten == 0) - { - DWORD errorCode = GetLastError(); - if (errorCode == ERROR_INSUFFICIENT_BUFFER) - { - palError = static_cast(SharedMemoryError::NameTooLong); - } - else - { - ASSERT("WideCharToMultiByte failed (%u)\n", errorCode); - palError = errorCode; - } - goto CreateMutexWExit; - } - } - { - SharedMemorySystemCallErrors errors(lpSystemCallErrors, (int)dwSystemCallErrorsBufferSize); palError = InternalCreateMutex( - &errors, pthr, - nullptr, + nullptr, // lpMutexAttributes currently ignored bInitialOwner, - lpName == nullptr ? nullptr : utf8Name, - bCurrentUserOnly, &hMutex ); } -CreateMutexWExit: // // We always need to set last error, even on success: // we need to protect ourselves from the situation @@ -186,8 +105,8 @@ PAL_CreateMutexW( pthr->SetLastError(palError); - LOGEXIT("PAL_CreateMutexW returns HANDLE %p\n", hMutex); - PERF_EXIT(PAL_CreateMutexW); + LOGEXIT("CreateMutexW returns HANDLE %p\n", hMutex); + PERF_EXIT(CreateMutexW); return hMutex; } @@ -234,12 +153,9 @@ CreateMutexExW( PAL_ERROR CorUnix::InternalCreateMutex( - SharedMemorySystemCallErrors *errors, CPalThread *pthr, LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, - LPCSTR lpName, - BOOL bCurrentUserOnly, HANDLE *phMutex ) { @@ -249,32 +165,21 @@ CorUnix::InternalCreateMutex( IPalObject *pobjRegisteredMutex = NULL; ISynchStateController *pssc = NULL; HANDLE hMutex = nullptr; - bool createdNamedMutex = false; _ASSERTE(NULL != pthr); _ASSERTE(NULL != phMutex); ENTRY("InternalCreateMutex(pthr=%p, lpMutexAttributes=%p, bInitialOwner=%d" - ", lpName=%p, phMutex=%p)\n", + ", phMutex=%p)\n", pthr, lpMutexAttributes, bInitialOwner, - lpName, phMutex ); - if (lpName != nullptr && lpName[0] == '\0') - { - // Empty name is treated as a request for an unnamed process-local mutex - lpName = nullptr; - } - - CObjectType *ot = lpName == nullptr ? &otMutex : &otNamedMutex; - CAllowedObjectTypes *aot = lpName == nullptr ? &aotMutex : &aotNamedMutex; - palError = g_pObjectManager->AllocateObject( pthr, - ot, + &otMutex, &oa, &pobjMutex ); @@ -284,61 +189,42 @@ CorUnix::InternalCreateMutex( goto InternalCreateMutexExit; } - if (lpName == nullptr) - { - palError = pobjMutex->GetSynchStateController( - pthr, - &pssc - ); - - if (NO_ERROR != palError) - { - ASSERT("Unable to create state controller (%d)\n", palError); - goto InternalCreateMutexExit; - } - - if (bInitialOwner) - { - palError = pssc->SetOwner(pthr); - } - else - { - palError = pssc->SetSignalCount(1); - } + palError = pobjMutex->GetSynchStateController( + pthr, + &pssc + ); - pssc->ReleaseController(); + if (NO_ERROR != palError) + { + ASSERT("Unable to create state controller (%d)\n", palError); + goto InternalCreateMutexExit; + } - if (NO_ERROR != palError) - { - ASSERT("Unable to set initial mutex state (%d)\n", palError); - goto InternalCreateMutexExit; - } + if (bInitialOwner) + { + palError = pssc->SetOwner(pthr); } else { - SharedMemoryProcessDataHeader *processDataHeader; - try - { - processDataHeader = - NamedMutexProcessData::CreateOrOpen(errors, lpName, !!bCurrentUserOnly, !!bInitialOwner, &createdNamedMutex); - } - catch (SharedMemoryException ex) - { - palError = ex.GetErrorCode(); - goto InternalCreateMutexExit; - } + palError = pssc->SetSignalCount(1); + } + + pssc->ReleaseController(); - SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(pobjMutex, processDataHeader); + if (NO_ERROR != palError) + { + ASSERT("Unable to set initial mutex state (%d)\n", palError); + goto InternalCreateMutexExit; } palError = g_pObjectManager->RegisterObject( pthr, pobjMutex, - aot, + &aotMutex, &hMutex, &pobjRegisteredMutex ); - _ASSERTE(palError != ERROR_ALREADY_EXISTS); // PAL's naming infrastructure is not used for named mutexes + _ASSERTE(palError != ERROR_ALREADY_EXISTS); // Mutexes can't have names _ASSERTE(palError != NO_ERROR || pobjRegisteredMutex == pobjMutex); _ASSERTE((palError == NO_ERROR) == (hMutex != nullptr)); @@ -358,13 +244,6 @@ CorUnix::InternalCreateMutex( *phMutex = hMutex; hMutex = nullptr; - if (lpName != nullptr && !createdNamedMutex) - { - // Indicate to the caller that an existing mutex was opened, and hence the caller will not have initial ownership of the - // mutex if requested through bInitialOwner - palError = ERROR_ALREADY_EXISTS; - } - InternalCreateMutexExit: _ASSERTE(pobjRegisteredMutex == nullptr); @@ -443,7 +322,7 @@ CorUnix::InternalReleaseMutex( palError = g_pObjectManager->ReferenceObjectByHandle( pthr, hMutex, - &aotAnyMutex, + &aotMutex, &pobjMutex ); @@ -453,44 +332,23 @@ CorUnix::InternalReleaseMutex( goto InternalReleaseMutexExit; } - objectTypeId = pobjMutex->GetObjectType()->GetId(); - if (objectTypeId == otiMutex) - { - palError = pobjMutex->GetSynchStateController( - pthr, - &pssc - ); + palError = pobjMutex->GetSynchStateController( + pthr, + &pssc + ); - if (NO_ERROR != palError) - { - ASSERT("Error %d obtaining synch state controller\n", palError); - goto InternalReleaseMutexExit; - } + if (NO_ERROR != palError) + { + ASSERT("Error %d obtaining synch state controller\n", palError); + goto InternalReleaseMutexExit; + } - palError = pssc->DecrementOwnershipCount(); + palError = pssc->DecrementOwnershipCount(); - if (NO_ERROR != palError) - { - ERROR("Error %d decrementing mutex ownership count\n", palError); - goto InternalReleaseMutexExit; - } - } - else + if (NO_ERROR != palError) { - _ASSERTE(objectTypeId == otiNamedMutex); - - SharedMemoryProcessDataHeader *processDataHeader = - SharedMemoryProcessDataHeader::PalObject_GetProcessDataHeader(pobjMutex); - _ASSERTE(processDataHeader != nullptr); - try - { - static_cast(processDataHeader->GetData())->ReleaseLock(); - } - catch (SharedMemoryException ex) - { - palError = ex.GetErrorCode(); - goto InternalReleaseMutexExit; - } + ERROR("Error %d decrementing mutex ownership count\n", palError); + goto InternalReleaseMutexExit; } InternalReleaseMutexExit: @@ -510,1221 +368,39 @@ CorUnix::InternalReleaseMutex( return palError; } -/*++ -Function: - OpenMutexA - -Note: - dwDesiredAccess is currently ignored (no Win32 object security support) - bInheritHandle is currently ignored (handles to mutexes are not inheritable) - -See MSDN doc. ---*/ - -HANDLE -PALAPI -OpenMutexA ( - IN DWORD dwDesiredAccess, - IN BOOL bInheritHandle, - IN LPCSTR lpName) -{ - HANDLE hMutex = NULL; - CPalThread *pthr = NULL; - PAL_ERROR palError; - - PERF_ENTRY(OpenMutexA); - ENTRY("OpenMutexA(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%s))\n", - dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:"NULL"); - - pthr = InternalGetCurrentThread(); - - /* validate parameters */ - if (lpName == nullptr) - { - ERROR("name is NULL\n"); - palError = ERROR_INVALID_PARAMETER; - goto OpenMutexAExit; - } - - palError = InternalOpenMutex(nullptr, pthr, lpName, false /* bCurrentUserOnly */, &hMutex); - -OpenMutexAExit: - if (NO_ERROR != palError) - { - pthr->SetLastError(palError); - } - - LOGEXIT("OpenMutexA returns HANDLE %p\n", hMutex); - PERF_EXIT(OpenMutexA); - return hMutex; -} - -/*++ -Function: - OpenMutexW - -Parameters: - See doc for PAL_OpenMutexW. ---*/ - -HANDLE -PALAPI -OpenMutexW( - IN DWORD dwDesiredAccess, - IN BOOL bInheritHandle, - IN LPCWSTR lpName) -{ - return PAL_OpenMutexW(lpName, false /* bCurrentUserOnly */, nullptr, 0); -} - -/*++ -Function: - PAL_OpenMutexW - -Note: - dwDesiredAccess is currently ignored (no Win32 object security support) - bInheritHandle is currently ignored (handles to mutexes are not inheritable) - -Parameters: - lpSystemCallErrors -- An optional buffer into which system call errors are written, for more detailed error information. - dwSystemCallErrorsBufferSize -- Size of the buffer pointed to by lpSystemCallErrors in bytes. - - See MSDN docs on OpenMutexW for all other parameters. ---*/ - -HANDLE -PALAPI -PAL_OpenMutexW( - IN LPCWSTR lpName, - IN BOOL bCurrentUserOnly, - IN LPSTR lpSystemCallErrors, - IN DWORD dwSystemCallErrorsBufferSize) +/* Basic spinlock implementation */ +void SPINLOCKAcquire (LONG * lock, unsigned int flags) { - HANDLE hMutex = NULL; - PAL_ERROR palError = NO_ERROR; - CPalThread *pthr = NULL; - char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1]; - - PERF_ENTRY(PAL_OpenMutexW); - ENTRY("PAL_OpenMutexW(lpName=%p (%S), lpSystemCallErrors=%p, dwSystemCallErrorsBufferSize=%d)\n", - lpName, - lpName?lpName:W16_NULLSTRING, - lpSystemCallErrors, - dwSystemCallErrorsBufferSize); - - pthr = InternalGetCurrentThread(); - - /* validate parameters */ - if (lpName == nullptr || - lpName[0] == W('\0') || - (int)dwSystemCallErrorsBufferSize < 0 || - (lpSystemCallErrors == nullptr) != (dwSystemCallErrorsBufferSize == 0)) - { - ERROR("One or more parameters are invalid\n"); - palError = ERROR_INVALID_PARAMETER; - goto OpenMutexWExit; - } + size_t loop_seed = 1, loop_count = 0; - if (lpSystemCallErrors != nullptr) + if (flags & SYNCSPINLOCK_F_ASYMMETRIC) { - lpSystemCallErrors[0] = '\0'; + loop_seed = ((size_t)pthread_self() % 10) + 1; } - + while (InterlockedCompareExchange(lock, 1, 0)) { - int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr); - if (bytesWritten == 0) + if (!(flags & SYNCSPINLOCK_F_ASYMMETRIC) || (++loop_count % loop_seed)) { - DWORD errorCode = GetLastError(); - if (errorCode == ERROR_INSUFFICIENT_BUFFER) - { - palError = static_cast(SharedMemoryError::NameTooLong); - } - else - { - ASSERT("WideCharToMultiByte failed (%u)\n", errorCode); - palError = errorCode; - } - goto OpenMutexWExit; +#if PAL_IGNORE_NORMAL_THREAD_PRIORITY + struct timespec tsSleepTime; + tsSleepTime.tv_sec = 0; + tsSleepTime.tv_nsec = 1; + nanosleep(&tsSleepTime, NULL); +#else + sched_yield(); +#endif } - - SharedMemorySystemCallErrors errors(lpSystemCallErrors, (int)dwSystemCallErrorsBufferSize); - palError = InternalOpenMutex(&errors, pthr, lpName == nullptr ? nullptr : utf8Name, bCurrentUserOnly, &hMutex); - } - -OpenMutexWExit: - if (NO_ERROR != palError) - { - pthr->SetLastError(palError); } - LOGEXIT("PAL_OpenMutexW returns HANDLE %p\n", hMutex); - PERF_EXIT(PAL_OpenMutexW); - - return hMutex; } -/*++ -Function: - InternalOpenMutex - -Parameters: - errors -- An optional wrapper for system call errors, for more detailed error information. - pthr -- thread data for calling thread - phEvent -- on success, receives the allocated mutex handle - - See MSDN docs on OpenMutex for all other parameters. ---*/ - -PAL_ERROR -CorUnix::InternalOpenMutex( - SharedMemorySystemCallErrors *errors, - CPalThread *pthr, - LPCSTR lpName, - BOOL bCurrentUserOnly, - HANDLE *phMutex - ) +void SPINLOCKRelease (LONG * lock) { - CObjectAttributes oa; - PAL_ERROR palError = NO_ERROR; - IPalObject *pobjMutex = NULL; - IPalObject *pobjRegisteredMutex = NULL; - HANDLE hMutex = nullptr; - - _ASSERTE(NULL != pthr); - _ASSERTE(NULL != lpName); - _ASSERTE(NULL != phMutex); - - ENTRY("InternalOpenMutex(pthr=%p, " - "lpName=%p, phMutex=%p)\n", - pthr, - lpName, - phMutex - ); - - palError = g_pObjectManager->AllocateObject( - pthr, - &otNamedMutex, - &oa, - &pobjMutex - ); - - if (NO_ERROR != palError) - { - goto InternalOpenMutexExit; - } - - { - SharedMemoryProcessDataHeader *processDataHeader; - try - { - processDataHeader = NamedMutexProcessData::Open(errors, lpName, bCurrentUserOnly); - } - catch (SharedMemoryException ex) - { - palError = ex.GetErrorCode(); - goto InternalOpenMutexExit; - } - - if (processDataHeader == nullptr) - { - palError = ERROR_FILE_NOT_FOUND; - goto InternalOpenMutexExit; - } - - SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(pobjMutex, processDataHeader); - } - - palError = g_pObjectManager->RegisterObject( - pthr, - pobjMutex, - &aotNamedMutex, - &hMutex, - &pobjRegisteredMutex - ); - _ASSERTE(palError != ERROR_ALREADY_EXISTS); // PAL's naming infrastructure is not used for named mutexes - _ASSERTE(palError != NO_ERROR || pobjRegisteredMutex == pobjMutex); - _ASSERTE((palError == NO_ERROR) == (hMutex != nullptr)); - - // When RegisterObject succeeds, the object would have an additional reference from the handle, and one reference is - // released below through pobjRegisteredMutex. When RegisterObject fails, it releases the initial reference to the object. - // Either way, pobjMutex is invalidated by the above call to RegisterObject. - pobjMutex = nullptr; - - if (palError != NO_ERROR) - { - goto InternalOpenMutexExit; - } - - pobjRegisteredMutex->ReleaseReference(pthr); - pobjRegisteredMutex = nullptr; - - *phMutex = hMutex; - hMutex = nullptr; - -InternalOpenMutexExit: - - _ASSERTE(pobjRegisteredMutex == nullptr); - _ASSERTE(hMutex == nullptr); - - if (pobjMutex != nullptr) - { - pobjMutex->ReleaseReference(pthr); - } - - LOGEXIT("InternalCreateMutex returns %i\n", palError); - - return palError; -} - - -/* Basic spinlock implementation */ -void SPINLOCKAcquire (LONG * lock, unsigned int flags) -{ - size_t loop_seed = 1, loop_count = 0; - - if (flags & SYNCSPINLOCK_F_ASYMMETRIC) - { - loop_seed = ((size_t)pthread_self() % 10) + 1; - } - while (InterlockedCompareExchange(lock, 1, 0)) - { - if (!(flags & SYNCSPINLOCK_F_ASYMMETRIC) || (++loop_count % loop_seed)) - { -#if PAL_IGNORE_NORMAL_THREAD_PRIORITY - struct timespec tsSleepTime; - tsSleepTime.tv_sec = 0; - tsSleepTime.tv_nsec = 1; - nanosleep(&tsSleepTime, NULL); -#else - sched_yield(); -#endif - } - } - -} - -void SPINLOCKRelease (LONG * lock) -{ - VolatileStore(lock, 0); -} + VolatileStore(lock, 0); +} DWORD SPINLOCKTryAcquire (LONG * lock) { return InterlockedCompareExchange(lock, 1, 0); // only returns 0 or 1. } - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// MutexHelpers - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX -void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex) -{ - _ASSERTE(mutex != nullptr); - - struct AutoCleanup - { - pthread_mutexattr_t *m_mutexAttributes; - - AutoCleanup() : m_mutexAttributes(nullptr) - { - } - - ~AutoCleanup() - { - if (m_mutexAttributes != nullptr) - { - int error = pthread_mutexattr_destroy(m_mutexAttributes); - _ASSERTE(error == 0); - } - } - } autoCleanup; - - pthread_mutexattr_t mutexAttributes; - int error = pthread_mutexattr_init(&mutexAttributes); - if (error != 0) - { - if (errors != nullptr) - { - errors->Append("pthread_mutexattr_init(...) == %s;", GetFriendlyErrorCodeString(error)); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); - } - autoCleanup.m_mutexAttributes = &mutexAttributes; - - error = pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED); - _ASSERTE(error == 0); - - error = pthread_mutexattr_setrobust(&mutexAttributes, PTHREAD_MUTEX_ROBUST); - _ASSERTE(error == 0); - - error = pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE); - _ASSERTE(error == 0); - - error = pthread_mutex_init(mutex, &mutexAttributes); - if (error != 0) - { - if (errors != nullptr) - { - errors->Append("pthread_mutex_init(...) == %s;", GetFriendlyErrorCodeString(error)); - } - - throw SharedMemoryException(static_cast(error == EPERM ? SharedMemoryError::IO : SharedMemoryError::OutOfMemory)); - } -} - -void MutexHelpers::DestroyMutex(pthread_mutex_t *mutex) -{ - _ASSERTE(mutex != nullptr); - - int error = pthread_mutex_destroy(mutex); - _ASSERTE(error == 0 || error == EBUSY); // the error will be EBUSY if the mutex is locked -} - -MutexTryAcquireLockResult MutexHelpers::TryAcquireLock( - SharedMemorySystemCallErrors *errors, - pthread_mutex_t *mutex, - DWORD timeoutMilliseconds) -{ - _ASSERTE(mutex != nullptr); - - int lockResult; - switch (timeoutMilliseconds) - { - case static_cast(-1): - lockResult = pthread_mutex_lock(mutex); - break; - - case 0: - lockResult = pthread_mutex_trylock(mutex); - break; - - default: - { - struct timespec timeoutTime; - PAL_ERROR palError = CPalSynchronizationManager::GetAbsoluteTimeout(timeoutMilliseconds, &timeoutTime, /*fPreferMonotonicClock*/ FALSE); - _ASSERTE(palError == NO_ERROR); - lockResult = pthread_mutex_timedlock(mutex, &timeoutTime); - break; - } - } - - switch (lockResult) - { - case 0: - return MutexTryAcquireLockResult::AcquiredLock; - - case EBUSY: - _ASSERTE(timeoutMilliseconds == 0); - return MutexTryAcquireLockResult::TimedOut; - - case ETIMEDOUT: - _ASSERTE(timeoutMilliseconds != static_cast(-1)); - _ASSERTE(timeoutMilliseconds != 0); - return MutexTryAcquireLockResult::TimedOut; - - case EOWNERDEAD: - { - int setConsistentResult = pthread_mutex_consistent(mutex); - _ASSERTE(setConsistentResult == 0); - return MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned; - } - - case EAGAIN: - throw SharedMemoryException(static_cast(NamedMutexError::MaximumRecursiveLocksReached)); - - default: - { - if (errors != nullptr) - { - errors->Append( - "%s(...) == %s;", - timeoutMilliseconds == (DWORD)-1 ? "pthread_mutex_lock" - : timeoutMilliseconds == 0 ? "pthread_mutex_trylock" - : "pthread_mutex_timedlock", - GetFriendlyErrorCodeString(lockResult)); - } - - throw SharedMemoryException(static_cast(NamedMutexError::Unknown)); - } - } -} - -void MutexHelpers::ReleaseLock(pthread_mutex_t *mutex) -{ - _ASSERTE(mutex != nullptr); - - int unlockResult = pthread_mutex_unlock(mutex); - _ASSERTE(unlockResult == 0); -} -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// NamedMutexSharedData - -NamedMutexSharedData::NamedMutexSharedData(SharedMemorySystemCallErrors *errors) - : -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - m_timedWaiterCount(0), -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - m_lockOwnerProcessId(SharedMemoryHelpers::InvalidProcessId), - m_lockOwnerThreadId(SharedMemoryHelpers::InvalidSharedThreadId), - m_isAbandoned(false) -{ -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - static_assert_no_msg(sizeof(m_timedWaiterCount) == sizeof(LONG)); // for interlocked operations -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX - - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX - MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(errors, &m_lock); -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX -} - -NamedMutexSharedData::~NamedMutexSharedData() -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX - MutexHelpers::DestroyMutex(&m_lock); -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX -} - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX -pthread_mutex_t *NamedMutexSharedData::GetLock() -{ - return &m_lock; -} -#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX -bool NamedMutexSharedData::HasAnyTimedWaiters() const -{ - return - InterlockedCompareExchange( - const_cast(reinterpret_cast(&m_timedWaiterCount)), - -1 /* Exchange */, - -1 /* Comparand */) != 0; -} - -void NamedMutexSharedData::IncTimedWaiterCount() -{ - ULONG newValue = InterlockedIncrement(reinterpret_cast(&m_timedWaiterCount)); - if (newValue == 0) - { - InterlockedDecrement(reinterpret_cast(&m_timedWaiterCount)); - throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory)); - } -} - -void NamedMutexSharedData::DecTimedWaiterCount() -{ - ULONG newValue = InterlockedDecrement(reinterpret_cast(&m_timedWaiterCount)); - _ASSERTE(newValue + 1 != 0); -} -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX - -bool NamedMutexSharedData::IsAbandoned() const -{ - _ASSERTE(IsLockOwnedByCurrentThread()); - return m_isAbandoned; -} - -void NamedMutexSharedData::SetIsAbandoned(bool isAbandoned) -{ - _ASSERTE(IsLockOwnedByCurrentThread()); - _ASSERTE(m_isAbandoned != isAbandoned); - - m_isAbandoned = isAbandoned; -} - -bool NamedMutexSharedData::IsLockOwnedByAnyThread() const -{ - return - m_lockOwnerProcessId != SharedMemoryHelpers::InvalidProcessId || - m_lockOwnerThreadId != SharedMemoryHelpers::InvalidSharedThreadId; -} - -bool NamedMutexSharedData::IsLockOwnedByCurrentThread() const -{ - return m_lockOwnerProcessId == GetCurrentProcessId() && m_lockOwnerThreadId == THREADSilentGetCurrentThreadId(); -} - -void NamedMutexSharedData::SetLockOwnerToCurrentThread() -{ - m_lockOwnerProcessId = GetCurrentProcessId(); - _ASSERTE(m_lockOwnerProcessId != SharedMemoryHelpers::InvalidProcessId); - m_lockOwnerThreadId = THREADSilentGetCurrentThreadId(); - _ASSERTE(m_lockOwnerThreadId != SharedMemoryHelpers::InvalidSharedThreadId); -} - -void NamedMutexSharedData::ClearLockOwner() -{ - _ASSERTE(IsLockOwnedByCurrentThread()); - - m_lockOwnerProcessId = SharedMemoryHelpers::InvalidProcessId; - m_lockOwnerThreadId = SharedMemoryHelpers::InvalidSharedThreadId; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// NamedMutexProcessData - -// This value should only be incremented if a non-backward-compatible change to the sync system is made. A process would fail to -// open a mutex created with a different sync system version. -const UINT8 NamedMutexProcessData::SyncSystemVersion = 1; - -const DWORD NamedMutexProcessData::PollLoopMaximumSleepMilliseconds = 100; - -SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( - SharedMemorySystemCallErrors *errors, - LPCSTR name, - bool isUserScope, - bool acquireLockIfCreated, - bool *createdRef) -{ - return CreateOrOpen(errors, name, isUserScope, true /* createIfNotExist */, acquireLockIfCreated, createdRef); -} - -SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(SharedMemorySystemCallErrors *errors, LPCSTR name, bool isUserScope) -{ - return - CreateOrOpen( - errors, - name, - isUserScope, - false /* createIfNotExist */, - false /* acquireLockIfCreated */, - nullptr /* createdRef */); -} - -SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen( - SharedMemorySystemCallErrors *errors, - LPCSTR name, - bool isUserScope, - bool createIfNotExist, - bool acquireLockIfCreated, - bool *createdRef) -{ - _ASSERTE(name != nullptr); - _ASSERTE(createIfNotExist || !acquireLockIfCreated); - -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - PathCharString lockFilePath; -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - - struct AutoCleanup - { - bool m_acquiredCreationDeletionProcessLock; - bool m_acquiredCreationDeletionFileLock; - SharedMemoryProcessDataHeader *m_processDataHeader; - #if !NAMED_MUTEX_USE_PTHREAD_MUTEX - PathCharString *m_lockFilePath; - SIZE_T m_sessionDirectoryPathCharCount; - bool m_createdLockFile; - int m_lockFileDescriptor; - #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - bool m_cancel; - - AutoCleanup() - : m_acquiredCreationDeletionProcessLock(false), - m_acquiredCreationDeletionFileLock(false), - m_processDataHeader(nullptr), - #if !NAMED_MUTEX_USE_PTHREAD_MUTEX - m_lockFilePath(nullptr), - m_sessionDirectoryPathCharCount(0), - m_createdLockFile(false), - m_lockFileDescriptor(-1), - #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - m_cancel(false) - { - } - - ~AutoCleanup() - { - #if !NAMED_MUTEX_USE_PTHREAD_MUTEX - if (!m_cancel) - { - if (m_lockFileDescriptor != -1) - { - SharedMemoryHelpers::CloseFile(m_lockFileDescriptor); - } - - if (m_createdLockFile) - { - _ASSERTE(m_lockFilePath != nullptr); - unlink(*m_lockFilePath); - } - - if (m_sessionDirectoryPathCharCount != 0) - { - _ASSERTE(m_lockFilePath != nullptr); - m_lockFilePath->CloseBuffer(m_sessionDirectoryPathCharCount); - rmdir(*m_lockFilePath); - } - } - #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - - if (m_acquiredCreationDeletionFileLock) - { - _ASSERTE(m_processDataHeader != nullptr); - SharedMemoryManager::ReleaseCreationDeletionFileLock(m_processDataHeader->GetId()); - } - - if (!m_cancel && m_processDataHeader != nullptr) - { - _ASSERTE(m_acquiredCreationDeletionProcessLock); - m_processDataHeader->DecRefCount(); - } - - if (m_acquiredCreationDeletionProcessLock) - { - SharedMemoryManager::ReleaseCreationDeletionProcessLock(); - } - } - } autoCleanup; - - SharedMemoryManager::AcquireCreationDeletionProcessLock(); - autoCleanup.m_acquiredCreationDeletionProcessLock = true; - - // Create or open the shared memory - bool created; - SharedMemoryProcessDataHeader *processDataHeader = - SharedMemoryProcessDataHeader::CreateOrOpen( - errors, - name, - isUserScope, - SharedMemorySharedDataHeader(SharedMemoryType::Mutex, SyncSystemVersion), - sizeof(NamedMutexSharedData), - createIfNotExist, - &created); - if (createdRef != nullptr) - { - *createdRef = created; - } - if (processDataHeader == nullptr) - { - _ASSERTE(!created); - _ASSERTE(!createIfNotExist); - return nullptr; - } - if (created) - { - // If the shared memory file was created, the creation/deletion file lock would have been acquired so that we can - // initialize the shared data - _ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - autoCleanup.m_acquiredCreationDeletionFileLock = true; - } - autoCleanup.m_processDataHeader = processDataHeader; - - if (created) - { - // Initialize the shared data - new(processDataHeader->GetSharedDataHeader()->GetData()) NamedMutexSharedData(errors); - } - - if (processDataHeader->GetData() == nullptr) - { - #if !NAMED_MUTEX_USE_PTHREAD_MUTEX - // Create the lock files directory - const SharedMemoryId *id = processDataHeader->GetId(); - SharedMemoryHelpers::VerifyStringOperation( - lockFilePath.Set(*gSharedFilesPath) && - id->AppendRuntimeTempDirectoryName(lockFilePath) && - lockFilePath.Append('/') && lockFilePath.Append(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME)); - if (created) - { - SharedMemoryHelpers::EnsureDirectoryExists(errors, lockFilePath, id, true /* isGlobalLockAcquired */); - } - - // Create the session directory - SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append('/') && id->AppendSessionDirectoryName(lockFilePath)); - if (created) - { - SharedMemoryHelpers::EnsureDirectoryExists(errors, lockFilePath, id, true /* isGlobalLockAcquired */); - autoCleanup.m_lockFilePath = &lockFilePath; - autoCleanup.m_sessionDirectoryPathCharCount = lockFilePath.GetCount(); - } - - // Create or open the lock file - SharedMemoryHelpers::VerifyStringOperation( - lockFilePath.Append('/') && lockFilePath.Append(id->GetName(), id->GetNameCharCount())); - int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(errors, lockFilePath, id, created); - if (lockFileDescriptor == -1) - { - _ASSERTE(!created); - if (createIfNotExist) - { - if (errors != nullptr) - { - errors->Append( - "open(\"%s\", O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0) == -1; errno == ENOENT;", - (const char *)lockFilePath); - } - - throw SharedMemoryException(static_cast(SharedMemoryError::IO)); - } - - return nullptr; - } - autoCleanup.m_createdLockFile = created; - autoCleanup.m_lockFileDescriptor = lockFileDescriptor; - #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - - // Create the process data - void *processDataBuffer = SharedMemoryHelpers::Alloc(sizeof(NamedMutexProcessData)); - AutoFreeBuffer autoFreeProcessDataBuffer(processDataBuffer); - NamedMutexProcessData *processData = - new(processDataBuffer) - NamedMutexProcessData( - processDataHeader - #if !NAMED_MUTEX_USE_PTHREAD_MUTEX - , - lockFileDescriptor - #endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - ); - autoFreeProcessDataBuffer.Cancel(); - processDataHeader->SetData(processData); - - // If the mutex was created and if requested, acquire the lock initially while holding the creation/deletion locks - if (created && acquireLockIfCreated) - { - MutexTryAcquireLockResult tryAcquireLockResult = processData->TryAcquireLock(errors, 0); - _ASSERTE(tryAcquireLockResult == MutexTryAcquireLockResult::AcquiredLock); - } - } - - autoCleanup.m_cancel = true; - return processDataHeader; -} - -NamedMutexProcessData::NamedMutexProcessData( - SharedMemoryProcessDataHeader *processDataHeader -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - , - int sharedLockFileDescriptor -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX -) - : - m_processDataHeader(processDataHeader), - m_lockCount(0), -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - m_sharedLockFileDescriptor(sharedLockFileDescriptor), -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - m_lockOwnerThread(nullptr), - m_nextInThreadOwnedNamedMutexList(nullptr), - m_hasRefFromLockOwnerThread(false) -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(processDataHeader != nullptr); - -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - _ASSERTE(sharedLockFileDescriptor != -1); - - m_processLockHandle = CreateMutex(nullptr /* lpMutexAttributes */, false /* bInitialOwner */, nullptr /* lpName */); - if (m_processLockHandle == nullptr) - { - throw SharedMemoryException(GetLastError()); - } -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX -} - -bool NamedMutexProcessData::CanClose() const -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - - // When using a pthread robust mutex, the mutex may only be unlocked and destroyed by the thread that owns the lock. When - // using file locks, even though any thread could release that lock, the behavior is kept consistent to the more - // conservative case. If the last handle to the mutex is closed when a different thread owns the lock, the mutex cannot be - // closed. Due to these limitations, the behavior in this corner case is necessarily different from Windows. The caller will - // extend the lifetime of the mutex and will call OnLifetimeExtendedDueToCannotClose() shortly. - return m_lockOwnerThread == nullptr || m_lockOwnerThread == GetCurrentPalThread(); -} - -bool NamedMutexProcessData::HasImplicitRef() const -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - return m_hasRefFromLockOwnerThread; -} - -void NamedMutexProcessData::SetHasImplicitRef(bool value) -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(m_hasRefFromLockOwnerThread != value); - _ASSERTE(!value || !CanClose()); - - // If value == true: - // The mutex could not be closed and the caller extended the lifetime of the mutex. Record that the lock owner thread - // should release the ref when the lock is released on that thread. - // Else: - // The mutex has an implicit ref and got the first explicit reference from this process. Remove the implicit ref from the - // lock owner thread. - m_hasRefFromLockOwnerThread = value; -} - -void NamedMutexProcessData::Close(bool isAbruptShutdown, bool releaseSharedData) -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - _ASSERTE(!releaseSharedData || SharedMemoryManager::IsCreationDeletionFileLockAcquired()); - - // If the process is shutting down abruptly without having closed some mutexes, there could still be threads running with - // active references to the mutex. So when shutting down abruptly, don't clean up any object or global process-local state. - if (!isAbruptShutdown) - { - _ASSERTE(CanClose()); - _ASSERTE(!m_hasRefFromLockOwnerThread); - - CPalThread *lockOwnerThread = m_lockOwnerThread; - if (lockOwnerThread == GetCurrentPalThread()) - { - // The mutex was not released before the last handle to it from this process was closed on the lock-owning thread. - // Another process may still have a handle to the mutex, but since it appears as though this process would not be - // releasing the mutex, abandon the mutex. The only way for this process to otherwise release the mutex is to open - // another handle to it and release the lock on the same thread, which would be incorrect-looking code. The behavior - // in this corner case is different from Windows. - lockOwnerThread->synchronizationInfo.RemoveOwnedNamedMutex(this); - Abandon(); - } - else - { - _ASSERTE(lockOwnerThread == nullptr); - } - - if (releaseSharedData) - { - GetSharedData()->~NamedMutexSharedData(); - } - -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - CloseHandle(m_processLockHandle); - SharedMemoryHelpers::CloseFile(m_sharedLockFileDescriptor); -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX - - } - -#if !NAMED_MUTEX_USE_PTHREAD_MUTEX - - if (!releaseSharedData) - { - return; - } - - try - { - // Delete the lock file, and the session directory if it's not empty - PathCharString path; - const SharedMemoryId *id = m_processDataHeader->GetId(); - SharedMemoryHelpers::VerifyStringOperation( - path.Set(*gSharedFilesPath) && - id->AppendRuntimeTempDirectoryName(path) && - path.Append('/') && path.Append(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) && - path.Append('/') && id->AppendSessionDirectoryName(path) && - path.Append('/')); - SIZE_T sessionDirectoryPathCharCount = path.GetCount(); - SharedMemoryHelpers::VerifyStringOperation(path.Append(id->GetName(), id->GetNameCharCount())); - unlink(path); - path.CloseBuffer(sessionDirectoryPathCharCount); - rmdir(path); - } - catch (SharedMemoryException) - { - // Ignore the error, just don't release shared data - } -#endif // !NAMED_MUTEX_USE_PTHREAD_MUTEX -} - -NamedMutexSharedData *NamedMutexProcessData::GetSharedData() const -{ - return reinterpret_cast(m_processDataHeader->GetSharedDataHeader()->GetData()); -} - -void NamedMutexProcessData::SetLockOwnerThread(CorUnix::CPalThread *lockOwnerThread) -{ - _ASSERTE(lockOwnerThread == nullptr || lockOwnerThread == GetCurrentPalThread()); - _ASSERTE(IsLockOwnedByCurrentThread()); - - m_lockOwnerThread = lockOwnerThread; -} - -NamedMutexProcessData *NamedMutexProcessData::GetNextInThreadOwnedNamedMutexList() const -{ - _ASSERTE(IsLockOwnedByCurrentThread()); - return m_nextInThreadOwnedNamedMutexList; -} - -void NamedMutexProcessData::SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next) -{ - _ASSERTE(IsLockOwnedByCurrentThread()); - m_nextInThreadOwnedNamedMutexList = next; -} - -MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(SharedMemorySystemCallErrors *errors, DWORD timeoutMilliseconds) -{ - NamedMutexSharedData *sharedData = GetSharedData(); - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX - MutexTryAcquireLockResult result = MutexHelpers::TryAcquireLock(errors, sharedData->GetLock(), timeoutMilliseconds); - if (result == MutexTryAcquireLockResult::TimedOut) - { - return result; - } - - // Check if a recursive lock was just taken. The recursion level is tracked manually so that the lock owner can be cleared - // at the appropriate time, see ReleaseLock(). - if (m_lockCount != 0) - { - _ASSERTE(IsLockOwnedByCurrentThread()); // otherwise, this thread would not have acquired the lock - _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this)); - - if (m_lockCount + 1 < m_lockCount) - { - MutexHelpers::ReleaseLock(sharedData->GetLock()); - throw SharedMemoryException(static_cast(NamedMutexError::MaximumRecursiveLocksReached)); - } - ++m_lockCount; - - // The lock is released upon acquiring a recursive lock from the thread that already owns the lock - MutexHelpers::ReleaseLock(sharedData->GetLock()); - - _ASSERTE(result != MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned); - _ASSERTE(!sharedData->IsAbandoned()); - return result; - } - - // The non-recursive case is handled below (skip the #else and see below that) -#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX - // If a timeout is specified, determine the start time - DWORD startTime = 0; - if (timeoutMilliseconds != static_cast(-1) && timeoutMilliseconds != 0) - { - startTime = (DWORD)minipal_lowres_ticks(); - } - - // Acquire the process lock. A file lock can only be acquired once per file descriptor, so to synchronize the threads of - // this process, the process lock is used. - while (true) - { - DWORD waitResult = WaitForSingleObject(m_processLockHandle, timeoutMilliseconds); - switch (waitResult) - { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: // abandoned state for the process lock is irrelevant, the shared lock will also have been abandoned - break; - - case WAIT_TIMEOUT: - return MutexTryAcquireLockResult::TimedOut; - - case WAIT_IO_COMPLETION: - continue; - - case WAIT_FAILED: - throw SharedMemoryException(GetLastError()); - - default: - _ASSERTE(false); - break; - } - break; - } - - struct AutoReleaseProcessLock - { - HANDLE m_processLockHandle; - bool m_cancel; - - AutoReleaseProcessLock(HANDLE processLockHandle) : m_processLockHandle(processLockHandle), m_cancel(false) - { - } - - ~AutoReleaseProcessLock() - { - if (!m_cancel) - { - ReleaseMutex(m_processLockHandle); - } - } - } autoReleaseProcessLock(m_processLockHandle); - - // Check if it's a recursive lock attempt - if (m_lockCount != 0) - { - _ASSERTE(IsLockOwnedByCurrentThread()); // otherwise, this thread would not have acquired the process lock - _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this)); - - if (m_lockCount + 1 < m_lockCount) - { - throw SharedMemoryException(static_cast(NamedMutexError::MaximumRecursiveLocksReached)); - } - ++m_lockCount; - - // The process lock is released upon acquiring a recursive lock from the thread that already owns the lock - return MutexTryAcquireLockResult::AcquiredLock; - } - - switch (timeoutMilliseconds) - { - case static_cast(-1): - { - // The file lock API does not have a timeout on the wait, so timed waiters will poll the file lock in a loop, - // sleeping for a short duration in-between. Due to the polling nature of a timed wait, timed waiters will almost - // never acquire the file lock as long as there are also untimed waiters. So, in order to make the file lock - // acquisition reasonable, when there are timed waiters, have untimed waiters also use polling. - bool acquiredFileLock = false; - while (sharedData->HasAnyTimedWaiters()) - { - if (SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) - { - acquiredFileLock = true; - break; - } - Sleep(PollLoopMaximumSleepMilliseconds); - } - if (acquiredFileLock) - { - break; - } - - acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX); - _ASSERTE(acquiredFileLock); - break; - } - - case 0: - if (!SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) - { - return MutexTryAcquireLockResult::TimedOut; - } - break; - - default: - { - // Try to acquire the file lock without waiting - if (SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)) - { - break; - } - - // The file lock API does not have a timeout on the wait, so timed waiters need to poll the file lock in a loop, - // sleeping for a short duration in-between. Due to the polling nature of a timed wait, timed waiters will almost - // never acquire the file lock as long as there are also untimed waiters. So, in order to make the file lock - // acquisition reasonable, record that there is a timed waiter, to have untimed waiters also use polling. - sharedData->IncTimedWaiterCount(); - struct AutoDecTimedWaiterCount - { - NamedMutexSharedData *m_sharedData; - - AutoDecTimedWaiterCount(NamedMutexSharedData *sharedData) : m_sharedData(sharedData) - { - } - - ~AutoDecTimedWaiterCount() - { - m_sharedData->DecTimedWaiterCount(); - } - } autoDecTimedWaiterCount(sharedData); - - // Poll for the file lock - do - { - DWORD elapsedMilliseconds = (DWORD)minipal_lowres_ticks() - startTime; - if (elapsedMilliseconds >= timeoutMilliseconds) - { - return MutexTryAcquireLockResult::TimedOut; - } - - DWORD remainingMilliseconds = timeoutMilliseconds - elapsedMilliseconds; - DWORD sleepMilliseconds = - remainingMilliseconds < PollLoopMaximumSleepMilliseconds - ? remainingMilliseconds - : PollLoopMaximumSleepMilliseconds; - Sleep(sleepMilliseconds); - } while (!SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB)); - break; - } - } - - // There cannot be any exceptions after this - autoReleaseProcessLock.m_cancel = true; - - // After acquiring the file lock, if we find that a lock owner is already designated, the process that previously owned the - // lock must have terminated while holding the lock. - MutexTryAcquireLockResult result = - sharedData->IsLockOwnedByAnyThread() - ? MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned - : MutexTryAcquireLockResult::AcquiredLock; -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX - - sharedData->SetLockOwnerToCurrentThread(); - m_lockCount = 1; - CPalThread *currentThread = GetCurrentPalThread(); - SetLockOwnerThread(currentThread); - currentThread->synchronizationInfo.AddOwnedNamedMutex(this); - - if (sharedData->IsAbandoned()) - { - // The thread that previously owned the lock did not release it before exiting - sharedData->SetIsAbandoned(false); - result = MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned; - } - return result; -} - -void NamedMutexProcessData::ReleaseLock() -{ - if (!IsLockOwnedByCurrentThread()) - { - throw SharedMemoryException(static_cast(NamedMutexError::ThreadHasNotAcquiredMutex)); - } - - _ASSERTE(GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this)); - _ASSERTE(!m_hasRefFromLockOwnerThread); - - _ASSERTE(m_lockCount != 0); - --m_lockCount; - if (m_lockCount != 0) - { - return; - } - - GetCurrentPalThread()->synchronizationInfo.RemoveOwnedNamedMutex(this); - SetLockOwnerThread(nullptr); - ActuallyReleaseLock(); -} - -void NamedMutexProcessData::Abandon() -{ - _ASSERTE(SharedMemoryManager::IsCreationDeletionProcessLockAcquired()); - - NamedMutexSharedData *sharedData = GetSharedData(); - _ASSERTE(IsLockOwnedByCurrentThread()); - _ASSERTE(m_lockCount != 0); - - sharedData->SetIsAbandoned(true); - m_lockCount = 0; - SetLockOwnerThread(nullptr); - ActuallyReleaseLock(); - - if (m_hasRefFromLockOwnerThread) - { - m_hasRefFromLockOwnerThread = false; - m_processDataHeader->DecRefCount(); - } -} - -void NamedMutexProcessData::ActuallyReleaseLock() -{ - _ASSERTE(IsLockOwnedByCurrentThread()); - _ASSERTE(!GetCurrentPalThread()->synchronizationInfo.OwnsNamedMutex(this)); - _ASSERTE(m_lockCount == 0); - - NamedMutexSharedData *sharedData = GetSharedData(); - sharedData->ClearLockOwner(); - -#if NAMED_MUTEX_USE_PTHREAD_MUTEX - MutexHelpers::ReleaseLock(sharedData->GetLock()); -#else // !NAMED_MUTEX_USE_PTHREAD_MUTEX - SharedMemoryHelpers::ReleaseFileLock(m_sharedLockFileDescriptor); - ReleaseMutex(m_processLockHandle); -#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX -} diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 206a9629189b5b..e0ea3f4b0c6c81 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -34,6 +34,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include #include +#include #include #if HAVE_POLL @@ -174,7 +175,6 @@ DWORD gSID = (DWORD) -1; LPCSTR gApplicationGroupId = nullptr; int gApplicationGroupIdLength = 0; #endif // __APPLE__ -PathCharString* gSharedFilesPath = nullptr; // The lowest common supported semaphore length, including null character // NetBSD-7.99.25: 15 characters diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index a6338791711233..5b96ab5fa81568 100644 --- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt @@ -415,8 +415,6 @@ add_executable_clr(paltests threading/GetCurrentThreadId/test1/threadId.cpp threading/GetExitCodeProcess/test1/childProcess.cpp threading/GetExitCodeProcess/test1/test1.cpp - threading/NamedMutex/test1/namedmutex.cpp - threading/NamedMutex/test1/nopal.cpp threading/OpenEventW/test1/test1.cpp threading/OpenEventW/test2/test2.cpp threading/OpenEventW/test3/childprocess.cpp @@ -459,8 +457,6 @@ add_executable_clr(paltests threading/WaitForMultipleObjectsEx/test4/test4.cpp threading/WaitForMultipleObjectsEx/test5/helper.cpp threading/WaitForMultipleObjectsEx/test5/test5.cpp - threading/WaitForMultipleObjectsEx/test6/child6.cpp - threading/WaitForMultipleObjectsEx/test6/test6.cpp threading/WaitForSingleObject/test1/test1.cpp threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.cpp threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.cpp diff --git a/src/coreclr/pal/tests/palsuite/compilableTests.txt b/src/coreclr/pal/tests/palsuite/compilableTests.txt index ee5ba036ce7a31..930d5979fa0318 100644 --- a/src/coreclr/pal/tests/palsuite/compilableTests.txt +++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt @@ -312,7 +312,6 @@ threading/GetCurrentThread/test1/paltest_getcurrentthread_test1 threading/GetCurrentThread/test2/paltest_getcurrentthread_test2 threading/GetCurrentThreadId/test1/paltest_getcurrentthreadid_test1 threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1 -threading/NamedMutex/test1/paltest_namedmutex_test1 threading/OpenEventW/test1/paltest_openeventw_test1 threading/OpenEventW/test2/paltest_openeventw_test2 threading/OpenEventW/test3/paltest_openeventw_test3 diff --git a/src/coreclr/pal/tests/palsuite/paltestlist.txt b/src/coreclr/pal/tests/palsuite/paltestlist.txt index b55a93735f16f2..62a943e4726b7c 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt @@ -269,7 +269,6 @@ threading/ExitThread/test1/paltest_exitthread_test1 threading/GetCurrentProcessId/test1/paltest_getcurrentprocessid_test1 threading/GetCurrentThread/test1/paltest_getcurrentthread_test1 threading/GetCurrentThread/test2/paltest_getcurrentthread_test2 -threading/NamedMutex/test1/paltest_namedmutex_test1 threading/QueryThreadCycleTime/test1/paltest_querythreadcycletime_test1 threading/QueueUserAPC/test2/paltest_queueuserapc_test2 threading/QueueUserAPC/test3/paltest_queueuserapc_test3 diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.cpp index ea50041168746e..5d41efb9d6fa9d 100644 --- a/src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.cpp @@ -37,8 +37,6 @@ #define szEmpty "" /* Function Prototypes */ -BOOL TestNamedMutex_CreateMutexW_ReleaseMutex_test2(const char *szMutexName); -DWORD NamedMutexThread_CreateMutexW_ReleaseMutex_test2(LPVOID lpParam); BOOL NegativeReleaseMutexTests_CreateMutexW_ReleaseMutex_test2(); struct ThreadData @@ -59,43 +57,6 @@ PALTEST(threading_CreateMutexW_ReleaseMutex_test2_paltest_createmutexw_releasemu return ( FAIL ); } - - /* - * Test named Mutexes with ordinary string - */ - - if (!TestNamedMutex_CreateMutexW_ReleaseMutex_test2(szMutex)) - { - bFailures = TRUE; - } - - - /* - * Test named Mutexes with empty ("") string - */ - - if (!TestNamedMutex_CreateMutexW_ReleaseMutex_test2(szEmpty)) - { - bFailures = TRUE; - } - - - /* - * Test named Mutexes with string of length MAX_LONGPATH - */ - - szMaxPath = (char *)malloc(MAX_LONGPATH+2); - memset(szMaxPath, 'A', MAX_LONGPATH-60); - szMaxPath[MAX_LONGPATH-60] = 0; - - if (!TestNamedMutex_CreateMutexW_ReleaseMutex_test2(szMaxPath)) - { - bFailures = TRUE; - } - - free(szMaxPath); - - /* * Run some negative tests on ReleaseMutex */ @@ -119,141 +80,6 @@ PALTEST(threading_CreateMutexW_ReleaseMutex_test2_paltest_createmutexw_releasemu return ( PASS ); } - -/* - * Testing Function - * - * Try to get multiple handles to a named Mutex and test - * to make sure they actually refer to same Mutex object. - */ -BOOL TestNamedMutex_CreateMutexW_ReleaseMutex_test2(const char *szMutexName) -{ - DWORD dwData; - HANDLE hMutex1; - HANDLE hMutex2; - HANDLE hThread; - WCHAR *swzMutexName; - THREADDATA threadData; - - /* Convert the Mutex name to wide characters */ - swzMutexName = convert((char *)szMutexName); - - /* Create a mutex and take ownership immediately */ - hMutex1 = CreateMutexW (NULL, TRUE, swzMutexName); - - if (NULL == hMutex1) - { - Trace("ERROR: CreateMutex #1 failed. GetLastError returned %u\n", - GetLastError()); - free(swzMutexName); - return FALSE; - } - - /* Try to wait on the Mutex we just created. We should not block. */ - if (WaitForSingleObject(hMutex1, 1000) == WAIT_TIMEOUT) - { - Trace("WaitForSingleObject blocked on a Mutex that we owned.\n"); - free(swzMutexName); - return FALSE; - } - /* We have to call ReleaseMutex here because of the Wait */ - if (ReleaseMutex(hMutex1) == FALSE) - { - Trace("ReleaseMutex Failed.\n"); - return FALSE; - } - - /* Get a second handle to the same mutex */ - hMutex2 = CreateMutexW (NULL, FALSE, swzMutexName); - - if (NULL == hMutex2) - { - Trace("ERROR: CreateMutex #2 failed. GetLastError returned %u\n", - GetLastError()); - free(swzMutexName); - return FALSE; - } - - /* Get rid of the wide character string */ - free(swzMutexName); - - /* - * Create a thread that will Wait on the second handle. - */ - threadData.hMutex = hMutex2; - hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)NamedMutexThread_CreateMutexW_ReleaseMutex_test2, - (LPVOID)&threadData, 0, &dwData); - - if (NULL == hThread) - { - Trace("ERROR: CreateThread failed. GetLastError returned %u\n", - GetLastError()); - return FALSE; - } - - /* Give the thread a little time to execute & wait*/ - Sleep(500); - - /* Signal the first handle */ - if (ReleaseMutex(hMutex1) == FALSE) - { - Trace("ReleaseMutex Failed.\n"); - return FALSE; - } - - /* Give the thread some time to finish */ - Sleep(2000); - - /* Clean Up */ - if (CloseHandle(hMutex1) == FALSE || - CloseHandle(hMutex2) == FALSE || - CloseHandle(hThread) == FALSE) - { - Trace("ERROR: CloseHandle failed.\n"); - return FALSE; - } - - /* Check the return code to see if signalling the first */ - /* Mutex handle woke up the thread which was Waiting on */ - /* the second handle. */ - if (threadData.bReturnCode != FALSE) - { - Trace("ERROR: The handles did not refer to the same Mutex object.\n"); - return FALSE; - } - - return TRUE; -} - - -/* - * Thread function used with above testing function. - */ -DWORD NamedMutexThread_CreateMutexW_ReleaseMutex_test2(LPVOID lpParam) -{ - BOOL bTimedOut = FALSE; - THREADDATA *lpThreadData = (THREADDATA *)lpParam; - - /* Wait on the Mutex that was passed to us */ - if (WaitForSingleObject(lpThreadData->hMutex, 10000) == WAIT_TIMEOUT) - { - /* The Mutex was not signaled in the allotted time */ - bTimedOut = TRUE; - } - if (ReleaseMutex(lpThreadData->hMutex) == FALSE) - { - Trace("ERROR: ReleaseMutex failed.\n"); - lpThreadData->bReturnCode = FALSE; - return 0; - } - - /* Indicate whether we timed out Waiting on the Mutex */ - lpThreadData->bReturnCode = bTimedOut; - - return 0; -} - - /* * Testing Function * diff --git a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp b/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp deleted file mode 100644 index 340a83e67d2104..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/namedmutex.cpp +++ /dev/null @@ -1,1361 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// These test cases test named mutexes, including positive -// and negative cases, cross - thread and cross - process, mutual -// exclusion, abandon detection, etc. - -#include - -const char CurrentSessionOnlyPrefix[] = "Local\\"; -const char AllSessionsPrefix[] = "Global\\"; - -const char NamePrefix[] = "paltest_namedmutex_test1_"; -const char TempNamePrefix[] = "paltest_namedmutex_test1_temp_"; -const char HeaderMismatchTestsNamePrefix[] = "paltest_namedmutex_test1_headermismatchtests_"; -const char InvalidNamePrefix0[] = "paltest\\namedmutex_"; -const char InvalidNamePrefix1[] = "paltest/namedmutex_"; -const char ParentEventNamePrefix0[] = "paltest_namedmutex_test1_pe0_"; -const char ParentEventNamePrefix1[] = "paltest_namedmutex_test1_pe1_"; -const char ChildEventNamePrefix0[] = "paltest_namedmutex_test1_ce0_"; -const char ChildEventNamePrefix1[] = "paltest_namedmutex_test1_ce1_"; -const char ChildRunningEventNamePrefix[] = "paltest_namedmutex_test1_cr_"; - -#define MaxPathSize 200 -const DWORD PollLoopSleepMilliseconds = 100; -const DWORD FailTimeoutMilliseconds = 30000; -DWORD g_expectedTimeoutMilliseconds = 500; - -bool g_isParent = true; -bool g_currentUserOnly = true; -bool g_currentSessionOnly = true; -bool g_isStress = false; -#define MaxProcessPathSize 4096 -char g_processPath[MaxProcessPathSize], g_processCommandLinePath[MaxProcessPathSize]; -DWORD g_parentPid = static_cast(-1); - -extern char *(*test_strcpy)(char *dest, const char *src); -extern int (*test_strcmp)(const char *s1, const char *s2); -extern size_t (*test_strlen)(const char *s); -extern int (*test_snprintf)(char *str, size_t size, const char *format, ...); -extern int (*test_sscanf)(const char *str, const char *format, ...); -extern int(*test_close)(int fd); -extern int (*test_unlink)(const char *pathname); -extern unsigned int test_getpid(); -extern unsigned int test_getsid(); -extern unsigned int test_geteuid(); -extern int test_kill(unsigned int pid); - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Test helpers - -extern bool TestFileExists(const char *path); -extern bool WriteHeaderInfo(const char *path, bool currentUserOnly, char sharedMemoryType, char version, int *fdRef); - -#define TestAssert(expression) \ - do \ - { \ - if (!(expression)) \ - { \ - if (!g_isParent) \ - { \ - Trace( \ - "'paltest_namedmutex_test1' child process failed at line %u. CurrentUserOnly: %d, CurrentSessionOnly: %d. Expression: " #expression "\n", \ - __LINE__, \ - (int)g_currentUserOnly, \ - (int)g_currentSessionOnly); \ - } \ - else \ - { \ - Trace( \ - "'paltest_namedmutex_test1' failed at line %u. CurrentUserOnly: %d, CurrentSessionOnly: %d. Expression: " #expression "\n", \ - __LINE__, \ - (int)g_currentUserOnly, \ - (int)g_currentSessionOnly); \ - } \ - fflush(stdout); \ - return false; \ - } \ - } while(false) - -char *BuildName(const char *testName, char *buffer, const char *namePrefix = nullptr) -{ - size_t nameLength = 0; - if (!g_currentSessionOnly) - { - test_strcpy(&buffer[nameLength], AllSessionsPrefix); - nameLength += STRING_LENGTH(AllSessionsPrefix); - } - - if (namePrefix != nullptr) - { - nameLength += test_snprintf(&buffer[nameLength], MaxPathSize - nameLength, "%s", namePrefix); - } - - if (g_isStress) - { - // Append the test name so that tests can run in parallel - nameLength += test_snprintf(&buffer[nameLength], MaxPathSize - nameLength, "%s_", testName); - } - - nameLength += test_snprintf(&buffer[nameLength], MaxPathSize - nameLength, "%u", g_parentPid); - return buffer; -} - -char *BuildShmFilePath(const char *testName, char *buffer, const char *namePrefix) -{ - size_t pathLength = 0; - if (g_currentUserOnly) - { - pathLength += test_snprintf(&buffer[pathLength], MaxPathSize - pathLength, "/tmp/.dotnet-uid%u/shm/", test_geteuid()); - } - else - { - pathLength += test_snprintf(&buffer[pathLength], MaxPathSize - pathLength, "%s", "/tmp/.dotnet/shm/"); - } - - if (g_currentSessionOnly) - { - pathLength += test_snprintf(&buffer[pathLength], MaxPathSize - pathLength, "session%u/", test_getsid()); - } - else - { - pathLength += test_snprintf(&buffer[pathLength], MaxPathSize - pathLength, "%s", "global/"); - } - - pathLength += test_snprintf(&buffer[pathLength], MaxPathSize - pathLength, "%s", namePrefix); - - if (g_isStress) - { - // Append the test name so that tests can run in parallel - pathLength += test_snprintf(&buffer[pathLength], MaxPathSize - pathLength, "%s_", testName); - } - - pathLength += test_snprintf(&buffer[pathLength], MaxPathSize - pathLength, "%u", g_parentPid); - return buffer; -} - -class AutoCloseMutexHandle -{ -private: - HANDLE m_handle; - -public: - AutoCloseMutexHandle(HANDLE handle = nullptr) : m_handle(handle) - { - } - - ~AutoCloseMutexHandle() - { - Close(); - } - -public: - HANDLE GetHandle() const - { - return m_handle; - } - - bool Release() - { - return !!ReleaseMutex(m_handle); - } - - void Close() - { - if (m_handle != nullptr) - { - CloseHandle(m_handle); - m_handle = nullptr; - } - } - - void Abandon() - { - // Don't close the handle - m_handle = nullptr; - } - - AutoCloseMutexHandle &operator =(HANDLE handle) - { - Close(); - m_handle = handle; - return *this; - } - - operator HANDLE() const - { - return m_handle; - } - -private: - AutoCloseMutexHandle(const AutoCloseMutexHandle &other); - AutoCloseMutexHandle(AutoCloseMutexHandle &&other); - AutoCloseMutexHandle &operator =(const AutoCloseMutexHandle &other); -}; - -void TestCreateMutex(AutoCloseMutexHandle &m, const char *name, bool initiallyOwned = false) -{ - m.Close(); - LPWSTR nameW = convert(name); - m = PAL_CreateMutexW(initiallyOwned, nameW, g_currentUserOnly, nullptr, 0); - free(nameW); -} - -HANDLE TestOpenMutex(const char *name) -{ - LPWSTR nameW = convert(name); - HANDLE h = PAL_OpenMutexW(nameW, g_currentUserOnly, nullptr, 0); - free(nameW); - return h; -} - -bool StartProcess(const char *funcName) -{ - // Command line format: - // <0|1> /* currentUserOnly */ <0|1> /* currentSessionOnly */ [stress] - test_snprintf( - g_processCommandLinePath, - MaxProcessPathSize, - "\"%s\" %s %u %s %u %u%s", - g_processPath, - "threading/NamedMutex/test1/paltest_namedmutex_test1", - g_parentPid, - funcName, - g_currentUserOnly ? 1 : 0, - g_currentSessionOnly ? 1 : 0, - g_isStress ? " stress" : ""); - - STARTUPINFO si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - PROCESS_INFORMATION pi; - memset(&pi, 0, sizeof(pi)); - LPWSTR nameW = convert(g_processCommandLinePath); - if (!CreateProcessW(nullptr, nameW, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi)) - { - free(nameW); - return false; - } - - free(nameW); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - return true; -} - -bool StartThread(LPTHREAD_START_ROUTINE func, void *arg = nullptr, HANDLE *threadHandleRef = nullptr) -{ - DWORD threadId; - HANDLE handle = CreateThread(nullptr, 0, func, arg, 0, &threadId); - if (handle != nullptr) - { - if (threadHandleRef == nullptr) - { - CloseHandle(handle); - } - else - { - *threadHandleRef = handle; - } - return true; - } - return false; -} - -bool WaitForMutexToBeCreated(const char *testName, AutoCloseMutexHandle &m, const char *eventNamePrefix) -{ - char eventName[MaxPathSize]; - BuildName(testName, eventName, eventNamePrefix); - DWORD startTime = (DWORD)minipal_lowres_ticks(); - while (true) - { - m = TestOpenMutex(eventName); - if (m != nullptr) - { - return true; - } - if ((DWORD)minipal_lowres_ticks() - startTime >= FailTimeoutMilliseconds) - { - return false; - } - Sleep(PollLoopSleepMilliseconds); - } -} - -// The following functions are used for parent/child tests, where the child runs in a separate thread or process. The tests are -// organized such that one the parent or child is ever running code, and they yield control and wait for the other. Since the -// named mutex is the only type of cross-process sync object available, they are used as events to synchronize. The parent and -// child have a pair of event mutexes each, which they own initially. To release the other waiting thread/process, the -// thread/process releases one of its mutexes, which the other thread/process would be waiting on. To wait, the thread/process -// waits on one of the other thread/process' mutexes. All the while, they ping-pong between the two mutexes. YieldToChild() and -// YieldToParent() below control the releasing, waiting, and ping-ponging, to help create a deterministic path through the -// parent and child tests while both are running concurrently. - -bool AcquireChildRunningEvent(const char *testName, AutoCloseMutexHandle &childRunningEvent) -{ - char name[MaxPathSize]; - TestCreateMutex(childRunningEvent, BuildName(testName, name, ChildRunningEventNamePrefix)); - TestAssert(WaitForSingleObject(childRunningEvent, FailTimeoutMilliseconds) == WAIT_OBJECT_0); - return true; -} - -bool InitializeParent(const char *testName, AutoCloseMutexHandle parentEvents[2], AutoCloseMutexHandle childEvents[2]) -{ - // Create parent events - char name[MaxPathSize]; - for (int i = 0; i < 2; ++i) - { - TestCreateMutex( - parentEvents[i], - BuildName(testName, name, i == 0 ? ParentEventNamePrefix0 : ParentEventNamePrefix1), - true); - TestAssert(parentEvents[i] != nullptr); - TestAssert(GetLastError() != ERROR_ALREADY_EXISTS); - } - - // Wait for the child to create and acquire locks on its events so that the parent can wait on them - TestAssert(WaitForMutexToBeCreated(testName, childEvents[0], ChildEventNamePrefix0)); - TestAssert(WaitForMutexToBeCreated(testName, childEvents[1], ChildEventNamePrefix1)); - return true; -} - -bool UninitializeParent(const char *testName, AutoCloseMutexHandle parentEvents[2], bool releaseParentEvents = true) -{ - if (releaseParentEvents) - { - TestAssert(parentEvents[0].Release()); - TestAssert(parentEvents[1].Release()); - } - - // Wait for the child to finish its test. Child tests will release and close 'childEvents' before releasing - // 'childRunningEvent', so after this wait, the parent process can freely start another child that will deterministically - // recreate the 'childEvents', which the next parent test will wait on, upon its initialization. - AutoCloseMutexHandle childRunningEvent; - TestAssert(AcquireChildRunningEvent(testName, childRunningEvent)); - TestAssert(childRunningEvent.Release()); - return true; -} - -bool InitializeChild( - const char *testName, - AutoCloseMutexHandle &childRunningEvent, - AutoCloseMutexHandle parentEvents[2], - AutoCloseMutexHandle childEvents[2]) -{ - TestAssert(AcquireChildRunningEvent(testName, childRunningEvent)); - - // Create child events - char name[MaxPathSize]; - for (int i = 0; i < 2; ++i) - { - TestCreateMutex( - childEvents[i], - BuildName(testName, name, i == 0 ? ChildEventNamePrefix0 : ChildEventNamePrefix1), - true); - TestAssert(childEvents[i] != nullptr); - TestAssert(GetLastError() != ERROR_ALREADY_EXISTS); - } - - // Wait for the parent to create and acquire locks on its events so that the child can wait on them - TestAssert(WaitForMutexToBeCreated(testName, parentEvents[0], ParentEventNamePrefix0)); - TestAssert(WaitForMutexToBeCreated(testName, parentEvents[1], ParentEventNamePrefix1)); - - // Parent/child tests start with the parent, so after initialization, wait for the parent to tell the child test to start - TestAssert(WaitForSingleObject(parentEvents[0], FailTimeoutMilliseconds) == WAIT_OBJECT_0); - TestAssert(parentEvents[0].Release()); - return true; -} - -bool UninitializeChild( - AutoCloseMutexHandle &childRunningEvent, - AutoCloseMutexHandle parentEvents[2], - AutoCloseMutexHandle childEvents[2]) -{ - // Release and close 'parentEvents' and 'childEvents' before releasing 'childRunningEvent' to avoid races, see - // UninitializeParent() for more info - TestAssert(childEvents[0].Release()); - TestAssert(childEvents[1].Release()); - childEvents[0].Close(); - childEvents[1].Close(); - parentEvents[0].Close(); - parentEvents[1].Close(); - TestAssert(childRunningEvent.Release()); - return true; -} - -bool YieldToChild(AutoCloseMutexHandle parentEvents[2], AutoCloseMutexHandle childEvents[2], int &ei) -{ - TestAssert(parentEvents[ei].Release()); - TestAssert(WaitForSingleObject(childEvents[ei], FailTimeoutMilliseconds) == WAIT_OBJECT_0); - TestAssert(childEvents[ei].Release()); - TestAssert(WaitForSingleObject(parentEvents[ei], 0) == WAIT_OBJECT_0); - ei = 1 - ei; - return true; -} - -bool YieldToParent(AutoCloseMutexHandle parentEvents[2], AutoCloseMutexHandle childEvents[2], int &ei) -{ - TestAssert(childEvents[ei].Release()); - ei = 1 - ei; - TestAssert(WaitForSingleObject(parentEvents[ei], FailTimeoutMilliseconds) == WAIT_OBJECT_0); - TestAssert(parentEvents[ei].Release()); - TestAssert(WaitForSingleObject(childEvents[1 - ei], 0) == WAIT_OBJECT_0); - return true; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Tests - -bool NameTests() -{ - const char *testName = "NameTests"; - - AutoCloseMutexHandle m; - char name[MaxPathSize]; - - // Empty name - TestCreateMutex(m, ""); - TestAssert(m != nullptr); - - // Normal name - BuildName(testName, name, NamePrefix); - TestCreateMutex(m, name); - TestAssert(m != nullptr); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(name)) != nullptr); - if (g_currentSessionOnly) - { - // When creating or opening a mutex scoped to the current session, the prefix ("Local\") is optional - char nameWithExplicitPrefix[MaxPathSize]; - test_strcpy(nameWithExplicitPrefix, CurrentSessionOnlyPrefix); - BuildName(testName, &nameWithExplicitPrefix[STRING_LENGTH(CurrentSessionOnlyPrefix)], NamePrefix); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(nameWithExplicitPrefix)) != nullptr); - TestCreateMutex(m, nameWithExplicitPrefix); - TestAssert(m != nullptr); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(name)) != nullptr); - } - - // Name too long. The maximum allowed path length depends on the file system, so we're not checking for that. - if(g_currentSessionOnly) - { - char name[257]; - memset(name, 'a', STRING_LENGTH(name)); - name[STRING_LENGTH(name)] = '\0'; - TestCreateMutex(m, name); - TestAssert(m == nullptr); - TestAssert(GetLastError() == ERROR_FILENAME_EXCED_RANGE); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(name)) == nullptr); - TestAssert(GetLastError() == ERROR_FILENAME_EXCED_RANGE); - - name[STRING_LENGTH(name) - 1] = '\0'; - TestCreateMutex(m, name); - TestAssert(m != nullptr); - } - else - { - char name[STRING_LENGTH(AllSessionsPrefix) + 257]; - test_strcpy(name, AllSessionsPrefix); - memset(&name[STRING_LENGTH(AllSessionsPrefix)], 'a', STRING_LENGTH(name) - STRING_LENGTH(AllSessionsPrefix)); - name[STRING_LENGTH(name)] = '\0'; - TestCreateMutex(m, name); - TestAssert(m == nullptr); - TestAssert(GetLastError() == ERROR_FILENAME_EXCED_RANGE); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(name)) == nullptr); - TestAssert(GetLastError() == ERROR_FILENAME_EXCED_RANGE); - - name[STRING_LENGTH(name) - 1] = '\0'; - TestCreateMutex(m, name); - TestAssert(m != nullptr); - } - - // Invalid characters in name - BuildName(testName, name, InvalidNamePrefix0); - TestCreateMutex(m, name); - TestAssert(m == nullptr); - TestAssert(GetLastError() == ERROR_INVALID_NAME); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(name)) == nullptr); - TestAssert(GetLastError() == ERROR_INVALID_NAME); - BuildName(testName, name, InvalidNamePrefix1); - TestCreateMutex(m, name); - TestAssert(m == nullptr); - TestAssert(GetLastError() == ERROR_INVALID_NAME); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(name)) == nullptr); - TestAssert(GetLastError() == ERROR_INVALID_NAME); - - // Creating a second reference to the same named mutex yields an error indicating that it was opened, not created - { - BuildName(testName, name, NamePrefix); - TestCreateMutex(m, name); - TestAssert(m != nullptr); - AutoCloseMutexHandle m2; - TestCreateMutex(m2, name); - TestAssert(m2 != nullptr); - TestAssert(GetLastError() == ERROR_ALREADY_EXISTS); - } - - return true; -} - -bool HeaderMismatchTests() -{ - const char *testName = "HeaderMismatchTests"; - - AutoCloseMutexHandle m, m2; - char name[MaxPathSize], path[MaxPathSize]; - int fd; - - // Create and hold onto a mutex during this test to create the shared memory directory - TestCreateMutex(m2, BuildName(testName, name, TempNamePrefix)); - TestAssert(m2 != nullptr); - - // Init name and path for the remaining tests - BuildName(testName, name, HeaderMismatchTestsNamePrefix); - BuildShmFilePath(testName, path, HeaderMismatchTestsNamePrefix); - - // Unknown shared memory type - TestAssert(WriteHeaderInfo(path, g_currentUserOnly, -1, 1, &fd)); - TestCreateMutex(m, name); - TestAssert(m == nullptr); - TestAssert(GetLastError() == ERROR_INVALID_HANDLE); - TestAssert(test_close(fd) == 0); - TestAssert(test_unlink(path) == 0); - - // Mismatched version - TestAssert(WriteHeaderInfo(path, g_currentUserOnly, 0, -1, &fd)); - TestCreateMutex(m, name); - TestAssert(m == nullptr); - TestAssert(GetLastError() == ERROR_INVALID_HANDLE); - TestAssert(test_close(fd) == 0); - TestAssert(test_unlink(path) == 0); - - return true; -} - -bool MutualExclusionTests_Parent() -{ - const char *testName = "MutualExclusionTests"; - - AutoCloseMutexHandle parentEvents[2], childEvents[2]; - TestAssert(InitializeParent(testName, parentEvents, childEvents)); - int ei = 0; - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - - // Recursive locking with various timeouts - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); - TestAssert(WaitForSingleObject(m, FailTimeoutMilliseconds) == WAIT_OBJECT_0); - TestAssert(WaitForSingleObject(m, static_cast(-1)) == WAIT_OBJECT_0); - TestAssert(m.Release()); - TestAssert(m.Release()); - TestAssert(m.Release()); - TestAssert(!m.Release()); // try to release the lock while nobody owns it, and verify recursive lock counting - TestAssert(GetLastError() == ERROR_NOT_OWNER); - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child takes the lock - - TestAssert(WaitForSingleObject(m, 0) == WAIT_TIMEOUT); // try to lock the mutex without waiting - TestAssert(WaitForSingleObject(m, g_expectedTimeoutMilliseconds) == WAIT_TIMEOUT); // try to lock the mutex with a timeout - TestAssert(!m.Release()); // try to release the lock while another thread owns it - TestAssert(GetLastError() == ERROR_NOT_OWNER); - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child releases the lock - - TestAssert(WaitForSingleObject(m, static_cast(-1)) == WAIT_OBJECT_0); // lock the mutex with no timeout and release - TestAssert(m.Release()); - - TestAssert(UninitializeParent(testName, parentEvents)); - return true; -} - -DWORD PALAPI MutualExclusionTests_Child(void *arg = nullptr) -{ - const char *testName = "MutualExclusionTests"; - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); // lock the mutex - YieldToParent(parentEvents, childEvents, ei); // parent attempts to lock/release, and fails - TestAssert(m.Release()); // release the lock - } - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -bool MutualExclusionTests() -{ - const char *testName = "MutualExclusionTests"; - - { - AutoCloseMutexHandle m; - char name[MaxPathSize]; - - // Releasing a lock that is not owned by any thread fails - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(!m.Release()); - TestAssert(GetLastError() == ERROR_NOT_OWNER); - - // Acquire a lock during upon creation, and release - TestCreateMutex(m, BuildName(testName, name, NamePrefix), true); - TestAssert(m != nullptr); - TestAssert(m.Release()); - - // Multi-waits including a named mutex are not supported - AutoCloseMutexHandle m2; - TestCreateMutex(m2, nullptr); - TestAssert(m2 != nullptr); - HANDLE waitHandles[] = {m2.GetHandle(), m.GetHandle()}; - TestAssert( - WaitForMultipleObjects( - ARRAY_SIZE(waitHandles), - waitHandles, - false /* waitAll */, - FailTimeoutMilliseconds) == - WAIT_FAILED); - TestAssert(GetLastError() == ERROR_NOT_SUPPORTED); - TestAssert( - WaitForMultipleObjects( - ARRAY_SIZE(waitHandles), - waitHandles, - true /* waitAll */, - FailTimeoutMilliseconds) == - WAIT_FAILED); - TestAssert(GetLastError() == ERROR_NOT_SUPPORTED); - } - - // When another thread or process owns the lock, this process should not be able to acquire a lock, and the converse - TestAssert(StartThread(MutualExclusionTests_Child)); - TestAssert(MutualExclusionTests_Parent()); - TestAssert(StartProcess("MutualExclusionTests_Child")); - TestAssert(MutualExclusionTests_Parent()); - - return true; -} - -bool LifetimeTests_Parent() -{ - const char *testName = "LifetimeTests"; - - AutoCloseMutexHandle parentEvents[2], childEvents[2]; - TestAssert(InitializeParent(testName, parentEvents, childEvents)); - int ei = 0; - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); // create first reference to mutex - TestAssert(m != nullptr); - TestAssert(TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child creates second reference to mutex using CreateMutex - m.Close(); // close first reference - TestAssert(TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child closes second reference - TestAssert(!TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); // create first reference to mutex - TestAssert(m != nullptr); - TestAssert(TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child creates second reference to mutex using OpenMutex - m.Close(); // close first reference - TestAssert(TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child closes second reference - TestAssert(!TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - - TestAssert(UninitializeParent(testName, parentEvents)); - return true; -} - -DWORD PALAPI LifetimeTests_Child(void *arg = nullptr) -{ - const char *testName = "LifetimeTests"; - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - // ... parent creates first reference to mutex - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); // create second reference to mutex using CreateMutex - TestAssert(m != nullptr); - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent closes first reference - m.Close(); // close second reference - - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent verifies, and creates first reference to mutex again - m = TestOpenMutex(BuildName(testName, name, NamePrefix)); // create second reference to mutex using OpenMutex - TestAssert(m != nullptr); - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent closes first reference - m.Close(); // close second reference - - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent verifies - } - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -bool LifetimeTests() -{ - const char *testName = "LifetimeTests"; - - { - AutoCloseMutexHandle m; - char name[MaxPathSize]; - - // Shm file should be created and deleted - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - m.Close(); - TestAssert(!TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - } - - // Shm file should not be deleted until last reference is released - TestAssert(StartThread(LifetimeTests_Child)); - TestAssert(LifetimeTests_Parent()); - TestAssert(StartProcess("LifetimeTests_Child")); - TestAssert(LifetimeTests_Parent()); - - return true; -} - -DWORD PALAPI AbandonTests_Child_TryLock(void *arg = nullptr); - -bool AbandonTests_Parent() -{ - const char *testName = "AbandonTests"; - - char name[MaxPathSize]; - AutoCloseMutexHandle m; - { - AutoCloseMutexHandle parentEvents[2], childEvents[2]; - TestAssert(InitializeParent(testName, parentEvents, childEvents)); - int ei = 0; - - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child locks mutex - TestAssert(parentEvents[0].Release()); - TestAssert(parentEvents[1].Release()); // child sleeps for short duration and abandons the mutex - TestAssert(WaitForSingleObject(m, FailTimeoutMilliseconds) == WAIT_ABANDONED_0); // attempt to lock and see abandoned mutex - - TestAssert(UninitializeParent(testName, parentEvents, false /* releaseParentEvents */)); // parent events are released above - } - - // Verify that the mutex lock is owned by this thread, by starting a new thread and trying to lock it - TestAssert(StartThread(AbandonTests_Child_TryLock)); - { - AutoCloseMutexHandle parentEvents[2], childEvents[2]; - TestAssert(InitializeParent(testName, parentEvents, childEvents)); - int ei = 0; - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child tries to lock mutex - - TestAssert(UninitializeParent(testName, parentEvents)); - } - - // Verify that the mutex lock is owned by this thread, by starting a new process and trying to lock it - TestAssert(StartProcess("AbandonTests_Child_TryLock")); - AutoCloseMutexHandle parentEvents[2], childEvents[2]; - TestAssert(InitializeParent(testName, parentEvents, childEvents)); - int ei = 0; - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child tries to lock mutex - - // Continue verification - TestAssert(m.Release()); - TestAssert(WaitForSingleObject(m, FailTimeoutMilliseconds) == WAIT_OBJECT_0); // lock again to see it's not abandoned anymore - TestAssert(m.Release()); - - TestAssert(UninitializeParent(testName, parentEvents)); - - // Since the child abandons the mutex, and a child process may not release the file lock on the shared memory file before - // indicating completion to the parent, make sure to delete the shared memory file by repeatedly opening/closing the mutex - // until the parent process becomes the last process to reference the mutex and closing it deletes the file. - DWORD startTime = (DWORD)minipal_lowres_ticks(); - while (true) - { - m.Close(); - if (!TestFileExists(BuildShmFilePath(testName, name, NamePrefix))) - { - break; - } - - TestAssert((DWORD)minipal_lowres_ticks() - startTime < FailTimeoutMilliseconds); - m = TestOpenMutex(BuildName(testName, name, NamePrefix)); - } - - return true; -} - -DWORD PALAPI AbandonTests_Child_GracefulExit_Close(void *arg = nullptr) -{ - const char *testName = "AbandonTests"; - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - // ... parent waits for child to lock mutex - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits on mutex - Sleep(g_expectedTimeoutMilliseconds); // wait for parent to wait on mutex - m.Close(); // close mutex without releasing lock - } - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -DWORD AbandonTests_Child_GracefulExit_NoClose(void *arg = nullptr) -{ - const char *testName = "AbandonTests"; - - // This test needs to run in a separate process because it does not close the mutex handle. Running it in a separate thread - // causes the mutex object to retain a reference until the process terminates. - TestAssert(test_getpid() != g_parentPid); - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - // ... parent waits for child to lock mutex - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits on mutex - Sleep(g_expectedTimeoutMilliseconds); // wait for parent to wait on mutex - m.Abandon(); // don't close the mutex - } - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -DWORD AbandonTests_Child_AbruptExit(void *arg = nullptr) -{ - const char *testName = "AbandonTests"; - - DWORD currentPid = test_getpid(); - TestAssert(currentPid != g_parentPid); // this test needs to run in a separate process - - { - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - // ... parent waits for child to lock mutex - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits on mutex - Sleep(g_expectedTimeoutMilliseconds); // wait for parent to wait on mutex - m.Abandon(); // don't close the mutex - } - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - } - - TestAssert(test_kill(currentPid) == 0); // abandon the mutex abruptly - return 0; -} - -// This child process acquires the mutex lock, creates another child process (to ensure that file locks are not inherited), and -// abandons the mutex abruptly. The second child process detects the abandonment and abandons the mutex again for the parent to -// detect. Issue: https://github.com/dotnet/runtime/issues/11636 -DWORD AbandonTests_Child_FileLocksNotInherited_Parent_AbruptExit(void *arg = nullptr) -{ - const char *testName = "AbandonTests"; - - DWORD currentPid = test_getpid(); - TestAssert(currentPid != g_parentPid); // this test needs to run in a separate process - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - // ... root parent waits for child to lock mutex - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); - - // Start a child process while holding the lock on the mutex, to ensure that file locks are not inherited by the - // immediate child, such that the immediate child would be able to detect the mutex being abandoned below. This process - // does not communicate with the root parent, it only communicates to the immediate child by abandoning the mutex. The - // immediate child communicates with the root parent to complete the test. - TestAssert(StartProcess("AbandonTests_Child_FileLocksNotInherited_Child_AbruptExit")); // immediate child waits on mutex - - Sleep(g_expectedTimeoutMilliseconds); // wait for immediate child to wait on mutex - m.Abandon(); // don't close the mutex - } - - TestAssert(test_kill(currentPid) == 0); // abandon the mutex abruptly - return 0; -} - -DWORD AbandonTests_Child_FileLocksNotInherited_Child_AbruptExit(void *arg = nullptr) -{ - const char *testName = "AbandonTests"; - - DWORD currentPid = test_getpid(); - TestAssert(currentPid != g_parentPid); // this test needs to run in a separate process - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - // ... immediate parent expects child to wait on mutex - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, FailTimeoutMilliseconds) == WAIT_ABANDONED_0); // attempt to lock and see abandoned mutex - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // root parent waits on mutex - Sleep(g_expectedTimeoutMilliseconds); // wait for root parent to wait on mutex - m.Close(); // close mutex without releasing lock (root parent expects the mutex to be abandoned) - } - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -DWORD PALAPI AbandonTests_Child_TryLock(void *arg) -{ - const char *testName = "AbandonTests"; - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - - { - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - // ... parent waits for child to lock mutex - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_TIMEOUT); // try to lock the mutex while the parent holds the lock - TestAssert(WaitForSingleObject(m, g_expectedTimeoutMilliseconds) == WAIT_TIMEOUT); - } - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -bool AbandonTests() -{ - // Abandon by graceful exit where the lock owner closes the mutex before releasing it, unblocks a waiter - TestAssert(StartThread(AbandonTests_Child_GracefulExit_Close)); - TestAssert(AbandonTests_Parent()); - TestAssert(StartProcess("AbandonTests_Child_GracefulExit_Close")); - TestAssert(AbandonTests_Parent()); - - // Abandon by graceful exit without closing the mutex unblocks a waiter - TestAssert(StartProcess("AbandonTests_Child_GracefulExit_NoClose")); - TestAssert(AbandonTests_Parent()); - - // Abandon by abrupt exit unblocks a waiter - TestAssert(StartProcess("AbandonTests_Child_AbruptExit")); - TestAssert(AbandonTests_Parent()); - - TestAssert(StartProcess("AbandonTests_Child_FileLocksNotInherited_Parent_AbruptExit")); - TestAssert(AbandonTests_Parent()); - - return true; -} - -bool LockAndCloseWithoutThreadExitTests_Parent_CloseOnSameThread() -{ - const char *testName = "LockAndCloseWithoutThreadExitTests"; - - AutoCloseMutexHandle parentEvents[2], childEvents[2]; - TestAssert(InitializeParent(testName, parentEvents, childEvents)); - int ei = 0; - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child locks mutex and closes second reference to mutex on lock-owner thread - TestAssert(WaitForSingleObject(m, 0) == WAIT_TIMEOUT); // attempt to lock and fail - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child closes last reference to mutex on lock-owner thread - TestAssert(WaitForSingleObject(m, 0) == WAIT_ABANDONED_0); // attempt to lock and see abandoned mutex - TestAssert(m.Release()); - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child exits - TestAssert(TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - m.Close(); - TestAssert(!TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - - TestAssert(UninitializeParent(testName, parentEvents)); - return true; -} - -DWORD PALAPI LockAndCloseWithoutThreadExitTests_Child_CloseOnSameThread(void *arg = nullptr) -{ - const char *testName = "LockAndCloseWithoutThreadExitTests"; - - TestAssert(test_getpid() != g_parentPid); // this test needs to run in a separate process - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - char name[MaxPathSize]; - - // ... parent waits for child to lock and close second reference to mutex - AutoCloseMutexHandle m(TestOpenMutex(BuildName(testName, name, NamePrefix))); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, NamePrefix))) != nullptr); - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits for child to close last reference to mutex - - m.Close(); // close mutex on lock-owner thread without releasing lock - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent verifies while this thread is still active - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -DWORD PALAPI LockAndCloseWithoutThreadExitTests_ChildThread_CloseMutex(void *arg); - -bool LockAndCloseWithoutThreadExitTests_Parent_CloseOnDifferentThread() -{ - const char *testName = "LockAndCloseWithoutThreadExitTests"; - - AutoCloseMutexHandle parentEvents[2], childEvents[2]; - TestAssert(InitializeParent(testName, parentEvents, childEvents)); - int ei = 0; - char name[MaxPathSize]; - AutoCloseMutexHandle m; - - TestCreateMutex(m, BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child locks mutex and closes second reference to mutex on lock-owner thread - TestAssert(WaitForSingleObject(m, 0) == WAIT_TIMEOUT); // attempt to lock and fail - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child closes last reference to mutex on non-lock-owner thread - TestAssert(WaitForSingleObject(m, 0) == WAIT_TIMEOUT); // attempt to lock and fail - m.Close(); - m = TestOpenMutex(BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); // child has implicit reference to mutex - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child closes new reference to mutex on lock-owner thread - TestAssert(WaitForSingleObject(m, 0) == WAIT_ABANDONED_0); // attempt to lock and see abandoned mutex - TestAssert(m.Release()); - - TestAssert(YieldToChild(parentEvents, childEvents, ei)); // child exits - TestAssert(TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - m.Close(); - TestAssert(!TestFileExists(BuildShmFilePath(testName, name, NamePrefix))); - - TestAssert(UninitializeParent(testName, parentEvents)); - return true; -} - -DWORD PALAPI LockAndCloseWithoutThreadExitTests_Child_CloseOnDifferentThread(void *arg = nullptr) -{ - const char *testName = "LockAndCloseWithoutThreadExitTests"; - - TestAssert(test_getpid() != g_parentPid); // this test needs to run in a separate process - - AutoCloseMutexHandle childRunningEvent, parentEvents[2], childEvents[2]; - TestAssert(InitializeChild(testName, childRunningEvent, parentEvents, childEvents)); - int ei = 0; - char name[MaxPathSize]; - - // ... parent waits for child to lock and close second reference to mutex - AutoCloseMutexHandle m(TestOpenMutex(BuildName(testName, name, NamePrefix))); - TestAssert(m != nullptr); - TestAssert(WaitForSingleObject(m, 0) == WAIT_OBJECT_0); - TestAssert(AutoCloseMutexHandle(TestOpenMutex(BuildName(testName, name, NamePrefix))) != nullptr); - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent waits for child to close last reference to mutex - - // Close the mutex on a thread that is not the lock-owner thread, without releasing the lock - HANDLE closeMutexThread = nullptr; - TestAssert(StartThread(LockAndCloseWithoutThreadExitTests_ChildThread_CloseMutex, (HANDLE)m, &closeMutexThread)); - TestAssert(closeMutexThread != nullptr); - TestAssert(WaitForSingleObject(closeMutexThread, FailTimeoutMilliseconds) == WAIT_OBJECT_0); - TestAssert(CloseHandle(closeMutexThread)); - m.Abandon(); // mutex is already closed, don't close it again - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent verifies while this lock-owner thread is still active - - m = TestOpenMutex(BuildName(testName, name, NamePrefix)); - TestAssert(m != nullptr); - m.Close(); // close mutex on lock-owner thread without releasing lock - TestAssert(YieldToParent(parentEvents, childEvents, ei)); // parent verifies while this thread is still active - - TestAssert(UninitializeChild(childRunningEvent, parentEvents, childEvents)); - return 0; -} - -DWORD PALAPI LockAndCloseWithoutThreadExitTests_ChildThread_CloseMutex(void *arg) -{ - TestAssert(arg != nullptr); - AutoCloseMutexHandle((HANDLE)arg).Close(); - return 0; -} - -bool LockAndCloseWithoutThreadExitTests() -{ - TestAssert(StartProcess("LockAndCloseWithoutThreadExitTests_Child_CloseOnSameThread")); - TestAssert(LockAndCloseWithoutThreadExitTests_Parent_CloseOnSameThread()); - - TestAssert(StartProcess("LockAndCloseWithoutThreadExitTests_Child_CloseOnDifferentThread")); - TestAssert(LockAndCloseWithoutThreadExitTests_Parent_CloseOnDifferentThread()); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Test harness - -bool (*const TestList[])() = -{ - NameTests, - HeaderMismatchTests, - MutualExclusionTests, - LifetimeTests, - AbandonTests, - LockAndCloseWithoutThreadExitTests -}; - -bool RunTests() -{ - const bool Bools[] = {false, true}; - bool allPassed = true; - for (int i = 0; i < ARRAY_SIZE(TestList); i++) - { - for (int j = 0; j < ARRAY_SIZE(Bools); j++) - { - g_currentUserOnly = Bools[j]; - for (int k = 0; k < ARRAY_SIZE(Bools); k++) - { - g_currentSessionOnly = Bools[k]; - if (!TestList[i]()) - { - allPassed = false; - } - } - } - } - - return allPassed; -} - -DWORD g_stressDurationMilliseconds = 0; -LONG g_stressTestCounts[ARRAY_SIZE(TestList)] = {0}; -LONG g_stressResult = true; - -DWORD PALAPI StressTest(void *arg) -{ - // Run the specified test continuously for the stress duration - SIZE_T testIndex = reinterpret_cast(arg); - DWORD startTime = (DWORD)minipal_lowres_ticks(); - do - { - ++g_stressTestCounts[testIndex]; - if (!TestList[testIndex]()) - { - InterlockedExchange(&g_stressResult, false); - break; - } - } while ( - InterlockedCompareExchange(&g_stressResult, false, false) == true && - (DWORD)minipal_lowres_ticks() - startTime < g_stressDurationMilliseconds); - return 0; -} - -bool StressTests(DWORD durationMinutes) -{ - g_isStress = true; - g_expectedTimeoutMilliseconds = 1; - g_stressDurationMilliseconds = durationMinutes * (60 * 1000); - - // Start a thread for each test - HANDLE threadHandles[ARRAY_SIZE(TestList)]; - for (SIZE_T i = 0; i < ARRAY_SIZE(threadHandles); ++i) - { - TestAssert(StartThread(StressTest, reinterpret_cast(i), &threadHandles[i])); - } - - while (true) - { - DWORD waitResult = - WaitForMultipleObjects(ARRAY_SIZE(threadHandles), threadHandles, true /* bWaitAll */, 10 * 1000 /* dwMilliseconds */); - TestAssert(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_TIMEOUT); - if (waitResult == WAIT_OBJECT_0) - { - break; - } - - Trace("'paltest_namedmutex_test1' stress test counts: "); - for (SIZE_T i = 0; i < ARRAY_SIZE(g_stressTestCounts); ++i) - { - if (i != 0) - { - Trace(", "); - } - Trace("%u", g_stressTestCounts[i]); - } - Trace("\n"); - fflush(stdout); - } - - for (SIZE_T i = 0; i < ARRAY_SIZE(threadHandles); ++i) - { - CloseHandle(threadHandles[i]); - } - return static_cast(g_stressResult); -} - -PALTEST(threading_NamedMutex_test1_paltest_namedmutex_test1, "threading/NamedMutex/test1/paltest_namedmutex_test1") -{ - if (argc < 1 || argc > 6) - { - return FAIL; - } - - if (PAL_Initialize(argc, argv) != 0) - { - return FAIL; - } - - test_strcpy(g_processPath, argv[0]); - - if (argc == 1) - { - // Unit test arguments: - - g_parentPid = test_getpid(); - int result = RunTests() ? PASS : FAIL; - ExitProcess(result); - return result; - } - - if (test_strcmp(argv[1], "stress") == 0) - { - // Stress test arguments: stress [durationMinutes] - - DWORD durationMinutes = 1; - if (argc >= 3 && test_sscanf(argv[2], "%u", &durationMinutes) != 1) - { - ExitProcess(FAIL); - return FAIL; - } - - g_parentPid = test_getpid(); - int result = StressTests(durationMinutes) ? PASS : FAIL; - ExitProcess(result); - return result; - } - - // Child test process arguments: - // <0|1> /* currentUserOnly */ <0|1> /* currentSessionOnly */ [stress] - - g_isParent = false; - - if (argc < 5) - { - ExitProcess(FAIL); - return FAIL; - } - - // Get parent process' ID from argument - if (test_sscanf(argv[1], "%u", &g_parentPid) != 1) - { - ExitProcess(FAIL); - return FAIL; - } - - // Get the current-user-only and current-session-only args - if ((argv[3][0] != '0' && argv[3][0] != '1') || - argv[3][1] != '\0' || - (argv[4][0] != '0' && argv[4][0] != '1') || - argv[4][1] != '\0') - { - ExitProcess(FAIL); - return FAIL; - } - g_currentUserOnly = argv[3][0] != '0'; - g_currentSessionOnly = argv[4][0] != '0'; - - if (argc >= 6 && test_strcmp(argv[5], "stress") == 0) - { - g_isStress = true; - } - - if (test_strcmp(argv[2], "MutualExclusionTests_Child") == 0) - { - MutualExclusionTests_Child(); - } - else if (test_strcmp(argv[2], "LifetimeTests_Child") == 0) - { - LifetimeTests_Child(); - } - else if (test_strcmp(argv[2], "AbandonTests_Child_GracefulExit_Close") == 0) - { - AbandonTests_Child_GracefulExit_Close(); - } - else if (test_strcmp(argv[2], "AbandonTests_Child_GracefulExit_NoClose") == 0) - { - AbandonTests_Child_GracefulExit_NoClose(); - } - else if (test_strcmp(argv[2], "AbandonTests_Child_AbruptExit") == 0) - { - AbandonTests_Child_AbruptExit(); - } - else if (test_strcmp(argv[2], "AbandonTests_Child_FileLocksNotInherited_Parent_AbruptExit") == 0) - { - AbandonTests_Child_FileLocksNotInherited_Parent_AbruptExit(); - } - else if (test_strcmp(argv[2], "AbandonTests_Child_FileLocksNotInherited_Child_AbruptExit") == 0) - { - AbandonTests_Child_FileLocksNotInherited_Child_AbruptExit(); - } - else if (test_strcmp(argv[2], "AbandonTests_Child_TryLock") == 0) - { - AbandonTests_Child_TryLock(); - } - else if (test_strcmp(argv[2], "LockAndCloseWithoutThreadExitTests_Child_CloseOnSameThread") == 0) - { - LockAndCloseWithoutThreadExitTests_Child_CloseOnSameThread(); - } - else if (test_strcmp(argv[2], "LockAndCloseWithoutThreadExitTests_Child_CloseOnDifferentThread") == 0) - { - LockAndCloseWithoutThreadExitTests_Child_CloseOnDifferentThread(); - } - ExitProcess(PASS); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp b/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp deleted file mode 100644 index 435f53108b9300..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/NamedMutex/test1/nopal.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// Contains wrappers for functions whose required headers conflict with the PAL - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -auto test_strcpy = strcpy; -auto test_strcmp = strcmp; -auto test_strlen = strlen; -auto test_snprintf = snprintf; -auto test_sscanf = sscanf; -auto test_close = close; -auto test_unlink = unlink; - -unsigned int test_getpid() -{ - return getpid(); -} - -unsigned int test_getsid() -{ - return getsid(0); -} - -unsigned int test_geteuid() -{ - return geteuid(); -} - -int test_kill(unsigned int pid) -{ - return kill(pid, SIGKILL); -} - -bool TestFileExists(const char *path) -{ - int fd = open(path, O_RDWR); - if (fd == -1) - return false; - close(fd); - return true; -} - -bool WriteHeaderInfo(const char *path, bool currentUserOnly, char sharedMemoryType, char version, int *fdRef) -{ - int fd = open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); - if (fd == -1) - return false; - - if (currentUserOnly) - { - int chmodResult; - do - { - chmodResult = chmod(path, S_IRUSR | S_IWUSR); - } while (chmodResult != 0 && errno == EINTR); - - if (chmodResult != 0) - return false; - } - - *fdRef = fd; - if (ftruncate(fd, getpagesize()) != 0) - return false; - if (lseek(fd, 0, SEEK_SET) != 0) - return false; - - // See SharedMemorySharedDataHeader for format - char buffer[] = {sharedMemoryType, version}; - if (write(fd, buffer, ARRAY_SIZE(buffer)) != ARRAY_SIZE(buffer)) - return false; - - return flock(fd, LOCK_SH | LOCK_NB) == 0; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.cpp deleted file mode 100644 index 618c5edeca5555..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test6/child6.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: child6.c -** -** Purpose: Test for WaitForMultipleObjectsEx in multiple -** scenarios - child process -** -** -**=========================================================*/ - -#include - -PALTEST(threading_WaitForMultipleObjectsEx_test6_paltest_waitformultipleobjectsex_test6_child, "threading/WaitForMultipleObjectsEx/test6/paltest_waitformultipleobjectsex_test6_child") -{ - int i, iRet; - BOOL bRet; - BOOL bNamedEvent = 0; - BOOL bMutex = 0; - BOOL bMutexAndNamedEvent = 0; - BOOL bSemaphore = 0; - DWORD dwRet; - HANDLE hNamedEvent; - HANDLE hMutex; - char szTestName[256]; - WCHAR wszTestName[256] = { 0 }; - char szEventName[128] = { 0 }; - char szMutexName[128] = { 0 }; - char szSemName[128] = { 0 }; - WCHAR wszEventName[128]; - WCHAR wszMutexName[128]; - WCHAR wszSemName[128]; - DWORD iExitCode = 0; - HANDLE hSemaphore; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - Trace("[child] Starting\n"); - - for (i=1; i -#include - -#define MAX_COUNT 10000 -#define MAX_THREADS 256 - -BOOL g_bMutex = 0; -BOOL g_bEvent = 0; -BOOL g_bNamedEvent = 0; -BOOL g_bSemaphore = 0; -BOOL g_bProcess = 0; -BOOL g_bLocalWaitAll = 0; -BOOL g_bRemoteWaitAll = 0; -BOOL g_bRandom = 0; - -int iCount = 1; -int iThreads = 1; -HANDLE hThreads[MAX_THREADS]; - -#ifndef MIN -#define MIN(a,b) (((a)<(b)) ? (a) : (b)) -#endif - -DWORD PALAPI EventTestThread(PVOID pArg) -{ - BOOL bRet; - DWORD dwRet; - HANDLE hEvent[2]; - HANDLE (*prgHandles)[] = (HANDLE (*)[])pArg; - - Trace("[EventTestThread] Starting\n"); - - bRet = DuplicateHandle(GetCurrentProcess(), (*prgHandles)[0], GetCurrentProcess(), - &hEvent[0], 0, FALSE, DUPLICATE_SAME_ACCESS); - bRet &= DuplicateHandle(GetCurrentProcess(), (*prgHandles)[1], GetCurrentProcess(), - &hEvent[1], 0, FALSE, DUPLICATE_SAME_ACCESS); - if (FALSE == bRet) - { - Fail("[EventTestThread] Failed to duplicate handles\n"); - } - - Sleep(1000); - bRet = SetEvent(hEvent[1]); - if (FALSE == bRet) - { - Fail("SetEvent failed\n"); - Fail("[EventTestThread] SetEvent failed [GetLastError()=%u]\n", - GetLastError()); - } - - dwRet = WaitForSingleObject(hEvent[1], INFINITE); - if (WAIT_FAILED == dwRet) - { - Fail("[EventTestThread] WaitForMultipleObjects failed [GetLastError()=%u]\n", - GetLastError()); - } - - Sleep(1000); - bRet = SetEvent(hEvent[0]); - if (FALSE == bRet) - { - Fail("[EventTestThread] SetEvent failed [GetLastError()=%u]\n", - GetLastError()); - } - - Sleep(1000); - bRet = SetEvent(hEvent[1]); - if (FALSE == bRet) - { - Fail("[EventTestThread] SetEvent failed [GetLastError()=%u]\n", - GetLastError()); - } - - CloseHandle(hEvent[0]); - CloseHandle(hEvent[1]); - - Trace("[EventTestThread] Done\n"); - return 0; -} - -DWORD PALAPI MutexTestThread(PVOID pArg) -{ - BOOL bRet; - DWORD dwRet; - HANDLE hMutex; - - Trace("[MutexTestThread] Starting\n"); - - bRet = DuplicateHandle(GetCurrentProcess(), (HANDLE)pArg, GetCurrentProcess(), &hMutex, - 0, FALSE, DUPLICATE_SAME_ACCESS); - if (FALSE == bRet) - { - Fail("[EventTestThread] DuplicateHandle failed [GetLastError()=%u]\n", - GetLastError()); - } - - dwRet = WaitForSingleObject(hMutex, INFINITE); - if (WAIT_FAILED == dwRet) - { - Fail("[EventTestThread] WaitForMultipleObjects failed [GetLastError()=%u]\n", - GetLastError()); - } - - Sleep(1000); - CloseHandle(hMutex); - - Trace("[MutexTestThread] Done\n"); - - return 0; -} - -DWORD PALAPI TestThread(PVOID pArg) -{ - BOOL bRet; - DWORD dwRet; - PROCESS_INFORMATION pi; - STARTUPINFO si; - HANDLE hNamedEvent; - HANDLE hEvent[2] = { 0, 0 }; - HANDLE hMutex = 0; - HANDLE hSemaphore = 0; - HANDLE hObjs[2]; - DWORD dwThreadNum; - DWORD dwSlaveThreadTid = 0; - HANDLE hThread; - int i, iCnt, iRet; - char szTestName[128]; - char szCmd[128]; - char szEventName[128] = { 0 }; - char szMutexName[128] = { 0 }; - char szSemName[128] = { 0 }; - WCHAR wszEventName[128] = { 0 }; - WCHAR wszMutexName[128] = { 0 }; - WCHAR wszSemName[128] = { 0 }; - BOOL bMutex = g_bMutex; - BOOL bEvent = g_bEvent; - BOOL bNamedEvent = g_bNamedEvent; - BOOL bSemaphore = g_bSemaphore; - BOOL bProcess = g_bProcess; - BOOL bLocalWaitAll = g_bLocalWaitAll; - BOOL bRemoteWaitAll = g_bRemoteWaitAll; - int iDesiredExitCode; - - dwThreadNum = (DWORD)(SIZE_T)pArg; - - sprintf_s (szTestName, 128, "Test6_%u", dwThreadNum); - szTestName[127] = 0; - - sprintf_s(szEventName, 128, "%s_Event", szTestName); - szEventName[127] = 0; - sprintf_s(szMutexName, 128, "%s_Mutex", szTestName); - szMutexName[127] = 0; - sprintf_s(szSemName, 128, "%s_Semaphore", szTestName); - szSemName[127] = 0; - - iRet = MultiByteToWideChar(CP_ACP, 0, szEventName, strlen(szEventName)+1, wszEventName, 128); - iRet &= MultiByteToWideChar(CP_ACP, 0, szMutexName, strlen(szMutexName)+1, wszMutexName, 128); - iRet &= MultiByteToWideChar(CP_ACP, 0, szSemName, strlen(szSemName)+1, wszSemName, 128); - - if (0 == iRet) - { - Fail("[TestThread] Failed to convert strings\n"); - } - - Trace("[TestThread] TestName=%s Event: %S, Mutex: %S, Semaphore = %S\n", - szTestName, wszEventName, wszMutexName, wszSemName); - - hEvent[0] = CreateEvent(NULL, FALSE, FALSE, NULL); - hEvent[1] = CreateEvent(NULL, FALSE, FALSE, NULL); - - hNamedEvent = CreateEventW(NULL, FALSE, FALSE, wszEventName); - hMutex = CreateMutexW(NULL, FALSE, wszMutexName); - hSemaphore = CreateSemaphoreExW(NULL, 0, 256, wszSemName, 0, 0); - - if (NULL == hEvent[0] || NULL == hEvent[1] || NULL == hMutex || - NULL == hNamedEvent || NULL == hSemaphore) - { - Fail("[TestThread] Failed to create objects " - "[hNamedEvent=%p hMutex=%p hSemaphore=%p]\n", - (VOID*)hNamedEvent, (VOID*)hMutex, (VOID*)hSemaphore); - } - - for (iCnt=0; iCnt i+1)) - { - i++; - iCnt = atoi(argv[i]); - if (iCnt > 0 && iCnt < MAX_COUNT) - { - iCount = iCnt; - } - } - else if ((0 == strcmp(argv[i], "-threads")) && (argc > i+1)) - { - i++; - iCnt = atoi(argv[i]); - if (iCnt > 0 && iCnt <= MAX_THREADS) - { - iThreads = iCnt; - } - } - else - { - Trace("Unknown option %s ignored\n", argv[i]); - } - } - } - - - iCnt = 0; - for (i=0;i Date: Mon, 21 Jul 2025 21:51:00 +0000 Subject: [PATCH 04/33] Fix qcalls and managed/native sizes --- .../src/System/Threading/Thread.CoreCLR.cs | 8 ++++---- src/coreclr/vm/object.h | 3 +++ src/coreclr/vm/qcallentrypoints.cpp | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 24cc0e91781022..df8bd21b98d119 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -33,6 +33,10 @@ public sealed partial class Thread private string? _name; private StartHelper? _startHelper; +#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI + internal WaitSubsystem.ThreadWaitInfo? _waitInfo; +#endif + /*========================================================================= ** The base implementation of Thread is all native. The following fields ** should never be used in the C# code. They are here to define the proper @@ -62,10 +66,6 @@ public sealed partial class Thread private bool _isDead; private bool _isThreadPool; -#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI - internal WaitSubsystem.ThreadWaitInfo? _waitInfo; -#endif - private Thread() { } public int ManagedThreadId diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 0b97c65e53df4a..7ba028c03e19d5 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1309,6 +1309,9 @@ class ThreadBaseObject : public Object OBJECTREF m_SynchronizationContext; STRINGREF m_Name; OBJECTREF m_StartHelper; +#ifdef TARGET_UNIX + OBJECTREF m_WaitInfo; +#endif // TARGET_UNIX // The next field (m_InternalThread) is declared as IntPtr in the managed // definition of Thread. The loader will sort it next. diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 2400d89c714020..77075ea20025ad 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -290,6 +290,7 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_Initialize) DllImportEntry(ThreadNative_GetThreadState) DllImportEntry(ThreadNative_SetWaitSleepJoinState) + DllImportEntry(ThreadNative_ClearWaitSleepJoinState) #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT DllImportEntry(ThreadNative_GetApartmentState) DllImportEntry(ThreadNative_SetApartmentState) From 9d4373030de16f83f3efecac37d5931d365d4b8b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 21 Jul 2025 22:05:38 +0000 Subject: [PATCH 05/33] Unify LowLevelLifoSemaphore implementations for Unix to use the wait subsystem --- eng/SignCheckExclusionsFile.txt | 15 + .../System.Private.CoreLib.csproj | 1 - .../Threading/LowLevelLifoSemaphore.Unix.cs | 51 - src/coreclr/debug/runtimeinfo/.editorconfig | 2 + src/coreclr/debug/runtimeinfo/configure.h.in | 6 + .../runtimeinfo/contract-descriptor.c.in | 34 + .../runtimeinfo/contractdescriptorstub.c | 39 + .../debug/runtimeinfo/contractpointerdata.cpp | 24 + src/coreclr/debug/runtimeinfo/contracts.jsonc | 27 + .../debug/runtimeinfo/datadescriptor.cpp | 343 ++++++ .../debug/runtimeinfo/datadescriptor.inc | 1027 +++++++++++++++++ .../src/System/Collections/Generic/Empty.cs | 65 ++ .../Collections/Generic/LowLevelList.cs | 196 ++++ .../src/System.Private.CoreLib.csproj | 1 - .../src/System/RuntimeType.cs | 864 ++++++++++++++ src/coreclr/pal/inc/pal.h | 7 - src/coreclr/pal/src/synchmgr/wait.cpp | 30 - src/coreclr/vm/comwaithandle.cpp | 19 - src/coreclr/vm/comwaithandle.h | 4 - src/coreclr/vm/qcallentrypoints.cpp | 3 - .../System.Data.OleDb/src/OleDbComWrappers.cs | 124 ++ .../src/System/Linq/Skip.SizeOpt.cs | 20 + .../src/System/Linq/Take.SizeOpt.cs | 47 + .../System.Private.CoreLib.Shared.projitems | 1 + .../Threading/LowLevelLifoSemaphore.Unix.cs | 8 +- .../System/Threading/LowLevelLifoSemaphore.cs | 4 - .../Metadata/TypeNameParserOptions.cs | 38 + .../Legacy/TheoryDataExtensions.cs | 49 + .../IArrayOfStatelessElements.cs | 87 ++ .../SharedTypes/ComInterfaces/IBool.cs | 26 + .../ICustomStringMarshallingUtf16.cs | 20 + .../SharedTypes/ComInterfaces/IDerived.cs | 29 + .../SharedTypes/ComInterfaces/IEmpty.cs | 16 + .../SharedTypes/ComInterfaces/IEnumUnknown.cs | 19 + .../SharedTypes/ComInterfaces/IFloat.cs | 25 + .../ComInterfaces/IGetAndSetInt.cs | 27 + .../SharedTypes/ComInterfaces/IGetIntArray.cs | 19 + .../SharedTypes/ComInterfaces/IHide.cs | 56 + .../SharedTypes/ComInterfaces/IInt.cs | 47 + .../SharedTypes/ComInterfaces/IIntArray.cs | 72 ++ .../SharedTypes/ComInterfaces/IInterface.cs | 51 + .../ComInterfaces/IJaggedIntArray.cs | 60 + .../ComInterfaces/IPointProvider.cs | 28 + .../SharedTypes/ComInterfaces/IRefStrings.cs | 18 + .../SharedTypes/ComInterfaces/ISafeHandles.cs | 19 + .../ComInterfaces/IStatefulAllShapes.cs | 46 + .../IStatefulCallerAllocatedBuffer.cs | 58 + .../IStatefulCollectionAllShapes.cs | 72 ++ .../IStatefulCollectionBlittableElement.cs | 30 + .../IStatefulCollectionPinnableReference.cs | 86 ++ .../IStatefulCollectionStatelessElement.cs | 57 + .../IStatefulFinallyMarshalling.cs | 135 +++ .../ComInterfaces/IStatefulMarshalling.cs | 155 +++ .../IStatefulPinnedMarshalling.cs | 288 +++++ .../ComInterfaces/IStatelessAllShapes.cs | 63 + ...tatelessCallerAllocateBufferMarshalling.cs | 165 +++ .../IStatelessCollectionAllShapes.cs | 72 ++ .../IStatelessCollectionBlittableElement.cs | 30 + ...tatelessCollectionCallerAllocatedBuffer.cs | 140 +++ .../IStatelessCollectionPinnableReference.cs | 85 ++ .../IStatelessCollectionStatelessElement.cs | 134 +++ .../IStatelessFinallyMarshalling.cs | 55 + .../ComInterfaces/IStatelessMarshalling.cs | 77 ++ ...lessPinnableCollectionBlittableElements.cs | 156 +++ .../IStatelessPinnedMarshalling.cs | 62 + .../IStringMarshallingOverride.cs | 23 + .../IStringMarshallingOverrideDerived.cs | 23 + .../SharedTypes/ComInterfaces/ISystem.cs | 19 + .../ComInterfaces/IUTF16Marshalling.cs | 20 + .../ComInterfaces/IUTF8Marshalling.cs | 20 + .../ManagedComMethodFailureException.cs | 11 + .../ICollectionMarshallingFails.cs | 56 + .../IJaggedIntArrayMarshallingFails.cs | 89 ++ .../IStringArrayMarshallingFails.cs | 79 ++ .../MarshallingFails/ISupportErrorInfo.cs | 17 + .../MarshallingFailureException.cs | 13 + .../ThrowOn4thElementMarshalled.cs | 37 + ....Serialization.Xml.Canonicalization.csproj | 72 ++ ...m.Security.Cryptography.Pkcs.netcoreapp.cs | 222 ++++ .../ChannelClosedException.netcoreapp.cs | 27 + ...tem.Threading.Tasks.Dataflow.netcoreapp.cs | 19 + .../Base/DataflowBlock.IAsyncEnumerable.cs | 35 + .../TransformManyBlock.IAsyncEnumerable.cs | 267 +++++ .../System.Private.CoreLib.csproj | 3 - .../LowLevelLifoSemaphore.Unix.Mono.cs | 47 - ...PortableThreadPool.Browser.Threads.Mono.cs | 19 + .../ThreadPool.Browser.Threads.Mono.cs | 13 + .../System/Threading/ThreadPool.Wasi.Mono.cs | 138 +++ ...eadPoolBoundHandle.Browser.Threads.Mono.cs | 15 + .../System/Threading/TimerQueue.Wasi.Mono.cs | 157 +++ src/mono/mono/metadata/icall-decl.h | 5 - src/mono/mono/metadata/icall-def.h | 7 - src/mono/mono/metadata/threads.c | 28 - src/mono/mono/utils/CMakeLists.txt | 2 - src/mono/mono/utils/lifo-semaphore.c | 96 -- src/mono/mono/utils/lifo-semaphore.h | 34 - 96 files changed, 6902 insertions(+), 345 deletions(-) create mode 100644 eng/SignCheckExclusionsFile.txt delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs create mode 100644 src/coreclr/debug/runtimeinfo/.editorconfig create mode 100644 src/coreclr/debug/runtimeinfo/configure.h.in create mode 100644 src/coreclr/debug/runtimeinfo/contract-descriptor.c.in create mode 100644 src/coreclr/debug/runtimeinfo/contractdescriptorstub.c create mode 100644 src/coreclr/debug/runtimeinfo/contractpointerdata.cpp create mode 100644 src/coreclr/debug/runtimeinfo/contracts.jsonc create mode 100644 src/coreclr/debug/runtimeinfo/datadescriptor.cpp create mode 100644 src/coreclr/debug/runtimeinfo/datadescriptor.inc create mode 100644 src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs create mode 100644 src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs create mode 100644 src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs create mode 100644 src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs create mode 100644 src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs rename src/{coreclr/nativeaot => libraries}/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs (65%) create mode 100644 src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs create mode 100644 src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs create mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs create mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj create mode 100644 src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs create mode 100644 src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs create mode 100644 src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs create mode 100644 src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs create mode 100644 src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.Mono.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs delete mode 100644 src/mono/mono/utils/lifo-semaphore.c delete mode 100644 src/mono/mono/utils/lifo-semaphore.h diff --git a/eng/SignCheckExclusionsFile.txt b/eng/SignCheckExclusionsFile.txt new file mode 100644 index 00000000000000..f41753338155eb --- /dev/null +++ b/eng/SignCheckExclusionsFile.txt @@ -0,0 +1,15 @@ +;; Exclusions for SignCheck. Corresponds to info in Signing.props. +;; Format: https://github.com/dotnet/arcade/blob/397316e195639450b6c76bfeb9823b40bee72d6d/src/SignCheck/Microsoft.SignCheck/Verification/Exclusion.cs#L23-L35 +;; +;; This issue tracks a way to implement exclusions via Signing.props and avoid this extra file: https://github.com/dotnet/arcade/issues/2888 + +;; The apphost and comhost are template files, modified by the SDK to produce the executable for FDE +;; and SCD apps. If they are signed, the file that the SDK produces has an invalid signature and +;; can't be signed again. More info at https://github.com/dotnet/core-setup/pull/7549. +*apphost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*singlefilehost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*comhost.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*apphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*comhosttemplatecomhostdll.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*staticapphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 +*dotnet.js;;Workaround, https://github.com/dotnet/core-eng/issues/9933 \ No newline at end of file diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index a64294355f6e44..88992bc63e56ae 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -289,7 +289,6 @@ - diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs deleted file mode 100644 index 25fc6ff09ad2a1..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -namespace System.Threading -{ - /// - /// A LIFO semaphore implemented using the PAL's semaphore with uninterruptible waits. - /// - internal sealed partial class LowLevelLifoSemaphore : IDisposable - { - private Semaphore? _semaphore; - - private void Create(int maximumSignalCount) - { - Debug.Assert(maximumSignalCount > 0); - _semaphore = new Semaphore(0, maximumSignalCount); - } - - public bool WaitCore(int timeoutMs) - { - Debug.Assert(_semaphore != null); - Debug.Assert(timeoutMs >= -1); - - int waitResult = WaitNative(_semaphore!.SafeWaitHandle, timeoutMs); - Debug.Assert(waitResult == WaitHandle.WaitSuccess || waitResult == WaitHandle.WaitTimeout); - return waitResult == WaitHandle.WaitSuccess; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_WaitOnePrioritized")] - private static partial int WaitNative(SafeWaitHandle handle, int timeoutMs); - - private void ReleaseCore(int count) - { - Debug.Assert(_semaphore != null); - Debug.Assert(count > 0); - - _semaphore!.Release(count); - } - - public void Dispose() - { - Debug.Assert(_semaphore != null); - _semaphore!.Dispose(); - } - } -} diff --git a/src/coreclr/debug/runtimeinfo/.editorconfig b/src/coreclr/debug/runtimeinfo/.editorconfig new file mode 100644 index 00000000000000..ce2ce7b8d1ec54 --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/.editorconfig @@ -0,0 +1,2 @@ +[contracts.jsonc] +indent_size = 2 diff --git a/src/coreclr/debug/runtimeinfo/configure.h.in b/src/coreclr/debug/runtimeinfo/configure.h.in new file mode 100644 index 00000000000000..efe0ede365c644 --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/configure.h.in @@ -0,0 +1,6 @@ +#ifndef RUNTIME_INFO_CONFIGURE_H_INCLUDED +#define RUNTIME_INFO_CONFIGURE_H_INCLUDED + +#define RID_STRING @CLR_DOTNET_RID@ + +#endif // RUNTIME_INFO_CONFIGURE_H_INCLUDED diff --git a/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in b/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in new file mode 100644 index 00000000000000..c1f0edd7a66f9c --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#ifdef _MSC_VER +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT __attribute__((visibility("default"))) +#endif + +struct DotNetRuntimeContractDescriptor +{ + uint64_t magic; + uint32_t flags; + const uint32_t descriptor_size; + const char *descriptor; + const uint32_t pointer_data_count; + uint32_t pad0; + const uintptr_t *pointer_data; +}; + +extern const uintptr_t contractDescriptorPointerData[]; + +DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor; + +DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = { + .magic = 0x0043414443434e44ull, // "DNCCDAC\0" + .flags = %%platformFlags%%, + .descriptor_size = %%jsonDescriptorSize%%, + .descriptor = "%%jsonDescriptor%%", + .pointer_data_count = %%pointerDataCount%%, + .pointer_data = &contractDescriptorPointerData[0], +}; diff --git a/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c b/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c new file mode 100644 index 00000000000000..59421a6692d2a7 --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +#ifdef _MSC_VER +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT __attribute__((visibility("default"))) +#endif + +struct DotNetRuntimeContractDescriptor +{ + uint64_t magic; + uint32_t flags; + const uint32_t descriptor_size; + const char *descriptor; + const uint32_t pointer_data_count; + uint32_t pad0; + const uintptr_t *pointer_data; +}; + +extern const uintptr_t contractDescriptorPointerData[]; + +// just the placeholder pointer +const uintptr_t contractDescriptorPointerData[] = { (uintptr_t)0 }; + +DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor; + +#define STUB_DESCRIPTOR "{\"version\":0,\"baseline\":\"empty\",\"contracts\":{},\"types\":{},\"globals\":{}}" + +DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = { + .magic = 0x0043414443434e44ull, // "DNCCDAC\0" + .flags = 0x1u & (sizeof(void*) == 4 ? 0x02u : 0x00u), + .descriptor_size = sizeof(STUB_DESCRIPTOR), + .descriptor = STUB_DESCRIPTOR, + .pointer_data_count = 1, + .pointer_data = &contractDescriptorPointerData[0], +}; diff --git a/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp b/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp new file mode 100644 index 00000000000000..1848b1fb69d898 --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" + +#include +#include + +#include "cdacplatformmetadata.hpp" +#include "threads.h" +#include "vars.hpp" + +extern "C" +{ +// without an extern declaration, clang does not emit this global into the object file +extern const uintptr_t contractDescriptorPointerData[]; + +const uintptr_t contractDescriptorPointerData[] = { + (uintptr_t)0, // placeholder +#define CDAC_GLOBAL_POINTER(name,value) (uintptr_t)(value), +#include "datadescriptor.inc" +}; + +} diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc new file mode 100644 index 00000000000000..81b28cd2e1fe92 --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/contracts.jsonc @@ -0,0 +1,27 @@ +//algorithmic contracts for coreclr +// The format of this file is: JSON with comments +// { +// "CONTRACT NAME": VERSION, +// ... +// } +// CONTRACT NAME is an arbitrary string, VERSION is an integer +// +// cdac-build-tool can take multiple "-c contract_file" arguments +// so to conditionally include contracts, put additional contracts in a separate file +{ + "CodeVersions": 1, + "DacStreams": 1, + "EcmaMetadata" : 1, + "Exception": 1, + "ExecutionManager": 2, + "Loader": 1, + "Object": 1, + "PlatformMetadata": 1, + "PrecodeStubs": 3, + "ReJIT": 1, + "RuntimeInfo": 1, + "RuntimeTypeSystem": 1, + "StackWalk": 1, + "StressLog": 2, + "Thread": 1 +} diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp new file mode 100644 index 00000000000000..0efae1ad25089f --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp @@ -0,0 +1,343 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "common.h" + +#include +#include + +#include "static_assert.h" + +#include +#include "cdacplatformmetadata.hpp" +#include "methodtable.h" +#include "threads.h" +#include "exinfo.h" + +#include "configure.h" + +#include "../debug/ee/debugger.h" + +#ifdef HAVE_GCCOVER +#include "gccover.h" +#endif // HAVE_GCCOVER + +// begin blob definition + +extern "C" +{ + +struct TypeSpec +{ + uint32_t Name; + uint32_t Fields; + uint16_t Size; // note: C++ fragile no designated initializers - Size must come after Name and Fields +}; + +struct FieldSpec +{ + uint32_t Name; + uint32_t TypeName; + uint16_t FieldOffset; +}; + +struct GlobalLiteralSpec +{ + uint32_t Name; + uint32_t TypeName; + uint64_t Value; +}; + +struct GlobalPointerSpec +{ + uint32_t Name; + uint32_t PointerDataIndex; +}; + +struct GlobalStringSpec +{ + uint32_t Name; + uint32_t StringValue; +}; + +#define CONCAT(token1,token2) token1 ## token2 +#define CONCAT4(token1, token2, token3, token4) token1 ## token2 ## token3 ## token4 + +#define MAKE_TYPELEN_NAME(tyname) CONCAT(cdac_string_pool_typename__, tyname) +#define MAKE_FIELDLEN_NAME(tyname,membername) CONCAT4(cdac_string_pool_membername__, tyname, __, membername) +#define MAKE_FIELDTYPELEN_NAME(tyname,membername) CONCAT4(cdac_string_pool_membertypename__, tyname, __, membername) +#define MAKE_GLOBALLEN_NAME(globalname) CONCAT(cdac_string_pool_globalname__, globalname) +#define MAKE_GLOBALTYPELEN_NAME(globalname) CONCAT(cdac_string_pool_globaltypename__, globalname) +#define MAKE_GLOBALVALUELEN_NAME(globalname) CONCAT(cdac_string_pool_globalvalue__, globalname) + +// used to stringify the result of a macros expansion +#define STRINGIFY(x) #x + +// define a struct where the size of each field is the length of some string. we will use offsetof to get +// the offset of each struct element, which will be equal to the offset of the beginning of that string in the +// string pool. +struct CDacStringPoolSizes +{ + char cdac_string_pool_nil; // make the first real string start at offset 1 +#define DECL_LEN(membername,len) char membername[(len)]; +#define CDAC_BASELINE(name) DECL_LEN(cdac_string_pool_baseline_, (sizeof(name))) +#define CDAC_TYPE_BEGIN(name) DECL_LEN(MAKE_TYPELEN_NAME(name), sizeof(#name)) +#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) DECL_LEN(MAKE_FIELDLEN_NAME(tyname,membername), sizeof(#membername)) \ + DECL_LEN(MAKE_FIELDTYPELEN_NAME(tyname,membername), sizeof(#membertyname)) +#define CDAC_GLOBAL_STRING(name, stringval) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \ + DECL_LEN(MAKE_GLOBALVALUELEN_NAME(name), sizeof(STRINGIFY(stringval))) +#define CDAC_GLOBAL_POINTER(name,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) +#define CDAC_GLOBAL(name,tyname,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \ + DECL_LEN(MAKE_GLOBALTYPELEN_NAME(name), sizeof(#tyname)) +#include "datadescriptor.inc" + char cdac_string_pool_trailing_nil; +#undef DECL_LEN +}; + +#define GET_TYPE_NAME(name) offsetof(struct CDacStringPoolSizes, MAKE_TYPELEN_NAME(name)) +#define GET_FIELD_NAME(tyname,membername) offsetof(struct CDacStringPoolSizes, MAKE_FIELDLEN_NAME(tyname,membername)) +#define GET_FIELDTYPE_NAME(tyname,membername) offsetof(struct CDacStringPoolSizes, MAKE_FIELDTYPELEN_NAME(tyname,membername)) +#define GET_GLOBAL_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALLEN_NAME(globalname)) +#define GET_GLOBALTYPE_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALTYPELEN_NAME(globalname)) +#define GET_GLOBALSTRING_VALUE(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALVALUELEN_NAME(globalname)) + +// count the types +enum +{ + CDacBlobTypesCount = +#define CDAC_TYPES_BEGIN() 0 +#define CDAC_TYPE_BEGIN(name) + 1 +#include "datadescriptor.inc" +}; + +// count the field pool size. +// there's 1 placeholder element at the start, and 1 endmarker after each type +enum +{ + CDacBlobFieldsPoolCount = +#define CDAC_TYPES_BEGIN() 1 +#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) + 1 +#define CDAC_TYPE_END(name) + 1 +#include "datadescriptor.inc" +}; + +// count the literal globals +enum +{ + CDacBlobGlobalLiteralsCount = +#define CDAC_GLOBALS_BEGIN() 0 +#define CDAC_GLOBAL(name,tyname,value) + 1 +#include "datadescriptor.inc" +}; + +// count the aux vector globals +enum +{ + CDacBlobGlobalPointersCount = +#define CDAC_GLOBALS_BEGIN() 0 +#define CDAC_GLOBAL_POINTER(name,value) + 1 +#include "datadescriptor.inc" +}; + +// count the global strings +enum +{ + CDacBlobGlobalStringsCount = +#define CDAC_GLOBALS_BEGIN() 0 +#define CDAC_GLOBAL_STRING(name,value) + 1 +#include "datadescriptor.inc" +}; + + +#define MAKE_TYPEFIELDS_TYNAME(tyname) CONCAT(CDacFieldsPoolTypeStart__, tyname) + +// index of each run of fields. +// we make a struct containing one 1-byte field for each field in the run, and then take the offset of the +// struct to get the index of the run of fields. +// this looks like +// +// struct CDacFieldsPoolSizes { +// char cdac_fields_pool_start_placeholder__; +// struct CDacFieldsPoolTypeStart__MethodTable { +// char cdac_fields_pool_member__MethodTable__GCHandle; +// char cdac_fields_pool_member__MethodTable_endmarker; +// } CDacFieldsPoolTypeStart__MethodTable; +// ... +// }; +// +// so that offsetof(struct CDacFieldsPoolSizes, CDacFieldsPoolTypeStart__MethodTable) will give the offset of the +// method table field descriptors in the run of fields +struct CDacFieldsPoolSizes +{ +#define DECL_LEN(membername) char membername; +#define CDAC_TYPES_BEGIN() DECL_LEN(cdac_fields_pool_start_placeholder__) +#define CDAC_TYPE_BEGIN(name) struct MAKE_TYPEFIELDS_TYNAME(name) { +#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) DECL_LEN(CONCAT4(cdac_fields_pool_member__, tyname, __, membername)) +#define CDAC_TYPE_END(name) DECL_LEN(CONCAT4(cdac_fields_pool_member__, tyname, _, endmarker)) \ + } MAKE_TYPEFIELDS_TYNAME(name); +#include "datadescriptor.inc" +#undef DECL_LEN +}; + +#define GET_TYPE_FIELDS(tyname) offsetof(struct CDacFieldsPoolSizes, MAKE_TYPEFIELDS_TYNAME(tyname)) + +// index of each global pointer +// +// struct CDacGlobalPointerIndex +// { +// char placeholder; +// char firstGlobalPointerName; +// char secondGlobalPointerName; +// ... +//} +// +// offsetof (CDACGlobalPointerIndex, NAME) returns the index of the global +struct CDacGlobalPointerIndex +{ +#define DECL_LEN(membername) char membername; +#define CDAC_GLOBALS_BEGIN() DECL_LEN(cdac_global_pointer_index_start_placeholder__) +#define CDAC_GLOBAL_POINTER(name,value) DECL_LEN(CONCAT(cdac_global_pointer_index__, name)) +#include "datadescriptor.inc" +#undef DECL_LEN +}; + +#define GET_GLOBAL_POINTER_INDEX(name) offsetof(struct CDacGlobalPointerIndex, CONCAT(cdac_global_pointer_index__, name)) + +struct BinaryBlobDataDescriptor +{ + // The blob begins with a directory that gives the relative offsets of the `Baseline`, `Types`, + // `FieldsPool`, `GlobalLiteralValues`, `GlobalPointerValues` and `Names` fields of the blob. + // The number of elements of each of the arrays is next. This is followed by the sizes of the + // spec structs. Since `BinaryBlobDataDescriptor` is created via macros, we want to embed the + // `offsetof` and `sizeof` of the components of the blob into the blob itself without having to + // account for any padding that the C/C++ compiler may introduce to enforce alignment. + // Additionally the `Directory` tries to follow a common C/C++ alignment rule (we don't want + // padding introduced in the directory itself): N-byte members are aligned to start on N-byte + // boundaries. + struct Directory { + uint32_t FlagsAndBaselineStart; + uint32_t TypesStart; + + uint32_t FieldsPoolStart; + uint32_t GlobalLiteralValuesStart; + + uint32_t GlobalPointersStart; + uint32_t GlobalStringValuesStart; + uint32_t NamesPoolStart; + + uint32_t TypeCount; + uint32_t FieldsPoolCount; + + uint32_t GlobalLiteralValuesCount; + uint32_t GlobalPointerValuesCount; + uint32_t GlobalStringValuesCount; + + uint32_t NamesPoolCount; + + uint8_t TypeSpecSize; + uint8_t FieldSpecSize; + uint8_t GlobalLiteralSpecSize; + uint8_t GlobalPointerSpecSize; + uint8_t GlobalStringSpecSize; + } Directory; + uint32_t PlatformFlags; + uint32_t BaselineName; + struct TypeSpec Types[CDacBlobTypesCount]; + struct FieldSpec FieldsPool[CDacBlobFieldsPoolCount]; + struct GlobalLiteralSpec GlobalLiteralValues[CDacBlobGlobalLiteralsCount]; + struct GlobalPointerSpec GlobalPointerValues[CDacBlobGlobalPointersCount]; + struct GlobalStringSpec GlobalStringValues[CDacBlobGlobalStringsCount]; + uint8_t NamesPool[sizeof(struct CDacStringPoolSizes)]; + uint8_t EndMagic[4]; +}; + +struct MagicAndBlob { + uint64_t magic; + struct BinaryBlobDataDescriptor Blob; +}; + +// we only support 32-bit and 64-bit right now +static_assert_no_msg(sizeof(void*) == 4 || sizeof(void*) == 8); + +// C-style designated initializers are a C++20 feature. Have to use plain old aggregate initialization instead. + +DLLEXPORT +struct MagicAndBlob BlobDataDescriptor = { + /*.magic = */ 0x00424F4C42434144ull,// "DACBLOB", + /*.Blob =*/ { + /*.Directory =*/ { + /* .FlagsAndBaselineStart = */ offsetof(struct BinaryBlobDataDescriptor, PlatformFlags), + /* .TypesStart = */ offsetof(struct BinaryBlobDataDescriptor, Types), + /* .FieldsPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, FieldsPool), + /* .GlobalLiteralValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalLiteralValues), + /* .GlobalPointersStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalPointerValues), + /* .GlobalStringValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalStringValues), + /* .NamesPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, NamesPool), + /* .TypeCount = */ CDacBlobTypesCount, + /* .FieldsPoolCount = */ CDacBlobFieldsPoolCount, + /* .GlobalLiteralValuesCount = */ CDacBlobGlobalLiteralsCount, + /* .GlobalPointerValuesCount = */ CDacBlobGlobalPointersCount, + /* .GlobalStringValuesCount = */ CDacBlobGlobalStringsCount, + /* .NamesPoolCount = */ sizeof(struct CDacStringPoolSizes), + /* .TypeSpecSize = */ sizeof(struct TypeSpec), + /* .FieldSpecSize = */ sizeof(struct FieldSpec), + /* .GlobalLiteralSpecSize = */ sizeof(struct GlobalLiteralSpec), + /* .GlobalPointerSpecSize = */ sizeof(struct GlobalPointerSpec), + /* .GlobalStringSpecSize = */ sizeof(struct GlobalStringSpec) + }, + /* .PlatformFlags = */ (sizeof(void*) == 4 ? 0x02 : 0) | 0x01, + /* .BaselineName = */ offsetof(struct CDacStringPoolSizes, cdac_string_pool_baseline_), + + /* .Types = */ { +#define CDAC_TYPE_BEGIN(name) { \ + /* .Name = */ GET_TYPE_NAME(name), \ + /* .Fields = */ GET_TYPE_FIELDS(name), +#define CDAC_TYPE_INDETERMINATE(name) /*.Size = */ 0, +#define CDAC_TYPE_SIZE(size) /* .Size = */ size, +#define CDAC_TYPE_END(name) }, +#include "datadescriptor.inc" + }, + + /* .FieldsPool = */ { +#define CDAC_TYPES_BEGIN() {0,}, +#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) { \ + /* .Name = */ GET_FIELD_NAME(tyname,membername), \ + /* .TypeName = */ GET_FIELDTYPE_NAME(tyname,membername), \ + /* .FieldOffset = */ offset, \ +}, +#define CDAC_TYPE_END(name) { 0, }, +#include "datadescriptor.inc" + }, + + /* .GlobalLiteralValues = */ { +#define CDAC_GLOBAL(name,tyname,value) { /*.Name = */ GET_GLOBAL_NAME(name), /* .TypeName = */ GET_GLOBALTYPE_NAME(name), /* .Value = */ value }, +#include "datadescriptor.inc" + }, + + /* .GlobalPointerValues = */ { +#define CDAC_GLOBAL_POINTER(name,value) { /* .Name = */ GET_GLOBAL_NAME(name), /* .PointerDataIndex = */ GET_GLOBAL_POINTER_INDEX(name) }, +#include "datadescriptor.inc" + }, + + /* .GlobalStringValues = */ { +#define CDAC_GLOBAL_STRING(name,value) { /* .Name = */ GET_GLOBAL_NAME(name), /* .Value = */ GET_GLOBALSTRING_VALUE(name) }, +#include "datadescriptor.inc" + }, + + /* .NamesPool = */ ("\0" // starts with a nul +#define CDAC_BASELINE(name) name "\0" +#define CDAC_TYPE_BEGIN(name) #name "\0" +#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) #membername "\0" #membertyname "\0" +#define CDAC_GLOBAL_STRING(name,value) #name "\0" STRINGIFY(value) "\0" +#define CDAC_GLOBAL_POINTER(name,value) #name "\0" +#define CDAC_GLOBAL(name,tyname,value) #name "\0" #tyname "\0" +#include "datadescriptor.inc" + ), + + /* .EndMagic = */ { 0x01, 0x02, 0x03, 0x04 }, + } +}; + +// end blob definition + +} // extern "C" diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.inc b/src/coreclr/debug/runtimeinfo/datadescriptor.inc new file mode 100644 index 00000000000000..f28496e5c9b7d3 --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.inc @@ -0,0 +1,1027 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// No include guards. This file is included multiple times. + +// The format is: +// CDAC_BASELINE("string") baseline data contract that the runtime should follow. "empty" is reasonable +// CDAC_TYPES_BEGIN() +// ... ... +// CDAC_TYPES_END() +// CDAC_GLOBALS_BEGIN() +// ... ... +// CDAC_GLOBALS_END() +// +// In the format is: +// CDAC_TYPE_BEGIN(cdacTypeIdentifier) // defined a new data descriptor named cdacIdentifier +// +// CDAC_TYPE_SIZE(k) -or- CDAC_TYPE_INDETERMINATE(cdacTypeIdentifier) specifies that the type has +// size k (bytes - usually sizeof(SomeNativeType)) or specify that the type's size is not provided +// It is important that CDAC_TYPE_SIZE or CDAC_TYPE_INDETERMINATE immediately follows +// CDAC_TYPE_BEGIN +// +// CDAC_TYPE_FIELD(cdacTypeIdentifier, cdacFieldTypeIdentifier, cdacFieldName, k) specifies the +// field of "cdacTypeIdentifier" that has name cdacFieldName and has the type +// "cdacFieldtypeIdentifier" located at offset k in the type layout. k is usually +// offsetof(SomeClass, m_FieldName) if the field is public +// +// if the field is private, the convention is that SomeClass declares a friend struct +// cdac_data and provides a specialization of cdac_data with a public constexpr +// size_t member that holds the offset: +// +// class MyClass { +// private: +// void* m_myField; +// friend template cdac_data; +// }; +// template<> struct cdac_data { +// static constexpr size_t MyField = offsetof(MyClass, m_myField); +// }; +// +// then the field layout can be specified as +// CDAC_TYPE_FIELD(MyClassLayout, pointer, MyField, cdac_data::MyField) +// There can be zero or more CDAC_TYPE_FIELD entries per type layout +// For types mapping to managed objects, use exact managed type field names in the descriptor, as +// field names often can't change due to binary serialization or implicit diagnostic contracts +// +// CDAC_TYPE_END(cdacTypeIdentifier) specifies the end of the type layout for cdacTypeIdentifier +// +// In the format is: +// +// CDAC_GLOBAL(cdacGlobalName, cdacTypeIdentifier, value) +// or +// CDAC_GLOBAL_POINTER(cdacGlobalName, cdacTypeIdentifier, address) +// +// Zero or more globals can be defined +// +// if a global is given with CDAC_GLOBAL(), `value` should be a constexpr uint64_t (or convertible +// to uint64_t) for example, it can be a literal constant or a preprocessor definition +// +// if a global is a CDAC_GLOBAL_POINTER(), address should be a constexpr pointer or a constexpr +// uintptr_t +// +// +// +// This file is compiled using the target architecture. Preprocessor defines for the target +// platform will be available. It is ok to use `#ifdef`. + +#ifndef CDAC_BASELINE +#define CDAC_BASELINE(identifier) +#endif +#ifndef CDAC_TYPES_BEGIN +#define CDAC_TYPES_BEGIN() +#endif +#ifndef CDAC_TYPE_BEGIN +#define CDAC_TYPE_BEGIN(tyname) +#endif +#ifndef CDAC_TYPE_SIZE +#define CDAC_TYPE_SIZE(k) +#endif +#ifndef CDAC_TYPE_INDETERMINATE +#define CDAC_TYPE_INDETERMINATE(tyname) +#endif +#ifndef CDAC_TYPE_FIELD +#define CDAC_TYPE_FIELD(tyname,fieldtyname,fieldname,off) +#endif +#ifndef CDAC_TYPE_END +#define CDAC_TYPE_END(tyname) +#endif +#ifndef CDAC_TYPES_END +#define CDAC_TYPES_END() +#endif +#ifndef CDAC_GLOBALS_BEGIN +#define CDAC_GLOBALS_BEGIN() +#endif +#ifndef CDAC_GLOBAL +#define CDAC_GLOBAL(globalname,tyname,val) +#endif +#ifndef CDAC_GLOBAL_POINTER +#define CDAC_GLOBAL_POINTER(globalname,addr) +#endif +#ifndef CDAC_GLOBAL_STRING +#define CDAC_GLOBAL_STRING(globalname,stringval) +#endif +#ifndef CDAC_GLOBALS_END +#define CDAC_GLOBALS_END() +#endif + +CDAC_BASELINE("empty") +CDAC_TYPES_BEGIN() + +CDAC_TYPE_BEGIN(Thread) +CDAC_TYPE_INDETERMINATE(Thread) +CDAC_TYPE_FIELD(Thread, /*uint32*/, Id, cdac_data::Id) +CDAC_TYPE_FIELD(Thread, /*nuint*/, OSId, cdac_data::OSId) +CDAC_TYPE_FIELD(Thread, /*uint32*/, State, cdac_data::State) +CDAC_TYPE_FIELD(Thread, /*uint32*/, PreemptiveGCDisabled, cdac_data::PreemptiveGCDisabled) +CDAC_TYPE_FIELD(Thread, /*pointer*/, RuntimeThreadLocals, cdac_data::RuntimeThreadLocals) +CDAC_TYPE_FIELD(Thread, /*pointer*/, Frame, cdac_data::Frame) +CDAC_TYPE_FIELD(Thread, /*pointer*/, ExceptionTracker, cdac_data::ExceptionTracker) +CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_data::ExposedObject) +CDAC_TYPE_FIELD(Thread, GCHandle, LastThrownObject, cdac_data::LastThrownObject) +CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_data::Link) +#ifndef TARGET_UNIX +CDAC_TYPE_FIELD(Thread, /*pointer*/, TEB, cdac_data::TEB) +#endif +CDAC_TYPE_END(Thread) + +CDAC_TYPE_BEGIN(ThreadStore) +CDAC_TYPE_INDETERMINATE(ThreadStore) +CDAC_TYPE_FIELD(ThreadStore, /*SLink*/, FirstThreadLink, cdac_data::FirstThreadLink) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, ThreadCount, cdac_data::ThreadCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, UnstartedCount, cdac_data::UnstartedCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, BackgroundCount, cdac_data::BackgroundCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, PendingCount, cdac_data::PendingCount) +CDAC_TYPE_FIELD(ThreadStore, /*int32*/, DeadCount, cdac_data::DeadCount) +CDAC_TYPE_END(ThreadStore) + +CDAC_TYPE_BEGIN(RuntimeThreadLocals) +CDAC_TYPE_INDETERMINATE(RuntimeThreadLocals) +CDAC_TYPE_FIELD(RuntimeThreadLocals, /*EEAllocContext*/, AllocContext, offsetof(RuntimeThreadLocals, alloc_context)) +CDAC_TYPE_END(RuntimeThreadLocals) + +CDAC_TYPE_BEGIN(EEAllocContext) +CDAC_TYPE_INDETERMINATE(EEAllocContext) +CDAC_TYPE_FIELD(EEAllocContext, /*GCAllocContext*/, GCAllocationContext, offsetof(ee_alloc_context, m_GCAllocContext)) +CDAC_TYPE_END(EEAllocContext) + +CDAC_TYPE_BEGIN(GCAllocContext) +CDAC_TYPE_INDETERMINATE(GCAllocContext) +CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Pointer, offsetof(gc_alloc_context, alloc_ptr)) +CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Limit, offsetof(gc_alloc_context, alloc_limit)) +CDAC_TYPE_END(GCAllocContext) + +// Exception + +// Use exact managed type field names for the descriptor as field names often can't change due to binary serialization or implicit diagnostic contracts +CDAC_TYPE_BEGIN(Exception) +CDAC_TYPE_INDETERMINATE(Exception) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _message, cdac_data::_message) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _innerException, cdac_data::_innerException) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTrace, cdac_data::_stackTrace) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _watsonBuckets, cdac_data::_watsonBuckets) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTraceString, cdac_data::_stackTraceString) +CDAC_TYPE_FIELD(Exception, /*pointer*/, _remoteStackTraceString, cdac_data::_remoteStackTraceString) +CDAC_TYPE_FIELD(Exception, /*int32*/, _HResult, cdac_data::_HResult) +CDAC_TYPE_FIELD(Exception, /*int32*/, _xcode, cdac_data::_xcode) +CDAC_TYPE_END(Exception) + +CDAC_TYPE_BEGIN(ExceptionInfo) +CDAC_TYPE_INDETERMINATE(ExceptionInfo) +CDAC_TYPE_FIELD(ExceptionInfo, /*pointer*/, ThrownObject, offsetof(ExInfo, m_hThrowable)) +CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExInfo, m_pPrevNestedInfo)) +CDAC_TYPE_END(ExceptionInfo) + + +CDAC_TYPE_BEGIN(GCHandle) +CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE)) +CDAC_TYPE_END(GCHandle) + +// Object + +CDAC_TYPE_BEGIN(Object) +CDAC_TYPE_INDETERMINATE(Object) +CDAC_TYPE_FIELD(Object, /*pointer*/, m_pMethTab, cdac_data::m_pMethTab) +CDAC_TYPE_END(Object) + +CDAC_TYPE_BEGIN(String) +CDAC_TYPE_INDETERMINATE(String) +CDAC_TYPE_FIELD(String, /*pointer*/, m_FirstChar, cdac_data::m_FirstChar) +CDAC_TYPE_FIELD(String, /*uint32*/, m_StringLength, cdac_data::m_StringLength) +CDAC_TYPE_END(String) + +CDAC_TYPE_BEGIN(Array) +CDAC_TYPE_SIZE(sizeof(ArrayBase)) +CDAC_TYPE_FIELD(Array, /*pointer*/, m_NumComponents, cdac_data::m_NumComponents) +CDAC_TYPE_END(Array) + +CDAC_TYPE_BEGIN(InteropSyncBlockInfo) +CDAC_TYPE_INDETERMINATE(InteropSyncBlockInfo) +#ifdef FEATURE_COMINTEROP +CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, CCW, cdac_data::CCW) +CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, RCW, cdac_data::RCW) +#endif // FEATURE_COMINTEROP +CDAC_TYPE_END(InteropSyncBlockInfo) + +CDAC_TYPE_BEGIN(SyncBlock) +CDAC_TYPE_INDETERMINATE(SyncBlock) +CDAC_TYPE_FIELD(SyncBlock, /*pointer*/, InteropInfo, cdac_data::InteropInfo) +CDAC_TYPE_END(SyncBlock) + +CDAC_TYPE_BEGIN(SyncTableEntry) +CDAC_TYPE_SIZE(sizeof(SyncTableEntry)) +CDAC_TYPE_FIELD(SyncTableEntry, /*pointer*/, SyncBlock, offsetof(SyncTableEntry, m_SyncBlock)) +CDAC_TYPE_END(SyncTableEntry) + +// Loader + +CDAC_TYPE_BEGIN(Module) +CDAC_TYPE_INDETERMINATE(Module) +CDAC_TYPE_FIELD(Module, /*pointer*/, Assembly, cdac_data::Assembly) +CDAC_TYPE_FIELD(Module, /*pointer*/, PEAssembly, cdac_data::PEAssembly) +CDAC_TYPE_FIELD(Module, /*pointer*/, Base, cdac_data::Base) +CDAC_TYPE_FIELD(Module, /*uint32*/, Flags, cdac_data::Flags) +CDAC_TYPE_FIELD(Module, /*pointer*/, LoaderAllocator, cdac_data::LoaderAllocator) +CDAC_TYPE_FIELD(Module, /*pointer*/, DynamicMetadata, cdac_data::DynamicMetadata) +CDAC_TYPE_FIELD(Module, /*pointer*/, Path, cdac_data::Path) +CDAC_TYPE_FIELD(Module, /*pointer*/, FileName, cdac_data::FileName) +CDAC_TYPE_FIELD(Module, /*pointer*/, ReadyToRunInfo, cdac_data::ReadyToRunInfo) +CDAC_TYPE_FIELD(Module, /*pointer*/, GrowableSymbolStream, cdac_data::GrowableSymbolStream) + +CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_data::FieldDefToDescMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_data::ManifestModuleReferencesMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, MemberRefToDescMap, cdac_data::MemberRefToDescMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, MethodDefToDescMap, cdac_data::MethodDefToDescMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, TypeDefToMethodTableMap, cdac_data::TypeDefToMethodTableMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, TypeRefToMethodTableMap, cdac_data::TypeRefToMethodTableMap) +CDAC_TYPE_FIELD(Module, /*pointer*/, MethodDefToILCodeVersioningStateMap, cdac_data::MethodDefToILCodeVersioningStateMap) +CDAC_TYPE_END(Module) + +CDAC_TYPE_BEGIN(ModuleLookupMap) +CDAC_TYPE_FIELD(ModuleLookupMap, /*pointer*/, TableData, offsetof(LookupMapBase, pTable)) +CDAC_TYPE_FIELD(ModuleLookupMap, /*pointer*/, Next, offsetof(LookupMapBase, pNext)) +CDAC_TYPE_FIELD(ModuleLookupMap, /*uint32*/, Count, offsetof(LookupMapBase, dwCount)) +CDAC_TYPE_FIELD(ModuleLookupMap, /*nuint*/, SupportedFlagsMask, offsetof(LookupMapBase, supportedFlags)) +CDAC_TYPE_END(ModuleLookupMap) + +CDAC_TYPE_BEGIN(Assembly) +CDAC_TYPE_INDETERMINATE(Assembly) +#ifdef FEATURE_COLLECTIBLE_TYPES +CDAC_TYPE_FIELD(Assembly, /*uint8*/, IsCollectible, cdac_data::IsCollectible) +#endif +CDAC_TYPE_FIELD(Assembly, /*pointer*/, Module, cdac_data::Module) +CDAC_TYPE_FIELD(Assembly, /*pointer*/, Error, cdac_data::Error) +CDAC_TYPE_FIELD(Assembly, /*uint32*/, NotifyFlags, cdac_data::NotifyFlags) +CDAC_TYPE_FIELD(Assembly, /*uint32*/, Level, cdac_data::Level) +CDAC_TYPE_END(Assembly) + +CDAC_TYPE_BEGIN(LoaderAllocator) +CDAC_TYPE_INDETERMINATE(LoaderAllocator) +CDAC_TYPE_FIELD(LoaderAllocator, /*uint32*/, ReferenceCount, cdac_data::ReferenceCount) +CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, HighFrequencyHeap, cdac_data::HighFrequencyHeap) +CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, LowFrequencyHeap, cdac_data::LowFrequencyHeap) +CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, StubHeap, cdac_data::StubHeap) +CDAC_TYPE_END(LoaderAllocator) + +CDAC_TYPE_BEGIN(PEAssembly) +CDAC_TYPE_INDETERMINATE(PEAssembly) +CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, PEImage, cdac_data::PEImage) +CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, AssemblyBinder, cdac_data::AssemblyBinder) +CDAC_TYPE_END(PEAssembly) + +CDAC_TYPE_BEGIN(AssemblyBinder) +CDAC_TYPE_INDETERMINATE(AssemblyBinder) +CDAC_TYPE_FIELD(AssemblyBinder, /*pointer*/, ManagedAssemblyLoadContext, cdac_data::ManagedAssemblyLoadContext) +CDAC_TYPE_END(AssemblyBinder) + +CDAC_TYPE_BEGIN(PEImage) +CDAC_TYPE_INDETERMINATE(PEImage) +CDAC_TYPE_FIELD(PEImage, /*pointer*/, LoadedImageLayout, cdac_data::LoadedImageLayout) +CDAC_TYPE_FIELD(PEImage, /*ProbeExtensionResult*/, ProbeExtensionResult, cdac_data::ProbeExtensionResult) +CDAC_TYPE_END(PEImage) + +CDAC_TYPE_BEGIN(PEImageLayout) +CDAC_TYPE_FIELD(PEImageLayout, /*pointer*/, Base, cdac_data::Base) +CDAC_TYPE_FIELD(PEImageLayout, /*uint32*/, Size, cdac_data::Size) +CDAC_TYPE_FIELD(PEImageLayout, /*uint32*/, Flags, cdac_data::Flags) +CDAC_TYPE_END(PEImageLayout) + +CDAC_TYPE_BEGIN(CGrowableSymbolStream) +CDAC_TYPE_INDETERMINATE(CGrowableSymbolStream) +CDAC_TYPE_FIELD(CGrowableSymbolStream, /*pointer*/, Buffer, cdac_data::Buffer) +CDAC_TYPE_FIELD(CGrowableSymbolStream, /*uint32*/, Size, cdac_data::Size) +CDAC_TYPE_END(CGrowableSymbolStream) + +CDAC_TYPE_BEGIN(ProbeExtensionResult) +CDAC_TYPE_INDETERMINATE(ProbeExtensionResult) +CDAC_TYPE_FIELD(ProbeExtensionResult, /*int32*/, Type, offsetof(ProbeExtensionResult, Type)) +CDAC_TYPE_END(ProbeExtensionResult) + +CDAC_TYPE_BEGIN(AppDomain) +CDAC_TYPE_INDETERMINATE(AppDomain) +CDAC_TYPE_FIELD(AppDomain, /*pointer*/, RootAssembly, cdac_data::RootAssembly) +CDAC_TYPE_FIELD(AppDomain, /*DomainAssemblyList*/, DomainAssemblyList, cdac_data::DomainAssemblyList) +CDAC_TYPE_FIELD(AppDomain, /*pointer*/, FriendlyName, cdac_data::FriendlyName) +CDAC_TYPE_END(AppDomain) + +CDAC_TYPE_BEGIN(SystemDomain) +CDAC_TYPE_INDETERMINATE(SystemDomain) +CDAC_TYPE_FIELD(SystemDomain, /*GlobalLoaderAllocator*/, GlobalLoaderAllocator, cdac_data::GlobalLoaderAllocator) +CDAC_TYPE_END(SystemDomain) + +CDAC_TYPE_BEGIN(ArrayListBase) +CDAC_TYPE_INDETERMINATE(ArrayListBase) +CDAC_TYPE_FIELD(ArrayListBase, /*uint32*/, Count, cdac_data::Count) +CDAC_TYPE_FIELD(ArrayListBase, /*pointer*/, FirstBlock, cdac_data::FirstBlock) +CDAC_TYPE_END(ArrayListBase) + +CDAC_TYPE_BEGIN(ArrayListBlock) +CDAC_TYPE_INDETERMINATE(ArrayListBlock) +CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, Next, cdac_data::Next) +CDAC_TYPE_FIELD(ArrayListBlock, /*uint32*/, Size, cdac_data::Size) +CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, ArrayStart, cdac_data::ArrayStart) +CDAC_TYPE_END(ArrayListBlock) + +// RuntimeTypeSystem + +CDAC_TYPE_BEGIN(MethodTable) +CDAC_TYPE_SIZE(sizeof(MethodTable)) +CDAC_TYPE_FIELD(MethodTable, /*uint32*/, MTFlags, cdac_data::MTFlags) +CDAC_TYPE_FIELD(MethodTable, /*uint32*/, BaseSize, cdac_data::BaseSize) +CDAC_TYPE_FIELD(MethodTable, /*uint32*/, MTFlags2, cdac_data::MTFlags2) +CDAC_TYPE_FIELD(MethodTable, /*nuint*/, EEClassOrCanonMT, cdac_data::EEClassOrCanonMT) +CDAC_TYPE_FIELD(MethodTable, /*pointer*/, Module, cdac_data::Module) +CDAC_TYPE_FIELD(MethodTable, /*pointer*/, ParentMethodTable, cdac_data::ParentMethodTable) +CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumInterfaces, cdac_data::NumInterfaces) +CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumVirtuals, cdac_data::NumVirtuals) +CDAC_TYPE_FIELD(MethodTable, /*pointer*/, PerInstInfo, cdac_data::PerInstInfo) +CDAC_TYPE_FIELD(MethodTable, /*pointer*/, AuxiliaryData, cdac_data::AuxiliaryData) +CDAC_TYPE_END(MethodTable) + +CDAC_TYPE_BEGIN(MethodTableAuxiliaryData) +CDAC_TYPE_INDETERMINATE(MethodTableAuxiliaryData) +CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*pointer*/, LoaderModule, offsetof(MethodTableAuxiliaryData, m_pLoaderModule)) +CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*int16*/, OffsetToNonVirtualSlots, offsetof(MethodTableAuxiliaryData, m_offsetToNonVirtualSlots)) +CDAC_TYPE_END(MethodTableAuxiliaryData) + +CDAC_TYPE_BEGIN(EEClass) +CDAC_TYPE_INDETERMINATE(EEClass) +CDAC_TYPE_FIELD(EEClass, /*pointer*/, MethodTable, cdac_data::MethodTable) +CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumMethods, cdac_data::NumMethods) +CDAC_TYPE_FIELD(EEClass, /*pointer*/, FieldDescList, cdac_data::FieldDescList) +CDAC_TYPE_FIELD(EEClass, /*uint32*/, CorTypeAttr, cdac_data::CorTypeAttr) +CDAC_TYPE_FIELD(EEClass, /*uint8*/, InternalCorElementType, cdac_data::InternalCorElementType) +CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumInstanceFields, cdac_data::NumInstanceFields) +CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumStaticFields, cdac_data::NumStaticFields) +CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumThreadStaticFields, cdac_data::NumThreadStaticFields) +CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumNonVirtualSlots, cdac_data::NumNonVirtualSlots) +CDAC_TYPE_END(EEClass) + +CDAC_TYPE_BEGIN(ArrayClass) +CDAC_TYPE_INDETERMINATE(ArrayClass) +CDAC_TYPE_FIELD(ArrayClass, /*uint8*/, Rank, cdac_data::Rank) +CDAC_TYPE_END(ArrayClass) + +CDAC_TYPE_BEGIN(GenericsDictInfo) +CDAC_TYPE_INDETERMINATE(GenericsDictInfo) +CDAC_TYPE_FIELD(GenericsDictInfo, /*uint16*/, NumDicts, offsetof(GenericsDictInfo, m_wNumDicts)) +CDAC_TYPE_FIELD(GenericsDictInfo, /*uint16*/, NumTypeArgs, offsetof(GenericsDictInfo, m_wNumTyPars)) +CDAC_TYPE_END(GenericsDictInfo) + +CDAC_TYPE_BEGIN(TypeDesc) +CDAC_TYPE_INDETERMINATE(TypeDesc) +CDAC_TYPE_FIELD(TypeDesc, /*uint32*/, TypeAndFlags, cdac_data::TypeAndFlags) +CDAC_TYPE_END(TypeDesc) + +CDAC_TYPE_BEGIN(ParamTypeDesc) +CDAC_TYPE_INDETERMINATE(ParamTypeDesc) +CDAC_TYPE_FIELD(ParamTypeDesc, /*pointer*/, TypeArg, cdac_data::TypeArg) +CDAC_TYPE_END(ParamTypeDesc) + +CDAC_TYPE_BEGIN(TypeVarTypeDesc) +CDAC_TYPE_INDETERMINATE(TypeVarTypeDesc) +CDAC_TYPE_FIELD(TypeVarTypeDesc, /*pointer*/, Module, cdac_data::Module) +CDAC_TYPE_FIELD(TypeVarTypeDesc, /*uint32*/, Token, cdac_data::Token) +CDAC_TYPE_END(TypeVarTypeDesc) + +CDAC_TYPE_BEGIN(FnPtrTypeDesc) +CDAC_TYPE_INDETERMINATE(FnPtrTypeDesc) +CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, NumArgs, cdac_data::NumArgs) +CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, CallConv, cdac_data::CallConv) +CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, RetAndArgTypes, cdac_data::RetAndArgTypes) +CDAC_TYPE_FIELD(FnPtrTypeDesc, /*pointer*/, LoaderModule, cdac_data::LoaderModule) +CDAC_TYPE_END(FnPtrTypeDesc) + +CDAC_TYPE_BEGIN(DynamicMetadata) +CDAC_TYPE_FIELD(DynamicMetadata, /*uint32*/, Size, cdac_data::Size) +CDAC_TYPE_FIELD(DynamicMetadata, /*inline byte array*/, Data, cdac_data::Data) +CDAC_TYPE_END(DynamicMetadata) + +#ifdef STRESS_LOG +CDAC_TYPE_BEGIN(StressLog) +CDAC_TYPE_SIZE(sizeof(StressLog)) +CDAC_TYPE_FIELD(StressLog, /* uint32 */, LoggedFacilities, cdac_offsets::facilitiesToLog) +CDAC_TYPE_FIELD(StressLog, /* uint32 */, Level, cdac_offsets::levelToLog) +CDAC_TYPE_FIELD(StressLog, /* uint32 */, MaxSizePerThread, cdac_offsets::MaxSizePerThread) +CDAC_TYPE_FIELD(StressLog, /* uint32 */, MaxSizeTotal, cdac_offsets::MaxSizeTotal) +CDAC_TYPE_FIELD(StressLog, /* uint32 */, TotalChunks, cdac_offsets::totalChunk) +CDAC_TYPE_FIELD(StressLog, /* pointer */, Logs, cdac_offsets::logs) +CDAC_TYPE_FIELD(StressLog, /* uint64 */, TickFrequency, cdac_offsets::tickFrequency) +CDAC_TYPE_FIELD(StressLog, /* uint64 */, StartTimestamp, cdac_offsets::startTimeStamp) +CDAC_TYPE_FIELD(StressLog, /* nuint */, ModuleOffset, cdac_offsets::moduleOffset) +CDAC_TYPE_END(StressLog) + +CDAC_TYPE_BEGIN(StressLogModuleDesc) +CDAC_TYPE_SIZE(cdac_offsets::ModuleDesc::type_size) +CDAC_TYPE_FIELD(StressLogModuleDesc, pointer, BaseAddress, cdac_offsets::ModuleDesc::baseAddress) +CDAC_TYPE_FIELD(StressLogModuleDesc, nuint, Size, cdac_offsets::ModuleDesc::size) +CDAC_TYPE_END(StressLogModuleDesc) + +CDAC_TYPE_BEGIN(ThreadStressLog) +CDAC_TYPE_INDETERMINATE(ThreadStressLog) +CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, Next, cdac_offsets::next) +CDAC_TYPE_FIELD(ThreadStressLog, uint64, ThreadId, cdac_offsets::threadId) +CDAC_TYPE_FIELD(ThreadStressLog, uint8, WriteHasWrapped, cdac_offsets::writeHasWrapped) +CDAC_TYPE_FIELD(ThreadStressLog, pointer, CurrentPtr, cdac_offsets::curPtr) +CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, ChunkListHead, cdac_offsets::chunkListHead) +CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, ChunkListTail, cdac_offsets::chunkListTail) +CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, CurrentWriteChunk, cdac_offsets::curWriteChunk) +CDAC_TYPE_END(ThreadStressLog) + +CDAC_TYPE_BEGIN(StressLogChunk) +CDAC_TYPE_SIZE(sizeof(StressLogChunk)) +CDAC_TYPE_FIELD(StressLogChunk, /* pointer */, Prev, offsetof(StressLogChunk, prev)) +CDAC_TYPE_FIELD(StressLogChunk, /* pointer */, Next, offsetof(StressLogChunk, next)) +CDAC_TYPE_FIELD(StressLogChunk, /* uint8[STRESSLOG_CHUNK_SIZE] */, Buf, offsetof(StressLogChunk, buf)) +CDAC_TYPE_FIELD(StressLogChunk, /* uint32 */, Sig1, offsetof(StressLogChunk, dwSig1)) +CDAC_TYPE_FIELD(StressLogChunk, /* uint32 */, Sig2, offsetof(StressLogChunk, dwSig2)) +CDAC_TYPE_END(StressLogChunk) + +// The StressMsg Header is the fixed size portion of the StressMsg +CDAC_TYPE_BEGIN(StressMsgHeader) +CDAC_TYPE_SIZE(sizeof(StressMsg)) +CDAC_TYPE_END(StressMsgHeader) + +CDAC_TYPE_BEGIN(StressMsg) +CDAC_TYPE_INDETERMINATE(StressMsg) +CDAC_TYPE_FIELD(StressMsg, StressMsgHeader, Header, 0) +CDAC_TYPE_FIELD(StressMsg, /* pointer */, Args, offsetof(StressMsg, args)) +CDAC_TYPE_END(StressMsg) +#endif + +CDAC_TYPE_BEGIN(MethodDesc) +CDAC_TYPE_SIZE(sizeof(MethodDesc)) +CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, ChunkIndex, cdac_data::ChunkIndex) +CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Slot, cdac_data::Slot) +CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags, cdac_data::Flags) +CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags3AndTokenRemainder, cdac_data::Flags3AndTokenRemainder) +CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, EntryPointFlags, cdac_data::EntryPointFlags) +CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, CodeData, cdac_data::CodeData) +#ifdef HAVE_GCCOVER +CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, offsetof(MethodDesc, m_GcCover)) +#endif // HAVE_GCCOVER +CDAC_TYPE_END(MethodDesc) + +CDAC_TYPE_BEGIN(MethodDescChunk) +CDAC_TYPE_SIZE(sizeof(MethodDescChunk)) +CDAC_TYPE_FIELD(MethodDescChunk, /*pointer*/, MethodTable, cdac_data::MethodTable) +CDAC_TYPE_FIELD(MethodDescChunk, /*pointer*/, Next, cdac_data::Next) +CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Size, cdac_data::Size) +CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Count, cdac_data::Count) +CDAC_TYPE_FIELD(MethodDescChunk, /*uint16*/, FlagsAndTokenRange, cdac_data::FlagsAndTokenRange) +CDAC_TYPE_END(MethodDescChunk) + +CDAC_TYPE_BEGIN(NonVtableSlot) +CDAC_TYPE_SIZE(sizeof(MethodDesc::NonVtableSlot)) +CDAC_TYPE_END(NonVtableSlot) + +CDAC_TYPE_BEGIN(MethodImpl) +CDAC_TYPE_SIZE(sizeof(MethodImpl)) +CDAC_TYPE_END(MethodImpl) + +CDAC_TYPE_BEGIN(NativeCodeSlot) +CDAC_TYPE_SIZE(sizeof(MethodDesc::NativeCodeSlot)) +CDAC_TYPE_END(NativeCodeSlot) + +CDAC_TYPE_BEGIN(InstantiatedMethodDesc) +CDAC_TYPE_SIZE(sizeof(InstantiatedMethodDesc)) +CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*pointer*/, PerInstInfo, cdac_data::PerInstInfo) +CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*uint16*/, Flags2, cdac_data::Flags2) +CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*uint16*/, NumGenericArgs, cdac_data::NumGenericArgs) +CDAC_TYPE_END(InstantiatedMethodDesc) + +CDAC_TYPE_BEGIN(StoredSigMethodDesc) +CDAC_TYPE_INDETERMINATE(StoredSigMethodDesc) +CDAC_TYPE_FIELD(StoredSigMethodDesc, /*pointer*/, Sig, cdac_data::Sig) +CDAC_TYPE_FIELD(StoredSigMethodDesc, /*uint32*/, cSig, cdac_data::cSig) +CDAC_TYPE_FIELD(StoredSigMethodDesc, /*uint32*/, ExtendedFlags, cdac_data::ExtendedFlags) +CDAC_TYPE_END(StoredSigMethodDesc) + +CDAC_TYPE_BEGIN(DynamicMethodDesc) +CDAC_TYPE_SIZE(sizeof(DynamicMethodDesc)) +CDAC_TYPE_FIELD(DynamicMethodDesc, /*pointer*/, MethodName, cdac_data::MethodName) +CDAC_TYPE_END(DynamicMethodDesc) + +CDAC_TYPE_BEGIN(ArrayMethodDesc) +CDAC_TYPE_SIZE(sizeof(ArrayMethodDesc)) +CDAC_TYPE_END(ArrayMethodDesc) + +CDAC_TYPE_BEGIN(FCallMethodDesc) +CDAC_TYPE_SIZE(sizeof(FCallMethodDesc)) +CDAC_TYPE_END(FCallMethodDesc) + +CDAC_TYPE_BEGIN(PInvokeMethodDesc) +CDAC_TYPE_SIZE(sizeof(NDirectMethodDesc)) +CDAC_TYPE_END(PInvokeMethodDesc) + +CDAC_TYPE_BEGIN(EEImplMethodDesc) +CDAC_TYPE_SIZE(sizeof(EEImplMethodDesc)) +CDAC_TYPE_END(EEImplMethodDesc) + +#ifdef FEATURE_COMINTEROP +CDAC_TYPE_BEGIN(CLRToCOMCallMethodDesc) +CDAC_TYPE_SIZE(sizeof(CLRToCOMCallMethodDesc)) +CDAC_TYPE_END(CLRToCOMCallMethodDesc) +#endif // FEATURE_COMINTEROP + +CDAC_TYPE_BEGIN(CodePointer) +CDAC_TYPE_SIZE(sizeof(PCODE)) +CDAC_TYPE_END(CodePointer) + +CDAC_TYPE_BEGIN(MethodDescCodeData) +CDAC_TYPE_INDETERMINATE(MethodDescCodeData) +CDAC_TYPE_FIELD(MethodDescCodeData, /*CodePointer*/, TemporaryEntryPoint, offsetof(MethodDescCodeData,TemporaryEntryPoint)) +CDAC_TYPE_FIELD(MethodDescCodeData, /*pointer*/, VersioningState, offsetof(MethodDescCodeData,VersioningState)) +CDAC_TYPE_END(MethodDescCodeData) + +CDAC_TYPE_BEGIN(MethodDescVersioningState) +CDAC_TYPE_INDETERMINATE(MethodDescVersioningState) +CDAC_TYPE_FIELD(MethodDescVersioningState, /*pointer*/, NativeCodeVersionNode, cdac_data::NativeCodeVersionNode) +CDAC_TYPE_FIELD(MethodDescVersioningState, /*uint8*/, Flags, cdac_data::Flags) +CDAC_TYPE_END(MethodDescVersioningState) + +CDAC_TYPE_BEGIN(PrecodeMachineDescriptor) +CDAC_TYPE_INDETERMINATE(PrecodeMachineDescriptor) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, InvalidPrecodeType, offsetof(PrecodeMachineDescriptor, InvalidPrecodeType)) +#ifdef HAS_NDIRECT_IMPORT_PRECODE +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, PInvokeImportPrecodeType, offsetof(PrecodeMachineDescriptor, PInvokeImportPrecodeType)) +#endif + +#ifdef HAS_FIXUP_PRECODE +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupPrecodeType, offsetof(PrecodeMachineDescriptor, FixupPrecodeType)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupCodeOffset, offsetof(PrecodeMachineDescriptor, FixupCodeOffset)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupStubPrecodeSize, offsetof(PrecodeMachineDescriptor, FixupStubPrecodeSize)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, FixupBytes, offsetof(PrecodeMachineDescriptor, FixupBytes)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, FixupIgnoredBytes, offsetof(PrecodeMachineDescriptor, FixupIgnoredBytes)) +#endif // HAS_FIXUP_PRECODE + +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, StubPrecodeSize, offsetof(PrecodeMachineDescriptor, StubPrecodeSize)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, StubPrecodeType, offsetof(PrecodeMachineDescriptor, StubPrecodeType)) + +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, StubBytes, offsetof(PrecodeMachineDescriptor, StubBytes)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, StubIgnoredBytes, offsetof(PrecodeMachineDescriptor, StubIgnoredBytes)) + +#ifdef HAS_THISPTR_RETBUF_PRECODE +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, ThisPointerRetBufPrecodeType, offsetof(PrecodeMachineDescriptor, ThisPointerRetBufPrecodeType)) +#endif +#ifdef FEATURE_INTERPRETER +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, InterpreterPrecodeType, offsetof(PrecodeMachineDescriptor, InterpreterPrecodeType)) +#endif +#ifdef FEATURE_STUBPRECODE_DYNAMIC_HELPERS +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, DynamicHelperPrecodeType, offsetof(PrecodeMachineDescriptor, DynamicHelperPrecodeType)) +#endif +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, UMEntryPrecodeType, offsetof(PrecodeMachineDescriptor, UMEntryPrecodeType)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint32*/, StubCodePageSize, offsetof(PrecodeMachineDescriptor, StubCodePageSize)) +CDAC_TYPE_END(PrecodeMachineDescriptor) + +CDAC_TYPE_BEGIN(PlatformMetadata) +CDAC_TYPE_INDETERMINATE(PlatformMetadata) +CDAC_TYPE_FIELD(PlatformMetadata, /*PrecodeMachineDescriptor*/, PrecodeMachineDescriptor, offsetof(CDacPlatformMetadata, precode)) +CDAC_TYPE_FIELD(PlatformMetadata, /*uint8*/, CodePointerFlags, offsetof(CDacPlatformMetadata, codePointerFlags)) +CDAC_TYPE_END(PlatformMetadata) + +CDAC_TYPE_BEGIN(StubPrecodeData) +CDAC_TYPE_INDETERMINATE(StubPrecodeData) +CDAC_TYPE_FIELD(StubPrecodeData, /*pointer*/, SecretParam, offsetof(StubPrecodeData, SecretParam)) +CDAC_TYPE_FIELD(StubPrecodeData, /*uint8*/, Type, offsetof(StubPrecodeData, Type)) +CDAC_TYPE_END(StubPrecodeData) + +#ifdef HAS_THISPTR_RETBUF_PRECODE +CDAC_TYPE_BEGIN(ThisPtrRetBufPrecodeData) +CDAC_TYPE_INDETERMINATE(ThisPtrRetBufPrecodeData) +CDAC_TYPE_FIELD(ThisPtrRetBufPrecodeData, /*pointer*/, MethodDesc, offsetof(ThisPtrRetBufPrecodeData, MethodDesc)) +CDAC_TYPE_END(ThisPtrRetBufPrecodeData) +#endif + +CDAC_TYPE_BEGIN(FixupPrecodeData) +CDAC_TYPE_INDETERMINATE(FixupPrecodeData) +CDAC_TYPE_FIELD(FixupPrecodeData, /*pointer*/, MethodDesc, offsetof(FixupPrecodeData, MethodDesc)) +CDAC_TYPE_END(FixupPrecodeData) + +CDAC_TYPE_BEGIN(ReadyToRunInfo) +CDAC_TYPE_INDETERMINATE(ReadyToRunInfo) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, ReadyToRunHeader, cdac_data::ReadyToRunHeader) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, CompositeInfo, cdac_data::CompositeInfo) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumRuntimeFunctions, cdac_data::NumRuntimeFunctions) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, RuntimeFunctions, cdac_data::RuntimeFunctions) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumHotColdMap, cdac_data::NumHotColdMap) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, HotColdMap, cdac_data::HotColdMap) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_data::DelayLoadMethodCallThunks) +CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data::EntryPointToMethodDescMap) +CDAC_TYPE_END(ReadyToRunInfo) + +CDAC_TYPE_BEGIN(ReadyToRunHeader) +CDAC_TYPE_INDETERMINATE(READYTORUN_HEADER) +CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MajorVersion, offsetof(READYTORUN_HEADER, MajorVersion)) +CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MinorVersion, offsetof(READYTORUN_HEADER, MinorVersion)) +CDAC_TYPE_END(ReadyToRunHeader) + +CDAC_TYPE_BEGIN(ImageDataDirectory) +CDAC_TYPE_SIZE(sizeof(IMAGE_DATA_DIRECTORY)) +CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_DATA_DIRECTORY, VirtualAddress)) +CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, Size, offsetof(IMAGE_DATA_DIRECTORY, Size)) +CDAC_TYPE_END(ImageDataDirectory) + +CDAC_TYPE_BEGIN(RuntimeFunction) +CDAC_TYPE_SIZE(sizeof(RUNTIME_FUNCTION)) +CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, BeginAddress, offsetof(RUNTIME_FUNCTION, BeginAddress)) +#ifdef TARGET_AMD64 +CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, EndAddress, offsetof(RUNTIME_FUNCTION, EndAddress)) +#endif +CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, UnwindData, offsetof(RUNTIME_FUNCTION, UnwindData)) +CDAC_TYPE_END(RuntimeFunction) + +CDAC_TYPE_BEGIN(UnwindInfo) +CDAC_TYPE_INDETERMINATE(UnwindInfo) +#ifdef TARGET_X86 +CDAC_TYPE_FIELD(UnwindInfo, /*uint32*/, FunctionLength, offsetof(UNWIND_INFO, FunctionLength)) +#endif +CDAC_TYPE_END(UnwindInfo) + +CDAC_TYPE_BEGIN(HashMap) +CDAC_TYPE_INDETERMINATE(HashMap) +CDAC_TYPE_FIELD(HashMap, /*pointer*/, Buckets, cdac_data::Buckets) +CDAC_TYPE_END(HashMap) + +CDAC_TYPE_BEGIN(Bucket) +CDAC_TYPE_SIZE(sizeof(Bucket)) +CDAC_TYPE_FIELD(Bucket, /*pointer*/, Keys, offsetof(Bucket, m_rgKeys)) +CDAC_TYPE_FIELD(Bucket, /*pointer*/, Values, offsetof(Bucket, m_rgValues)) +CDAC_TYPE_END(Bucket) + +CDAC_TYPE_BEGIN(RangeSectionMap) +CDAC_TYPE_INDETERMINATE(RangeSectionMap) +CDAC_TYPE_FIELD(RangeSectionMap, /*pointer*/, TopLevelData, cdac_data::TopLevelData) +CDAC_TYPE_END(RangeSectionMap) + +CDAC_TYPE_BEGIN(RangeSectionFragment) +CDAC_TYPE_INDETERMINATE(RangeSectionFragment) +CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, RangeBegin, cdac_data::RangeSectionFragment::RangeBegin) +CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, RangeEndOpen, cdac_data::RangeSectionFragment::RangeEndOpen) +CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, RangeSection, cdac_data::RangeSectionFragment::RangeSection) +CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, Next, cdac_data::RangeSectionFragment::Next) +CDAC_TYPE_END(RangeSectionFragment) + +CDAC_TYPE_BEGIN(RangeSection) +CDAC_TYPE_INDETERMINATE(RangeSection) +CDAC_TYPE_FIELD(RangeSection, /*pointer*/, RangeBegin, cdac_data::RangeBegin) +CDAC_TYPE_FIELD(RangeSection, /*pointer*/, RangeEndOpen, cdac_data::RangeEndOpen) +CDAC_TYPE_FIELD(RangeSection, /*pointer*/, NextForDelete, cdac_data::NextForDelete) +CDAC_TYPE_FIELD(RangeSection, /*pointer*/, JitManager, cdac_data::JitManager) +CDAC_TYPE_FIELD(RangeSection, /*int32_t*/, Flags, cdac_data::Flags) +CDAC_TYPE_FIELD(RangeSection, /*pointer*/, HeapList, cdac_data::HeapList) +CDAC_TYPE_FIELD(RangeSection, /*pointer*/, R2RModule, cdac_data::R2RModule) +CDAC_TYPE_END(RangeSection) + +CDAC_TYPE_BEGIN(RealCodeHeader) +CDAC_TYPE_INDETERMINATE(RealCodeHeader) +CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, MethodDesc, offsetof(RealCodeHeader, phdrMDesc)) +CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, GCInfo, offsetof(RealCodeHeader, phdrJitGCInfo)) +#ifdef FEATURE_EH_FUNCLETS +CDAC_TYPE_FIELD(RealCodeHeader, /*uint32*/, NumUnwindInfos, offsetof(RealCodeHeader, nUnwindInfos)) +CDAC_TYPE_FIELD(RealCodeHeader, /* T_RUNTIME_FUNCTION */, UnwindInfos, offsetof(RealCodeHeader, unwindInfos)) +#endif // FEATURE_EH_FUNCLETS +CDAC_TYPE_END(RealCodeHeader) + +CDAC_TYPE_BEGIN(CodeHeapListNode) +CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, Next, offsetof(HeapList, hpNext)) +CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, StartAddress, offsetof(HeapList, startAddress)) +CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, EndAddress, offsetof(HeapList, endAddress)) +CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, MapBase, offsetof(HeapList, mapBase)) +CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, HeaderMap, offsetof(HeapList, pHdrMap)) +CDAC_TYPE_END(CodeHeapListNode) + +CDAC_TYPE_BEGIN(ILCodeVersioningState) +CDAC_TYPE_INDETERMINATE(ILCodeVersioningState) +CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, FirstVersionNode, cdac_data::FirstVersionNode) +CDAC_TYPE_FIELD(ILCodeVersioningState, /*uint32*/, ActiveVersionKind, cdac_data::ActiveVersionKind) +CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, ActiveVersionNode, cdac_data::ActiveVersionNode) +CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, ActiveVersionModule, cdac_data::ActiveVersionModule) +CDAC_TYPE_FIELD(ILCodeVersioningState, /*uint32*/, ActiveVersionMethodDef, cdac_data::ActiveVersionMethodDef) +CDAC_TYPE_END(ILCodeVersioningState) + +CDAC_TYPE_BEGIN(NativeCodeVersionNode) +CDAC_TYPE_INDETERMINATE(NativeCodeVersionNode) +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, Next, cdac_data::Next) +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, MethodDesc, cdac_data::MethodDesc) +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, NativeCode, cdac_data::NativeCode) +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*uint32*/, Flags, cdac_data::Flags) +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*nuint*/, ILVersionId, cdac_data::ILVersionId) +#ifdef HAVE_GCCOVER +CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, GCCoverageInfo, cdac_data::GCCoverageInfo) +#endif // HAVE_GCCOVER +CDAC_TYPE_END(NativeCodeVersionNode) + +CDAC_TYPE_BEGIN(ILCodeVersionNode) +CDAC_TYPE_INDETERMINATE(ILCodeVersionNode) +CDAC_TYPE_FIELD(ILCodeVersionNode, /*nuint*/, VersionId, cdac_data::VersionId) +CDAC_TYPE_FIELD(ILCodeVersionNode, /*pointer*/, Next, cdac_data::Next) +CDAC_TYPE_FIELD(ILCodeVersionNode, /*uint32*/, RejitState, cdac_data::RejitState) +CDAC_TYPE_END(ILCodeVersionNode) + +CDAC_TYPE_BEGIN(ProfControlBlock) +CDAC_TYPE_FIELD(ProfControlBlock, /*uint64*/, GlobalEventMask, offsetof(ProfControlBlock, globalEventMask)) +CDAC_TYPE_END(ProfControlBlock) + +#ifdef HAVE_GCCOVER +CDAC_TYPE_BEGIN(GCCoverageInfo) +CDAC_TYPE_INDETERMINATE(GCCoverageInfo) +CDAC_TYPE_FIELD(GCCoverageInfo, /*pointer*/, SavedCode, offsetof(GCCoverageInfo, savedCode)) +CDAC_TYPE_END(GCCoverageInfo) +#endif // HAVE_GCCOVER + +CDAC_TYPE_BEGIN(Frame) +CDAC_TYPE_INDETERMINATE(Frame) +CDAC_TYPE_FIELD(Frame, /*pointer*/, Next, cdac_data::Next) +CDAC_TYPE_END(Frame) + +CDAC_TYPE_BEGIN(InlinedCallFrame) +CDAC_TYPE_SIZE(sizeof(InlinedCallFrame)) +CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CallSiteSP, offsetof(InlinedCallFrame, m_pCallSiteSP)) +CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CallerReturnAddress, offsetof(InlinedCallFrame, m_pCallerReturnAddress)) +CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CalleeSavedFP, offsetof(InlinedCallFrame, m_pCalleeSavedFP)) +#ifdef TARGET_ARM +CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, SPAfterProlog, offsetof(InlinedCallFrame, m_pSPAfterProlog)) +#endif // TARGET_ARM +CDAC_TYPE_END(InlinedCallFrame) + +CDAC_TYPE_BEGIN(SoftwareExceptionFrame) +CDAC_TYPE_SIZE(sizeof(SoftwareExceptionFrame)) +CDAC_TYPE_FIELD(SoftwareExceptionFrame, /*T_CONTEXT*/, TargetContext, cdac_data::TargetContext) +CDAC_TYPE_FIELD(SoftwareExceptionFrame, /*pointer*/, ReturnAddress, cdac_data::ReturnAddress) +CDAC_TYPE_END(SoftwareExceptionFrame) + +CDAC_TYPE_BEGIN(FramedMethodFrame) +CDAC_TYPE_SIZE(sizeof(FramedMethodFrame)) +CDAC_TYPE_FIELD(FramedMethodFrame, /*pointer*/, TransitionBlockPtr, cdac_data::TransitionBlockPtr) +CDAC_TYPE_END(FramedMethodFrame) + +CDAC_TYPE_BEGIN(TransitionBlock) +CDAC_TYPE_SIZE(sizeof(TransitionBlock)) +CDAC_TYPE_FIELD(TransitionBlock, /*pointer*/, ReturnAddress, offsetof(TransitionBlock, m_ReturnAddress)) +CDAC_TYPE_FIELD(TransitionBlock, /*CalleeSavedRegisters*/, CalleeSavedRegisters, offsetof(TransitionBlock, m_calleeSavedRegisters)) +#ifdef TARGET_ARM +CDAC_TYPE_FIELD(TransitionBlock, /*ArgumentRegisters*/, ArgumentRegisters, offsetof(TransitionBlock, m_argumentRegisters)) +#endif // TARGET_ARM +CDAC_TYPE_END(TransitionBlock) + +#ifdef DEBUGGING_SUPPORTED +CDAC_TYPE_BEGIN(FuncEvalFrame) +CDAC_TYPE_SIZE(sizeof(FuncEvalFrame)) +CDAC_TYPE_FIELD(FuncEvalFrame, /*pointer*/, DebuggerEvalPtr, cdac_data::DebuggerEvalPtr) +CDAC_TYPE_END(FuncEvalFrame) + +CDAC_TYPE_BEGIN(DebuggerEval) +CDAC_TYPE_SIZE(sizeof(DebuggerEval)) +CDAC_TYPE_FIELD(DebuggerEval, /*T_CONTEXT*/, TargetContext, offsetof(DebuggerEval, m_context)) +CDAC_TYPE_FIELD(DebuggerEval, /*bool*/, EvalDuringException, offsetof(DebuggerEval, m_evalDuringException)) +CDAC_TYPE_END(DebuggerEval) +#endif // DEBUGGING_SUPPORTED + +#ifdef FEATURE_HIJACK +CDAC_TYPE_BEGIN(ResumableFrame) +CDAC_TYPE_SIZE(sizeof(ResumableFrame)) +CDAC_TYPE_FIELD(ResumableFrame, /*pointer*/, TargetContextPtr, cdac_data::TargetContextPtr) +CDAC_TYPE_END(ResumableFrame) + +CDAC_TYPE_BEGIN(HijackFrame) +CDAC_TYPE_SIZE(sizeof(HijackFrame)) +CDAC_TYPE_FIELD(HijackFrame, /*pointer*/, ReturnAddress, cdac_data::ReturnAddress) +CDAC_TYPE_FIELD(HijackFrame, /*pointer*/, HijackArgsPtr, cdac_data::HijackArgsPtr) +CDAC_TYPE_END(HijackFrame) + +// HijackArgs struct is different on each platform +CDAC_TYPE_BEGIN(HijackArgs) +CDAC_TYPE_SIZE(sizeof(HijackArgs)) +#if defined(TARGET_AMD64) + +CDAC_TYPE_FIELD(HijackArgs, /*CalleeSavedRegisters*/, CalleeSavedRegisters, offsetof(HijackArgs, Regs)) +#ifdef TARGET_WINDOWS +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Rsp, offsetof(HijackArgs, Rsp)) +#endif // TARGET_WINDOWS + +#elif defined(TARGET_ARM64) + +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X0, offsetof(HijackArgs, X0)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X1, offsetof(HijackArgs, X1)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X19, offsetof(HijackArgs, X19)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X20, offsetof(HijackArgs, X20)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X21, offsetof(HijackArgs, X21)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X22, offsetof(HijackArgs, X22)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X23, offsetof(HijackArgs, X23)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X24, offsetof(HijackArgs, X24)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X25, offsetof(HijackArgs, X25)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X26, offsetof(HijackArgs, X26)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X27, offsetof(HijackArgs, X27)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X28, offsetof(HijackArgs, X28)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Fp, offsetof(HijackArgs, X29)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Lr, offsetof(HijackArgs, Lr)) + +#elif defined(TARGET_X86) + +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Edi, offsetof(HijackArgs, Edi)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Esi, offsetof(HijackArgs, Esi)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Ebx, offsetof(HijackArgs, Ebx)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Edx, offsetof(HijackArgs, Edx)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Ecx, offsetof(HijackArgs, Ecx)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Eax, offsetof(HijackArgs, Eax)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Ebp, offsetof(HijackArgs, Ebp)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Eip, offsetof(HijackArgs, Eip)) + +#elif defined(TARGET_ARM) + +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R0, offsetof(HijackArgs, R0)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R2, offsetof(HijackArgs, R2)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R4, offsetof(HijackArgs, R4)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R5, offsetof(HijackArgs, R5)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R6, offsetof(HijackArgs, R6)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R7, offsetof(HijackArgs, R7)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R8, offsetof(HijackArgs, R8)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R9, offsetof(HijackArgs, R9)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R10, offsetof(HijackArgs, R10)) +CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R11, offsetof(HijackArgs, R11)) + +#endif // Platform switch +CDAC_TYPE_END(HijackArgs) +#endif // FEATURE_HIJACK + +CDAC_TYPE_BEGIN(FaultingExceptionFrame) +CDAC_TYPE_SIZE(sizeof(FaultingExceptionFrame)) +#ifdef FEATURE_EH_FUNCLETS +CDAC_TYPE_FIELD(FaultingExceptionFrame, /*T_CONTEXT*/, TargetContext, cdac_data::TargetContext) +#endif // FEATURE_EH_FUNCLETS +CDAC_TYPE_END(FaultingExceptionFrame) + +#if defined(TARGET_X86) && !defined(UNIX_X86_ABI) +CDAC_TYPE_BEGIN(TailCallFrame) +CDAC_TYPE_SIZE(sizeof(TailCallFrame)) +CDAC_TYPE_FIELD(TailCallFrame, /*CalleeSavedRegisters*/, CalleeSavedRegisters, cdac_data::CalleeSavedRegisters) +CDAC_TYPE_FIELD(TailCallFrame, /*pointer*/, ReturnAddress, cdac_data::ReturnAddress) +CDAC_TYPE_END(TailCallFrame) +#endif // TARGET_X86 && !UNIX_X86_ABI + +// ArgumentRegisters struct is different on each platform +CDAC_TYPE_BEGIN(ArgumentRegisters) +CDAC_TYPE_SIZE(sizeof(ArgumentRegisters)) +#if defined(TARGET_ARM) + +CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R0, offsetof(ArgumentRegisters, r[0])) +CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R1, offsetof(ArgumentRegisters, r[1])) +CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R2, offsetof(ArgumentRegisters, r[2])) +CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R3, offsetof(ArgumentRegisters, r[3])) + +#endif // TARGET_ARM +CDAC_TYPE_END(ArgumentRegisters) + +// CalleeSavedRegisters struct is different on each platform +CDAC_TYPE_BEGIN(CalleeSavedRegisters) +CDAC_TYPE_SIZE(sizeof(CalleeSavedRegisters)) +#if defined(TARGET_AMD64) || defined(TARGET_X86) + +#define CALLEE_SAVED_REGISTER(regname) \ + CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, regname, offsetof(CalleeSavedRegisters, regname)) +ENUM_CALLEE_SAVED_REGISTERS() +#undef CALLEE_SAVED_REGISTER + +#elif defined(TARGET_ARM) + +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R4, offsetof(CalleeSavedRegisters, r4)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R5, offsetof(CalleeSavedRegisters, r5)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R6, offsetof(CalleeSavedRegisters, r6)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R7, offsetof(CalleeSavedRegisters, r7)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R8, offsetof(CalleeSavedRegisters, r8)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R9, offsetof(CalleeSavedRegisters, r9)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R10, offsetof(CalleeSavedRegisters, r10)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R11, offsetof(CalleeSavedRegisters, r11)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, Lr, offsetof(CalleeSavedRegisters, r14)) + +#elif defined(TARGET_ARM64) + +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X19, offsetof(CalleeSavedRegisters, x19)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X20, offsetof(CalleeSavedRegisters, x20)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X21, offsetof(CalleeSavedRegisters, x21)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X22, offsetof(CalleeSavedRegisters, x22)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X23, offsetof(CalleeSavedRegisters, x23)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X24, offsetof(CalleeSavedRegisters, x24)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X25, offsetof(CalleeSavedRegisters, x25)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X26, offsetof(CalleeSavedRegisters, x26)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X27, offsetof(CalleeSavedRegisters, x27)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X28, offsetof(CalleeSavedRegisters, x28)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, Fp, offsetof(CalleeSavedRegisters, x29)) +CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, Lr, offsetof(CalleeSavedRegisters, x30)) + +#endif // Platform switch +CDAC_TYPE_END(CalleeSavedRegisters) + +CDAC_TYPES_END() + +CDAC_GLOBALS_BEGIN() + +#if defined(TARGET_UNIX) +CDAC_GLOBAL_STRING(OperatingSystem, unix) +#elif defined(TARGET_WINDOWS) +CDAC_GLOBAL_STRING(OperatingSystem, windows) +#else +#error TARGET_{OS} define is not recognized by the cDAC. Update this switch and the enum values in IRuntimeInfo.cs +#endif + +#if defined(TARGET_X86) +CDAC_GLOBAL_STRING(Architecture, x86) +#elif defined(TARGET_AMD64) +CDAC_GLOBAL_STRING(Architecture, x64) +#elif defined(TARGET_ARM) +CDAC_GLOBAL_STRING(Architecture, arm) +#elif defined(TARGET_ARM64) +CDAC_GLOBAL_STRING(Architecture, arm64) +#elif defined(TARGET_LOONGARCH64) +CDAC_GLOBAL_STRING(Architecture, loongarch64) +#elif defined(TARGET_RISCV64) +CDAC_GLOBAL_STRING(Architecture, riscv64) +#else +#error TARGET_{ARCH} define is not recognized by the cDAC. Update this switch and the enum values in IRuntimeInfo.cs +#endif + +CDAC_GLOBAL_STRING(RID, RID_STRING) + +CDAC_GLOBAL(GCInfoVersion, uint32, GCINFO_VERSION) + +CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain) +CDAC_GLOBAL_POINTER(SystemDomain, cdac_data::SystemDomainPtr) +CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) +CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread) +CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread) + +// Add FrameIdentifier for all defined Frame types. Used to differentiate Frame objects. +#define FRAME_TYPE_NAME(frameType) \ + CDAC_GLOBAL(frameType##Identifier, nuint, (uint64_t)FrameIdentifier::frameType) + + #include "frames.h" +#undef FRAME_TYPE_NAME + +CDAC_GLOBAL(MethodDescTokenRemainderBitCount, uint8, METHOD_TOKEN_REMAINDER_BIT_COUNT) +#if FEATURE_COMINTEROP +CDAC_GLOBAL(FeatureCOMInterop, uint8, 1) +#else +CDAC_GLOBAL(FeatureCOMInterop, uint8, 0) +#endif +// See Object::GetGCSafeMethodTable +#ifdef TARGET_64BIT +CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1 | 1 << 2) +#else +CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1) +#endif //TARGET_64BIT +CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION) +CDAC_GLOBAL(DirectorySeparator, uint8, (uint8_t)DIRECTORY_SEPARATOR_CHAR_A) +CDAC_GLOBAL(HashMapSlotsPerBucket, uint32, SLOTS_PER_BUCKET) +CDAC_GLOBAL(HashMapValueMask, uint64, VALUE_MASK) +CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT) +CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE) +CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data::SyncBlockValue) +CDAC_GLOBAL(StubCodeBlockLast, uint8, STUB_CODE_BLOCK_LAST) +CDAC_GLOBAL(DefaultADID, uint32, DefaultADID) +CDAC_GLOBAL(MaxClrNotificationArgs, uint32, MAX_CLR_NOTIFICATION_ARGS) +CDAC_GLOBAL_POINTER(ClrNotificationArguments, &::g_clrNotificationArguments) +CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data::ArrayBoundsZero) +CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass) +CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable) +CDAC_GLOBAL_POINTER(ObjectMethodTable, &::g_pObjectClass) +CDAC_GLOBAL_POINTER(ObjectArrayMethodTable, &::g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]) +CDAC_GLOBAL_POINTER(StringMethodTable, &::g_pStringClass) +CDAC_GLOBAL_POINTER(SyncTableEntries, &::g_pSyncTable) +CDAC_GLOBAL_POINTER(MiniMetaDataBuffAddress, &::g_MiniMetaDataBuffAddress) +CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize) +CDAC_GLOBAL_POINTER(DacNotificationFlags, &::g_dacNotificationFlags) +CDAC_GLOBAL_POINTER(OffsetOfCurrentThreadInfo, &::g_offsetOfCurrentThreadInfo) +#ifdef TARGET_WINDOWS +CDAC_GLOBAL_POINTER(TlsIndexBase, &::_tls_index) +#endif // TARGET_WINDOWS +#ifdef STRESS_LOG +CDAC_GLOBAL(StressLogEnabled, uint8, 1) +CDAC_GLOBAL_POINTER(StressLog, &g_pStressLog) +CDAC_GLOBAL(StressLogHasModuleTable, uint8, 1) +CDAC_GLOBAL_POINTER(StressLogModuleTable, &g_pStressLog->modules) +CDAC_GLOBAL(StressLogMaxModules, uint64, cdac_offsets::MAX_MODULES) +CDAC_GLOBAL(StressLogChunkSize, uint32, STRESSLOG_CHUNK_SIZE) +CDAC_GLOBAL(StressLogValidChunkSig, uint32, StressLogChunk::ValidChunkSig) +CDAC_GLOBAL(StressLogMaxMessageSize, uint64, (uint64_t)StressMsg::maxMsgSize) +#else +CDAC_GLOBAL(StressLogEnabled, uint8, 0) +#endif +CDAC_GLOBAL_POINTER(ExecutionManagerCodeRangeMapAddress, cdac_data::CodeRangeMapAddress) +CDAC_GLOBAL_POINTER(PlatformMetadata, &::g_cdacPlatformMetadata) +CDAC_GLOBAL_POINTER(ProfilerControlBlock, &::g_profControlBlock) +CDAC_GLOBALS_END() + +#undef CDAC_BASELINE +#undef CDAC_TYPES_BEGIN +#undef CDAC_TYPE_BEGIN +#undef CDAC_TYPE_INDETERMINATE +#undef CDAC_TYPE_SIZE +#undef CDAC_TYPE_FIELD +#undef CDAC_TYPE_END +#undef CDAC_TYPES_END +#undef CDAC_GLOBALS_BEGIN +#undef CDAC_GLOBAL +#undef CDAC_GLOBAL_POINTER +#undef CDAC_GLOBAL_STRING +#undef CDAC_GLOBALS_END diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs new file mode 100644 index 00000000000000..c2b9bbae1c6ea4 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.Generic +{ + // + // Helper class to store reusable empty IEnumerables. + // + internal static class Empty + { + // + // Returns a reusable empty IEnumerable (that does not secretly implement more advanced collection interfaces.) + // + public static IEnumerable Enumerable + { + get + { + return _enumerable; + } + } + + private sealed class EmptyEnumImpl : IEnumerable, IEnumerator + { + public IEnumerator GetEnumerator() + { + return this; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this; + } + + public T Current + { + get { throw new InvalidOperationException(); } + } + + object IEnumerator.Current + { + get { throw new InvalidOperationException(); } + } + + public bool MoveNext() + { + return false; + } + + public void Reset() + { + } + + public void Dispose() + { + } + } + + private static IEnumerable _enumerable = new EmptyEnumImpl(); + } +} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs new file mode 100644 index 00000000000000..fc068b9c31e009 --- /dev/null +++ b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs @@ -0,0 +1,196 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** +** Private version of List for internal System.Private.CoreLib use. This +** permits sharing more source between BCL and System.Private.CoreLib (as well as the +** fact that List is just a useful class in general.) +** +** This does not strive to implement the full api surface area +** (but any portion it does implement should match the real List's +** behavior.) +** +** This file is a subset of System.Collections\System\Collections\Generics\List.cs +** and should be kept in sync with that file. +** +===========================================================*/ + +using System; +using System.Diagnostics; + +namespace System.Collections.Generic +{ + // Implements a variable-size List that uses an array of objects to store the + // elements. A List has a capacity, which is the allocated length + // of the internal array. As elements are added to a List, the capacity + // of the List is automatically increased as required by reallocating the + // internal array. + // + // LowLevelList with no interface implementation minimizes both code and data size. + // Data size is smaller because there will be minimal virtual function table. + // Code size is smaller because only functions called will be in the binary. + [DebuggerDisplay("Count = {Count}")] + internal class LowLevelList + { + private const int _defaultCapacity = 4; + + protected T[] _items; + protected int _size; + protected int _version; + +#pragma warning disable CA1825 // avoid the extra generic instantiation for Array.Empty() + private static readonly T[] s_emptyArray = new T[0]; +#pragma warning restore CA1825 + + // Constructs a List. The list is initially empty and has a capacity + // of zero. Upon adding the first element to the list the capacity is + // increased to 4, and then increased in multiples of two as required. + public LowLevelList() + { + _items = s_emptyArray; + } + + // Constructs a List with a given initial capacity. The list is + // initially empty, but will have room for the given number of elements + // before any reallocations are required. + // + public LowLevelList(int capacity) + { + ArgumentOutOfRangeException.ThrowIfNegative(capacity); + + if (capacity == 0) + _items = s_emptyArray; + else + _items = new T[capacity]; + } + + // Constructs a List, copying the contents of the given collection. The + // size and capacity of the new list will both be equal to the size of the + // given collection. + // + public LowLevelList(IEnumerable collection) + { + ArgumentNullException.ThrowIfNull(collection); + + ICollection? c = collection as ICollection; + if (c != null) + { + int count = c.Count; + if (count == 0) + { + _items = s_emptyArray; + } + else + { + _items = new T[count]; + c.CopyTo(_items, 0); + _size = count; + } + } + else + { + _size = 0; + _items = s_emptyArray; + // This enumerable could be empty. Let Add allocate a new array, if needed. + // Note it will also go to _defaultCapacity first, not 1, then 2, etc. + + using (IEnumerator en = collection.GetEnumerator()) + { + while (en.MoveNext()) + { + Add(en.Current); + } + } + } + } + + // Gets and sets the capacity of this list. The capacity is the size of + // the internal array used to hold items. When set, the internal + // array of the list is reallocated to the given capacity. + // + public int Capacity + { + get + { + return _items.Length; + } + set + { + ArgumentOutOfRangeException.ThrowIfLessThan(value, _size); + + if (value != _items.Length) + { + if (value > 0) + { + T[] newItems = new T[value]; + Array.Copy(_items, 0, newItems, 0, _size); + _items = newItems; + } + else + { + _items = s_emptyArray; + } + } + } + } + + // Read-only property describing how many elements are in the List. + public int Count + { + get + { + return _size; + } + } + + // Sets or Gets the element at the given index. + // + public T this[int index] + { + get + { + // Following trick can reduce the range check by one + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)_size, nameof(index)); + return _items[index]; + } + + set + { + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)_size, nameof(index)); + _items[index] = value; + _version++; + } + } + + + // Adds the given object to the end of this list. The size of the list is + // increased by one. If required, the capacity of the list is doubled + // before adding the new element. + // + public void Add(T item) + { + if (_size == _items.Length) EnsureCapacity(_size + 1); + _items[_size++] = item; + _version++; + } + + // Ensures that the capacity of this list is at least the given minimum + // value. If the current capacity of the list is less than min, the + // capacity is increased to twice the current capacity or to min, + // whichever is larger. + private void EnsureCapacity(int min) + { + if (_items.Length < min) + { + int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2; + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + //if ((uint)newCapacity > Array.MaxLength) newCapacity = Array.MaxLength; + if (newCapacity < min) newCapacity = min; + Capacity = newCapacity; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 6dd8b1dbe9fa54..bfdd1b53058657 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -295,7 +295,6 @@ - Interop\Unix\System.Native\Interop.Abort.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs new file mode 100644 index 00000000000000..1854e6ddeb210d --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs @@ -0,0 +1,864 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Reflection.Runtime.TypeInfos; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +using Internal.Reflection.Augments; +using Internal.Reflection.Core.Execution; +using Internal.Runtime; + +using Debug = System.Diagnostics.Debug; + +namespace System +{ + internal sealed unsafe class RuntimeType : TypeInfo, ICloneable + { + private MethodTable* _pUnderlyingEEType; + private IntPtr _runtimeTypeInfoHandle; + + internal RuntimeType(MethodTable* pEEType) + { + _pUnderlyingEEType = pEEType; + } + + internal RuntimeType(RuntimeTypeInfo runtimeTypeInfo) + { + // This needs to be a strong handle to prevent the type from being collected and re-created that would end up leaking the handle. + _runtimeTypeInfoHandle = RuntimeImports.RhHandleAlloc(runtimeTypeInfo, GCHandleType.Normal); + } + + internal void DangerousSetUnderlyingEEType(MethodTable* pEEType) + { + Debug.Assert(_pUnderlyingEEType == null); + _pUnderlyingEEType = pEEType; + } + + internal void Free() + { + RuntimeImports.RhHandleFree(_runtimeTypeInfoHandle); + } + + internal MethodTable* ToMethodTableMayBeNull() => _pUnderlyingEEType; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal RuntimeTypeInfo GetRuntimeTypeInfo() + { + IntPtr handle = _runtimeTypeInfoHandle; + if (handle != default) + { + object? runtimeTypeInfo = RuntimeImports.RhHandleGet(handle); + if (runtimeTypeInfo != null) + { + return Unsafe.As(runtimeTypeInfo); + } + } + return InitializeRuntimeTypeInfoHandle(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private RuntimeTypeInfo InitializeRuntimeTypeInfoHandle() + { + RuntimeTypeInfo runtimeTypeInfo = ExecutionDomain.GetRuntimeTypeInfo(_pUnderlyingEEType); + + // We assume that the RuntimeTypeInfo unifiers pick a winner when multiple threads + // race to create RuntimeTypeInfo. + + IntPtr handle = _runtimeTypeInfoHandle; + if (handle == default) + { + IntPtr tempHandle = RuntimeImports.RhHandleAlloc(runtimeTypeInfo, GCHandleType.Weak); + if (Interlocked.CompareExchange(ref _runtimeTypeInfoHandle, tempHandle, default) != default) + RuntimeImports.RhHandleFree(tempHandle); + } + else + { + RuntimeImports.RhHandleSet(handle, runtimeTypeInfo); + } + + return runtimeTypeInfo; + } + + public override string? GetEnumName(object value) + { + ArgumentNullException.ThrowIfNull(value); + + ulong rawValue; + if (!Enum.TryGetUnboxedValueOfEnumOrInteger(value, out rawValue)) + throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value)); + + // For desktop compatibility, do not bounce an incoming integer that's the wrong size. + // Do a value-preserving cast of both it and the enum values and do a 64-bit compare. + + if (!IsActualEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + return Enum.GetName(this, rawValue); + } + + public override string[] GetEnumNames() + { + if (!IsActualEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + string[] ret = Enum.GetNamesNoCopy(this); + + // Make a copy since we can't hand out the same array since users can modify them + return new ReadOnlySpan(ret).ToArray(); + } + + public override Type GetEnumUnderlyingType() + { + if (!IsActualEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + return Enum.InternalGetUnderlyingType(this); + } + + public override bool IsEnumDefined(object value) + { + ArgumentNullException.ThrowIfNull(value); + + if (!IsActualEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + if (value is string valueAsString) + { + EnumInfo enumInfo = Enum.GetEnumInfo(this); + foreach (string name in enumInfo.Names) + { + if (valueAsString == name) + return true; + } + return false; + } + else + { + ulong rawValue; + if (!Enum.TryGetUnboxedValueOfEnumOrInteger(value, out rawValue)) + { + if (Type.IsIntegerType(value.GetType())) + throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), Enum.InternalGetUnderlyingType(this))); + else + throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); + } + + if (value is Enum) + { + if (value.GetMethodTable() != this.ToMethodTableMayBeNull()) + throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, value.GetType(), this)); + } + else + { + Type underlyingType = Enum.InternalGetUnderlyingType(this); + if (!(underlyingType.TypeHandle.ToMethodTable() == value.GetMethodTable())) + throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), underlyingType)); + } + + return Enum.GetName(this, rawValue) != null; + } + } + + [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues overload instead.")] + public override Array GetEnumValues() + { + if (!IsActualEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + Array values = Enum.GetValuesAsUnderlyingTypeNoCopy(this); + int count = values.Length; + + // Without universal shared generics, chances are slim that we'll have the appropriate + // array type available. Offer an escape hatch that avoids a missing metadata exception + // at the cost of a small appcompat risk. + Array result = AppContext.TryGetSwitch("Switch.System.Enum.RelaxedGetValues", out bool isRelaxed) && isRelaxed ? + Array.CreateInstance(Enum.InternalGetUnderlyingType(this), count) : + Array.CreateInstance(this, count); + + Array.Copy(values, result, values.Length); + return result; + } + + public override Array GetEnumValuesAsUnderlyingType() + { + if (!IsActualEnum) + throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); + + return Enum.GetValuesAsUnderlyingType(this); + } + + public override int GetHashCode() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return ((nuint)pEEType).GetHashCode(); + return RuntimeHelpers.GetHashCode(this); + } + + public override RuntimeTypeHandle TypeHandle + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return new RuntimeTypeHandle(pEEType); + return GetRuntimeTypeInfo().TypeHandle; + } + } + + internal new unsafe bool IsInterface + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsInterface; + return GetRuntimeTypeInfo().IsInterface; + } + } + + protected override bool IsValueTypeImpl() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsValueType; + return GetRuntimeTypeInfo().IsValueType; + } + + internal bool IsActualValueType + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsValueType; + return GetRuntimeTypeInfo().IsActualValueType; + } + } + + public override unsafe bool IsEnum + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsEnum; + return GetRuntimeTypeInfo().IsEnum; + } + } + + internal unsafe bool IsActualEnum + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsEnum; + return GetRuntimeTypeInfo().IsActualEnum; + } + } + + protected override unsafe bool IsArrayImpl() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsArray; + return GetRuntimeTypeInfo().IsArray; + } + + protected override unsafe bool IsByRefImpl() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsByRef; + return GetRuntimeTypeInfo().IsByRef; + } + + protected override unsafe bool IsPointerImpl() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsPointer; + return GetRuntimeTypeInfo().IsPointer; + } + + protected override unsafe bool HasElementTypeImpl() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsParameterizedType; + return GetRuntimeTypeInfo().HasElementType; + } + + public override Type? GetElementType() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsParameterizedType ? GetTypeFromMethodTable(pEEType->RelatedParameterType) : null; + return GetRuntimeTypeInfo().GetElementType(); + } + + public override int GetArrayRank() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsArray ? pEEType->ArrayRank : throw new ArgumentException(SR.Argument_HasToBeArrayClass); + return GetRuntimeTypeInfo().GetArrayRank(); + } + + public override Type? BaseType + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + { + if (pEEType->IsCanonical) + { + MethodTable* pBaseType = pEEType->NonArrayBaseType; + return (pBaseType != null) ? GetTypeFromMethodTable(pBaseType) : null; + } + + if (pEEType->IsArray) + { + return typeof(Array); + } + + if (!pEEType->IsGenericTypeDefinition) + { + return null; + } + } + + return GetRuntimeTypeInfo().BaseType; + } + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null && !pEEType->IsGenericTypeDefinition) + { + int count = pEEType->NumInterfaces; + if (count == 0) + return EmptyTypes; + + Type[] result = new Type[count]; + for (int i = 0; i < result.Length; i++) + { + result[i] = GetTypeFromMethodTable(pEEType->InterfaceMap[i]); + } + return result; + } + + return GetRuntimeTypeInfo().GetInterfaces(); + } + + public override bool IsTypeDefinition + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return (pEEType->IsCanonical && !pEEType->IsGeneric) || pEEType->IsGenericTypeDefinition; + return GetRuntimeTypeInfo().IsTypeDefinition; + } + } + + public override bool IsGenericType + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsGeneric || pEEType->IsGenericTypeDefinition; + return GetRuntimeTypeInfo().IsGenericType; + } + } + + public override bool IsGenericTypeDefinition + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsGenericTypeDefinition; + return GetRuntimeTypeInfo().IsGenericTypeDefinition; + } + } + + public override bool IsConstructedGenericType + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsGeneric; + return GetRuntimeTypeInfo().IsConstructedGenericType; + } + } + + public override Type GetGenericTypeDefinition() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + { + return pEEType->IsGeneric ? GetTypeFromMethodTable(pEEType->GenericDefinition) : + pEEType->IsGenericTypeDefinition ? this : + throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); + } + return GetRuntimeTypeInfo().GetGenericTypeDefinition(); + } + + public override Type[] GenericTypeArguments + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + { + if (!pEEType->IsGeneric) + return EmptyTypes; + + MethodTableList genericArguments = pEEType->GenericArguments; + + Type[] result = new Type[pEEType->GenericArity]; + for (int i = 0; i < result.Length; i++) + { + result[i] = GetTypeFromMethodTable(genericArguments[i]); + } + return result; + } + + return GetRuntimeTypeInfo().GenericTypeArguments; + } + } + + public override Type[] GetGenericArguments() + { + if (IsConstructedGenericType) + return GenericTypeArguments; + if (IsGenericTypeDefinition) + return GenericTypeParameters; + return EmptyTypes; + } + + public override bool IsGenericParameter + { + get + { + if (_pUnderlyingEEType != null) + return false; + return GetRuntimeTypeInfo().IsGenericParameter; + } + } + + public override bool IsGenericTypeParameter + { + get + { + if (_pUnderlyingEEType != null) + return false; + return GetRuntimeTypeInfo().IsGenericTypeParameter; + } + } + + public override bool IsGenericMethodParameter + { + get + { + if (_pUnderlyingEEType != null) + return false; + return GetRuntimeTypeInfo().IsGenericMethodParameter; + } + } + + public override bool ContainsGenericParameters + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsGenericTypeDefinition; + return GetRuntimeTypeInfo().ContainsGenericParameters; + } + } + + protected override bool IsPrimitiveImpl() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsActualPrimitive; + return GetRuntimeTypeInfo().IsPrimitive; + } + + public override bool IsSZArray + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->ElementType == EETypeElementType.SzArray; + return GetRuntimeTypeInfo().IsSZArray; + } + } + + public override bool IsVariableBoundArray + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->ElementType == EETypeElementType.Array; + return GetRuntimeTypeInfo().IsVariableBoundArray; + } + } + + public override bool IsByRefLike + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsByRefLike; + return GetRuntimeTypeInfo().IsByRefLike; + } + } + + public override bool IsFunctionPointer + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsFunctionPointer; + return GetRuntimeTypeInfo().IsFunctionPointer; + } + } + + public override bool IsUnmanagedFunctionPointer + { + get + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + return pEEType->IsFunctionPointer && pEEType->IsUnmanagedFunctionPointer; + return GetRuntimeTypeInfo().IsUnmanagedFunctionPointer; + } + } + + public override Type[] GetFunctionPointerParameterTypes() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + { + if (!pEEType->IsFunctionPointer) + throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); + + uint count = pEEType->NumFunctionPointerParameters; + if (count == 0) + return EmptyTypes; + + MethodTableList parameterTypes = pEEType->FunctionPointerParameters; + + Type[] result = new Type[count]; + for (int i = 0; i < result.Length; i++) + { + result[i] = GetTypeFromMethodTable(parameterTypes[i]); + } + return result; + } + + return GetRuntimeTypeInfo().GetFunctionPointerParameterTypes(); + } + + public override Type GetFunctionPointerReturnType() + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType != null) + { + if (!pEEType->IsFunctionPointer) + throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); + + return GetTypeFromMethodTable(pEEType->FunctionPointerReturnType); + } + + return GetRuntimeTypeInfo().GetFunctionPointerReturnType(); + } + + public override bool IsAssignableFrom([NotNullWhen(true)] Type? c) + { + if (c == null) + return false; + + if (object.ReferenceEquals(c, this)) + return true; + + if (c.UnderlyingSystemType is not RuntimeType fromRuntimeType) + return false; // Desktop compat: If typeInfo is null, or implemented by a different Reflection implementation, return "false." + + if (fromRuntimeType._pUnderlyingEEType != null && _pUnderlyingEEType != null) + { + // If both types have type handles, let MRT handle this. It's not dependent on metadata. + if (RuntimeImports.AreTypesAssignable(fromRuntimeType._pUnderlyingEEType, _pUnderlyingEEType)) + return true; + + // Runtime IsAssignableFrom does not handle casts from generic type definitions: always returns false. For those, we fall through to the + // managed implementation. For everyone else, return "false". + // + // Runtime IsAssignableFrom does not handle pointer -> UIntPtr cast. + if (!fromRuntimeType._pUnderlyingEEType->IsGenericTypeDefinition || fromRuntimeType._pUnderlyingEEType->IsPointer) + return false; + } + + // If we got here, the types are open, or reduced away, or otherwise lacking in type handles. Perform the IsAssignability check using metadata. + return GetRuntimeTypeInfo().IsAssignableFrom(fromRuntimeType); + } + + public override bool IsInstanceOfType([NotNullWhen(true)] object? o) + { + MethodTable* pEEType = _pUnderlyingEEType; + if (pEEType == null || pEEType->IsGenericTypeDefinition) + return false; + if (pEEType->IsNullable) + pEEType = pEEType->NullableType; + return TypeCast.IsInstanceOfAny(pEEType, o) != null; + } + + // + // Methods that do not directly depend on _pUnderlyingEEType + // + + public override string ToString() + { + return GetRuntimeTypeInfo().ToString(); + } + + public override bool Equals(object? obj) => ReferenceEquals(obj, this); + + object ICloneable.Clone() => this; + + public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) + => typeInfo != null && IsAssignableFrom(typeInfo.AsType()); + + public override bool IsSecurityCritical => true; + public override bool IsSecuritySafeCritical => false; + public override bool IsSecurityTransparent => false; + + public override Type UnderlyingSystemType => this; + + public override bool IsCollectible => false; + + public override MemberTypes MemberType => GetRuntimeTypeInfo().MemberType; + + public override int MetadataToken => GetRuntimeTypeInfo().MetadataToken; + + public override Type? DeclaringType => GetRuntimeTypeInfo().DeclaringType; + public override Type? ReflectedType => DeclaringType; + + public override MethodBase? DeclaringMethod => GetRuntimeTypeInfo().DeclaringMethod; + + public override StructLayoutAttribute StructLayoutAttribute => GetRuntimeTypeInfo().StructLayoutAttribute; + + protected override bool IsCOMObjectImpl() => false; + + protected override TypeCode GetTypeCodeImpl() => ReflectionAugments.GetRuntimeTypeCode(this); + + protected override TypeAttributes GetAttributeFlagsImpl() => GetRuntimeTypeInfo().Attributes; + + public override Type[] GenericTypeParameters + => GetRuntimeTypeInfo().GenericTypeParameters; + + public override int GenericParameterPosition + => GetRuntimeTypeInfo().GenericParameterPosition; + public override GenericParameterAttributes GenericParameterAttributes + => GetRuntimeTypeInfo().GenericParameterAttributes; + public override Type[] GetGenericParameterConstraints() + => GetRuntimeTypeInfo().GetGenericParameterConstraints(); + + public override Type[] GetFunctionPointerCallingConventions() + { + if (!IsFunctionPointer) + throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); + + // Requires a modified type to return the modifiers. + return EmptyTypes; + } + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) + => GetRuntimeTypeInfo().GetConstructorImpl(bindingAttr, binder, callConvention, types, modifiers); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetConstructors(bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetEvent(name, bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents(BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetEvents(bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo? GetField(string name, BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetField(name, bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetFields(bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) + => GetRuntimeTypeInfo().GetMethodImpl(name, RuntimeTypeInfo.GenericParameterCountAny, bindingAttr, binder, callConvention, types, modifiers); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo? GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) + => GetRuntimeTypeInfo().GetMethodImpl(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetMethods(bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type? GetNestedType(string name, BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetNestedType(name, bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type[] GetNestedTypes(BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetNestedTypes(bindingAttr); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) + => GetRuntimeTypeInfo().GetPropertyImpl(name, bindingAttr, binder, returnType, types, modifiers); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetProperties(bindingAttr); + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMember(string name, BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetMember(name, bindingAttr); + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetMember(name, type, bindingAttr); + + [DynamicallyAccessedMembers(GetAllMembers)] + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + => GetRuntimeTypeInfo().GetMembers(bindingAttr); + + public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) + => GetRuntimeTypeInfo().GetMemberWithSameMetadataDefinitionAs(member); + + [DynamicallyAccessedMembers(InvokeMemberMembers)] + public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) + => GetRuntimeTypeInfo().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] + public override Type? GetInterface(string name, bool ignoreCase) + => GetRuntimeTypeInfo().GetInterface(name, ignoreCase); + + public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) + => GetRuntimeTypeInfo().GetInterfaceMap(interfaceType); + + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.PublicMethods + | DynamicallyAccessedMemberTypes.PublicEvents + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.PublicNestedTypes)] + public override MemberInfo[] GetDefaultMembers() + => GetRuntimeTypeInfo().GetDefaultMembers(); + + public override bool IsDefined(Type attributeType, bool inherit) + => GetRuntimeTypeInfo().IsDefined(attributeType, inherit); + + public override object[] GetCustomAttributes(bool inherit) + { + return GetRuntimeTypeInfo().GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return GetRuntimeTypeInfo().GetCustomAttributes(attributeType, inherit); + } + + public override IEnumerable CustomAttributes + { + get + { + return GetRuntimeTypeInfo().CustomAttributes; + } + } + + public override IList GetCustomAttributesData() + { + return GetRuntimeTypeInfo().GetCustomAttributesData(); + } + + public override string Name + { + get + { + return GetRuntimeTypeInfo().Name; + } + } + + public override string? Namespace + { + get + { + return GetRuntimeTypeInfo().Namespace; + } + } + + public override string? AssemblyQualifiedName => GetRuntimeTypeInfo().AssemblyQualifiedName; + + public override string? FullName => GetRuntimeTypeInfo().FullName; + + public override Assembly Assembly + { + get + { + return GetRuntimeTypeInfo().Assembly; + } + } + + public override Module Module => GetRuntimeTypeInfo().Module; + + public override Guid GUID => GetRuntimeTypeInfo().GUID; + + public override bool HasSameMetadataDefinitionAs(MemberInfo other) => GetRuntimeTypeInfo().HasSameMetadataDefinitionAs(other); + + public override Type MakePointerType() + => GetRuntimeTypeInfo().MakePointerType(); + + public override Type MakeByRefType() + => GetRuntimeTypeInfo().MakeByRefType(); + + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] + public override Type MakeArrayType() + => GetRuntimeTypeInfo().MakeArrayType(); + + [RequiresDynamicCode("The code for an array of the specified type might not be available.")] + public override Type MakeArrayType(int rank) + => GetRuntimeTypeInfo().MakeArrayType(rank); + + [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] + [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] + public override Type MakeGenericType(params Type[] instantiation) + => GetRuntimeTypeInfo().MakeGenericType(instantiation); + } +} diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 11ef97bf2efcd3..810baeb4f44af8 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -889,13 +889,6 @@ WaitForSingleObject( IN HANDLE hHandle, IN DWORD dwMilliseconds); -PALIMPORT -DWORD -PALAPI -PAL_WaitForSingleObjectPrioritized( - IN HANDLE hHandle, - IN DWORD dwMilliseconds); - PALIMPORT DWORD PALAPI diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index 1a3aa8c4df18df..4fa283394639ae 100644 --- a/src/coreclr/pal/src/synchmgr/wait.cpp +++ b/src/coreclr/pal/src/synchmgr/wait.cpp @@ -83,36 +83,6 @@ WaitForSingleObject(IN HANDLE hHandle, return dwRet; } - -/*++ -Function: - WaitForSingleObjectPrioritized - -Similar to WaitForSingleObject, except uses a LIFO release policy for waiting threads by prioritizing new waiters (registering -them at the beginning of the wait queue rather than at the end). ---*/ -DWORD -PALAPI -PAL_WaitForSingleObjectPrioritized(IN HANDLE hHandle, - IN DWORD dwMilliseconds) -{ - DWORD dwRet; - - PERF_ENTRY(PAL_WaitForSingleObjectPrioritized); - ENTRY("PAL_WaitForSingleObjectPrioritized(hHandle=%p, dwMilliseconds=%u)\n", - hHandle, dwMilliseconds); - - CPalThread * pThread = InternalGetCurrentThread(); - - dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE, - dwMilliseconds, FALSE, TRUE /* bPrioritize */); - - LOGEXIT("PAL_WaitForSingleObjectPrioritized returns DWORD %u\n", dwRet); - PERF_EXIT(PAL_WaitForSingleObjectPrioritized); - return dwRet; -} - - /*++ Function: WaitForSingleObjectEx diff --git a/src/coreclr/vm/comwaithandle.cpp b/src/coreclr/vm/comwaithandle.cpp index e80675cc03febf..322cf20704e831 100644 --- a/src/coreclr/vm/comwaithandle.cpp +++ b/src/coreclr/vm/comwaithandle.cpp @@ -82,22 +82,3 @@ extern "C" INT32 QCALLTYPE WaitHandle_SignalAndWait(HANDLE waitHandleSignal, HAN END_QCALL; return retVal; } - -#ifdef TARGET_UNIX -extern "C" INT32 QCALLTYPE WaitHandle_WaitOnePrioritized(HANDLE handle, INT32 timeoutMs) -{ - QCALL_CONTRACT; - - DWORD result = WAIT_FAILED; - - BEGIN_QCALL; - - _ASSERTE(handle != NULL); - _ASSERTE(handle != INVALID_HANDLE_VALUE); - - result = PAL_WaitForSingleObjectPrioritized(handle, timeoutMs); - - END_QCALL; - return (INT32)result; -} -#endif // TARGET_UNIX diff --git a/src/coreclr/vm/comwaithandle.h b/src/coreclr/vm/comwaithandle.h index ac605389129138..7fb890e7554ce2 100644 --- a/src/coreclr/vm/comwaithandle.h +++ b/src/coreclr/vm/comwaithandle.h @@ -18,8 +18,4 @@ extern "C" INT32 QCALLTYPE WaitHandle_WaitOneCore(HANDLE handle, INT32 timeout, extern "C" INT32 QCALLTYPE WaitHandle_WaitMultipleIgnoringSyncContext(HANDLE *handleArray, INT32 numHandles, BOOL waitForAll, INT32 timeout); extern "C" INT32 QCALLTYPE WaitHandle_SignalAndWait(HANDLE waitHandleSignal, HANDLE waitHandleWait, INT32 timeout); -#ifdef TARGET_UNIX -extern "C" INT32 QCALLTYPE WaitHandle_WaitOnePrioritized(HANDLE handle, INT32 timeoutMs); -#endif // TARGET_UNIX - #endif // _COM_WAITABLE_HANDLE_H diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 77075ea20025ad..103a03a0c668b1 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -308,9 +308,6 @@ static const Entry s_QCall[] = DllImportEntry(WaitHandle_WaitOneCore) DllImportEntry(WaitHandle_WaitMultipleIgnoringSyncContext) DllImportEntry(WaitHandle_SignalAndWait) -#ifdef TARGET_UNIX - DllImportEntry(WaitHandle_WaitOnePrioritized) -#endif // TARGET_UNIX DllImportEntry(ClrConfig_GetConfigBoolValue) DllImportEntry(Buffer_Clear) DllImportEntry(Buffer_MemMove) diff --git a/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs b/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs new file mode 100644 index 00000000000000..8cc99bc095026b --- /dev/null +++ b/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Data.Common; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// We need to target netstandard2.0, so keep using ref for MemoryMarshal.Write +// CS9191: The 'ref' modifier for argument 2 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. +#pragma warning disable CS9191 + +namespace System.Data.OleDb +{ + /// + /// The ComWrappers implementation for System.Data.OleDb's COM interop usages. + /// + /// Supports IErrorInfo COM interface. + /// + internal sealed unsafe class OleDbComWrappers : ComWrappers + { + private const int S_OK = (int)OleDbHResult.S_OK; + private static readonly Guid IID_IErrorInfo = new Guid(0x1CF2B120, 0x547D, 0x101B, 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19); + + internal static OleDbComWrappers Instance { get; } = new OleDbComWrappers(); + + private OleDbComWrappers() { } + + protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + throw new NotImplementedException(); + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + { + Debug.Assert(flags == CreateObjectFlags.UniqueInstance); + + Guid errorInfoIID = IID_IErrorInfo; +#pragma warning disable CS9191 // The 'ref' modifier for argument 1 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. + int hr = Marshal.QueryInterface(externalComObject, ref errorInfoIID, out IntPtr comObject); +#pragma warning restore CS9191 + if (hr == S_OK) + { + return new ErrorInfoWrapper(comObject); + } + + throw new NotImplementedException(); + } + + protected override void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + + // Doc and type layout: https://learn.microsoft.com/windows/win32/api/oaidl/nn-oaidl-ierrorinfo + private sealed class ErrorInfoWrapper : UnsafeNativeMethods.IErrorInfo, IDisposable + { + private readonly IntPtr _wrappedInstance; + + public ErrorInfoWrapper(IntPtr wrappedInstance) + { + _wrappedInstance = wrappedInstance; + } + + public void Dispose() + { + Marshal.Release(_wrappedInstance); + } + + [Obsolete("not used", true)] + void UnsafeNativeMethods.IErrorInfo.GetGUID(/*deleted parameter signature*/) + { + throw new NotImplementedException(); + } + + public unsafe System.Data.OleDb.OleDbHResult GetSource(out string? source) + { + IntPtr pSource = IntPtr.Zero; + int errorCode = ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 4 /* IErrorInfo.GetSource slot */))) + (_wrappedInstance, &pSource); + if (pSource == IntPtr.Zero || errorCode < 0) + { + source = null; + } + else + { + source = Marshal.PtrToStringBSTR(pSource); + } + + if (pSource != IntPtr.Zero) + { + Marshal.FreeBSTR(pSource); + } + + return (System.Data.OleDb.OleDbHResult)errorCode; + } + + public unsafe System.Data.OleDb.OleDbHResult GetDescription(out string? description) + { + IntPtr pDescription = IntPtr.Zero; + int errorCode = ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 5 /* IErrorInfo.GetDescription slot */))) + (_wrappedInstance, &pDescription); + if (pDescription == IntPtr.Zero || errorCode < 0) + { + description = null; + } + else + { + description = Marshal.PtrToStringBSTR(pDescription); + } + + if (pDescription != IntPtr.Zero) + { + Marshal.FreeBSTR(pDescription); + } + + return (System.Data.OleDb.OleDbHResult)errorCode; + } + } + + } +} diff --git a/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs b/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs new file mode 100644 index 00000000000000..13e6642ee1fc02 --- /dev/null +++ b/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; + +namespace System.Linq +{ + public static partial class Enumerable + { + private static IEnumerable SizeOptimizedSkipIterator(IEnumerable source, int count) + { + using IEnumerator e = source.GetEnumerator(); + while (count > 0 && e.MoveNext()) count--; + if (count <= 0) + { + while (e.MoveNext()) yield return e.Current; + } + } + } +} diff --git a/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs b/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs new file mode 100644 index 00000000000000..6f2bd0d9b0fa6b --- /dev/null +++ b/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Linq +{ + public static partial class Enumerable + { + private static IEnumerable SizeOptimizedTakeIterator(IEnumerable source, int count) + { + Debug.Assert(count > 0); + + foreach (TSource element in source) + { + yield return element; + if (--count == 0) break; + } + } + + private static IEnumerable SizeOptimizedTakeRangeIterator(IEnumerable source, int startIndex, int endIndex) + { + Debug.Assert(source is not null); + Debug.Assert(startIndex >= 0 && startIndex < endIndex); + + using IEnumerator e = source.GetEnumerator(); + + int index = 0; + while (index < startIndex && e.MoveNext()) + { + ++index; + } + + if (index < startIndex) + { + yield break; + } + + while (index < endIndex && e.MoveNext()) + { + yield return e.Current; + ++index; + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 3ed8313b45956a..e5981a2f0f1e3c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2806,6 +2806,7 @@ + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs similarity index 65% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs index a0873fc273ff32..41319b669c6e79 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs @@ -11,7 +11,9 @@ namespace System.Threading /// internal sealed partial class LowLevelLifoSemaphore : IDisposable { - private WaitSubsystem.WaitableObject _semaphore; + // Declared nullable even though it is initialized in Create + // as Roslyn doesn't see that it's set in Create and Create is called from all constructors. + private WaitSubsystem.WaitableObject? _semaphore; private void Create(int maximumSignalCount) { @@ -24,12 +26,12 @@ public void Dispose() private bool WaitCore(int timeoutMs) { - return WaitSubsystem.Wait(_semaphore, timeoutMs, false, true) == WaitHandle.WaitSuccess; + return WaitSubsystem.Wait(_semaphore!, timeoutMs, false, true) == WaitHandle.WaitSuccess; } private void ReleaseCore(int count) { - WaitSubsystem.ReleaseSemaphore(_semaphore, count); + WaitSubsystem.ReleaseSemaphore(_semaphore!, count); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs index 39233c87c15c96..0f789de54ee214 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.cs @@ -94,10 +94,6 @@ public bool Wait(int timeoutMs, bool spinWait) counts = countsBeforeUpdate; } -#if CORECLR && TARGET_UNIX - // The PAL's wait subsystem is slower, spin more to compensate for the more expensive wait - spinCount *= 2; -#endif bool isSingleProcessor = Environment.IsSingleProcessor; int spinIndex = isSingleProcessor ? SpinSleep0Threshold : 0; while (spinIndex < spinCount) diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs new file mode 100644 index 00000000000000..53d6f7f164275a --- /dev/null +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Reflection.Metadata +{ + public sealed class TypeNameParseOptions + { + private int _maxNodes = 20; + + /// + /// Limits the maximum value of node count that parser can handle. + /// + /// + /// + /// Setting this to a large value can render susceptible to Denial of Service + /// attacks when parsing or handling malicious input. + /// + /// The default value is 20. + /// + public int MaxNodes + { + get => _maxNodes; + set + { +#if NET8_0_OR_GREATER + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, 0, nameof(value)); +#else + if (value <= 0) + { + throw new ArgumentOutOfRangeException(paramName: nameof(value)); + } +#endif + + _maxNodes = value; + } + } + } +} diff --git a/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs b/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs new file mode 100644 index 00000000000000..ab5df917fdb5fd --- /dev/null +++ b/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Xunit; + +internal static class TheoryDataExtensions +{ + /// + /// Converts an IEnumerable into an Xunit theory compatible enumerable. + /// + public static TheoryData ToTheoryData(this IEnumerable data) + { + TheoryData theoryData = []; + foreach (var item in data) + { + theoryData.Add(item); + } + + return theoryData; + } + + /// + /// Converts an IEnumerable into an Xunit theory compatible enumerable. + /// + public static TheoryData ToTheoryData(this IEnumerable<(T1, T2)> data) + { + TheoryData theoryData = []; + foreach (var item in data) + { + theoryData.Add(item.Item1, item.Item2); + } + + return theoryData; + } + + /// + /// Converts an IEnumerable into an Xunit theory compatible enumerable. + /// + public static TheoryData ToTheoryData(this IEnumerable<(T1, T2, T3)> data) + { + TheoryData theoryData = []; + foreach (var item in data) + { + theoryData.Add(item.Item1, item.Item2, item.Item3); + } + + return theoryData; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs new file mode 100644 index 00000000000000..658606d84ee3cf --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("F4963CBF-10AF-460B-8495-107782187705")] + internal partial interface IArrayOfStatelessElements + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessType[] param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessType[] param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType[] param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType[] param, int size); + void MethodContentsIn([MarshalUsing(CountElementName = nameof(size))][In] StatelessType[] param, int size); + void MethodContentsOut([MarshalUsing(CountElementName = nameof(size))][Out] StatelessType[] param, int size); + void MethodContentsInOut([MarshalUsing(CountElementName = nameof(size))][In, Out] StatelessType[] param, int size); + } + + [GeneratedComClass] + internal partial class ArrayOfStatelessElements : IArrayOfStatelessElements + { + public void Method(StatelessType[] param, int size) + { + } + public void MethodContentsIn(StatelessType[] param, int size) + { + // We should be able to modify the contents and the caller shouldn't see it + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + public void MethodContentsInOut(StatelessType[] param, int size) + { + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + public void MethodContentsOut(StatelessType[] param, int size) + { + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = i }; + } + } + public void MethodIn(in StatelessType[] param, int size) + { + // We should be able to modify the contents and the caller shouldn't see it + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + public void MethodOut(out StatelessType[] param, int size) + { + param = new StatelessType[size]; + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = i }; + } + } + public void MethodRef(ref StatelessType[] param, int size) + { + for (int i = 0; i < size; i++) + { + param[i] = new StatelessType() { I = param[i].I * 2 }; + } + } + } + + [GeneratedComClass] + internal partial class ArrayOfStatelessElementsThrows : IArrayOfStatelessElements + { + public void Method(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodContentsIn(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodContentsInOut(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodContentsOut(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodIn(in StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodOut(out StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + public void MethodRef(ref StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs new file mode 100644 index 00000000000000..4e71832850fefc --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("5A9D3ED6-CC17-4FB9-8F82-0070489B7213")] + internal partial interface IBool + { + [return: MarshalAs(UnmanagedType.I1)] + bool Get(); + void Set([MarshalAs(UnmanagedType.I1)] bool value); + } + + [GeneratedComClass] + internal partial class IBoolImpl : IBool + { + bool _data; + public bool Get() => _data; + public void Set(bool value) => _data = value; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs new file mode 100644 index 00000000000000..7a1c97a7b37302 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [Guid(IID)] + [GeneratedComInterface(StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Utf16StringMarshaller))] + internal partial interface ICustomStringMarshallingUtf16 + { + public string GetString(); + + public void SetString(string value); + + public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs new file mode 100644 index 00000000000000..f6cd4ff8afc55b --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid(IID)] + internal partial interface IDerived : IGetAndSetInt + { + void SetName([MarshalUsing(typeof(Utf16StringMarshaller))] string name); + + [return: MarshalUsing(typeof(Utf16StringMarshaller))] + string GetName(); + + internal new const string IID = "7F0DB364-3C04-4487-9193-4BB05DC7B654"; + } + + [GeneratedComClass] + internal partial class Derived : GetAndSetInt, IDerived + { + string _data = "hello"; + public string GetName() => _data; + public void SetName(string name) => _data = name; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs new file mode 100644 index 00000000000000..86af99aa4ff726 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid(IID)] + internal partial interface IEmpty + { + public const string IID = "95D19F50-F2D8-4E61-884B-0A9162EA4646"; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs new file mode 100644 index 00000000000000..db5249a950bcab --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("00000100-0000-0000-C000-000000000046")] + internal partial interface IEnumUnknown + { + void Next(uint celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface)] object[] rgelt, out uint pceltFetched); + void Skip(uint celt); + void Reset(); + void Clone(out IEnumUnknown ppenum); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs new file mode 100644 index 00000000000000..f63cbbb91ab1de --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-2D8F-48A8-B6FB-B44B5F1B9FB6")] + internal partial interface IFloat + { + float Get(); + void Set(float value); + } + + [GeneratedComClass] + internal partial class IFloatImpl : IFloat + { + float _data; + public float Get() => _data; + public void Set(float value) => _data = value; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs new file mode 100644 index 00000000000000..3f1b18f34e9beb --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid(IID)] + internal partial interface IGetAndSetInt + { + int GetInt(); + + public void SetInt(int x); + + public const string IID = "2c3f9903-b586-46b1-881b-adfce9af47b1"; + } + [GeneratedComClass] + internal partial class GetAndSetInt : IGetAndSetInt + { + int _data = 0; + public int GetInt() => _data; + public void SetInt(int x) => _data = x; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs new file mode 100644 index 00000000000000..f2283708678df1 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid(IID)] + internal partial interface IGetIntArray + { + [return: MarshalUsing(ConstantElementCount = 10)] + int[] GetInts(); + + public const string IID = "7D802A0A-630A-4C8E-A21F-771CC9031FB9"; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs new file mode 100644 index 00000000000000..9d2fc102ba5df4 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("023EA72A-ECAA-4B65-9D96-2122CFADE16C")] + internal partial interface IHide + { + int SameMethod(); + int DifferentMethod(); + } + + [GeneratedComInterface] + [Guid("5293B3B1-4994-425C-803E-A21A5011E077")] + internal partial interface IHide2 : IHide + { + new int SameMethod(); + int DifferentMethod2(); + } + + internal interface UnrelatedInterfaceWithSameMethod + { + int SameMethod(); + int DifferentMethod3(); + } + + [GeneratedComInterface] + [Guid("5DD35432-4987-488D-94F1-7682D7E4405C")] + internal partial interface IHide3 : IHide2, UnrelatedInterfaceWithSameMethod + { + new int SameMethod(); + new int DifferentMethod3(); + } + + [GeneratedComClass] + [Guid("2D36BD6D-C80E-4F00-86E9-8D1B4A0CB59A")] + /// + /// Implements IHides3 and returns the expected VTable index for each method. + /// + internal partial class HideBaseMethods : IHide3 + { + int IHide.SameMethod() => 3; + int IHide.DifferentMethod() => 4; + int IHide2.SameMethod() => 5; + int IHide2.DifferentMethod2() => 6; + int IHide3.SameMethod() => 7; + int IHide3.DifferentMethod3() => 8; + int UnrelatedInterfaceWithSameMethod.SameMethod() => -1; + int UnrelatedInterfaceWithSameMethod.DifferentMethod3() => -1; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs new file mode 100644 index 00000000000000..e50344f3a268fd --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("EE6D1F2A-3418-4317-A87C-35488F6546AB")] + internal partial interface IInt + { + public int Get(); + public void Set(int value); + public void SwapRef(ref int value); + public void GetOut(out int value); + public void SetIn(in int value); + } + + [GeneratedComClass] + internal partial class IIntImpl : IInt + { + int _data; + + public int Get() => _data; + + public void Set(int value) => _data = value; + + public void SetIn(in int value) + { + _data = value; + } + + public void GetOut(out int value) + { + value = _data; + } + + public void SwapRef(ref int value) + { + var tmp = _data; + _data = value; + value = tmp; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs new file mode 100644 index 00000000000000..fbc68dcf4dbca0 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] + internal partial interface IIntArray + { + [return: MarshalUsing(CountElementName = nameof(size))] + int[] GetReturn(out int size); + int GetOut([MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out int[] array); + void SetContents([MarshalUsing(CountElementName = nameof(size))] int[] array, int size); + void FillAscending([Out][MarshalUsing(CountElementName = nameof(size))] int[] array, int size); + void Double([In, Out][MarshalUsing(CountElementName = nameof(size))] int[] array, int size); + void PassIn([MarshalUsing(CountElementName = nameof(size))] in int[] array, int size); + void SwapArray([MarshalUsing(CountElementName = nameof(size))] ref int[] array, int size); + } + + [GeneratedComClass] + internal partial class IIntArrayImpl : IIntArray + { + int[] _data; + public int[] GetReturn(out int size) + { + size = _data.Length; + return _data; + } + public int GetOut(out int[] array) + { + array = _data; + return array.Length; + } + public void SetContents(int[] array, int size) + { + _data = new int[size]; + array.CopyTo(_data, 0); + } + + public void FillAscending(int[] array, int size) + { + for (int i = 0; i < size; i++) + { + array[i] = i; + } + } + public void Double(int[] array, int size) + { + for (int i = 0; i < size; i++) + { + array[i] = array[i] * 2; + } + + } + + public void PassIn([MarshalUsing(CountElementName = "size")] in int[] array, int size) + { + _data = new int[size]; + array.CopyTo(_data, 0); + } + public void SwapArray([MarshalUsing(CountElementName = "size")] ref int[] array, int size) + { + var temp = _data; + _data = array; + array = temp; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs new file mode 100644 index 00000000000000..95c6a13335b44d --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("A4857398-06FB-4A6E-81DB-35461BE999C5")] + internal partial interface IInterface + { + public IInt Get(); + public void SetInt(IInt value); + public void SwapRef(ref IInt value); + public void GetOut(out IInt value); + public void InInt(in IInt value); + } + + [GeneratedComClass] + internal partial class IInterfaceImpl : IInterface + { + IInt _data = new IIntImpl(); + + IInt IInterface.Get() => _data; + + void IInterface.InInt(in IInt value) + { + var i = value.Get(); + } + + void IInterface.GetOut(out IInt value) + { + value = _data; + } + + void IInterface.SwapRef(ref IInt value) + { + var tmp = _data; + _data = value; + value = tmp; + } + + void IInterface.SetInt(IInt value) + { + int x = value.Get(); + value.Set(x); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs new file mode 100644 index 00000000000000..c1153e377fc656 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] + internal partial interface IJaggedIntArray + { + [return: MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] + int[][] Get( + [MarshalUsing(CountElementName = nameof(length))] + out int[] widths, + out int length); + + int Get2( + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] + out int[][] array, + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] + out int[] widths); + + void Set( + [MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] + int[][] array, + [MarshalUsing(CountElementName = nameof(length))] + int[] widths, + int length); + } + + [GeneratedComClass] + internal partial class IJaggedIntArrayImpl : IJaggedIntArray + { + int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; + int[] _widths = new int[] { 3, 2, 4 }; + public int[][] Get(out int[] widths, out int length) + { + widths = _widths; + length = _data.Length; + return _data; + } + public int Get2(out int[][] array, out int[] widths) + { + array = _data; + widths = _widths; + return array.Length; + } + public void Set(int[][] array, int[] widths, int length) + { + _data = array; + _widths = widths; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs new file mode 100644 index 00000000000000..e29b21b33a54a0 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("E4461914-4202-479F-8427-620E915F84B9")] + internal partial interface IPointProvider + { + [PreserveSig] + Point GetPoint(); + + [PreserveSig] + [return:MarshalAs(UnmanagedType.Error)] + HResult SetPoint(Point point); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct HResult + { + public int Value; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs new file mode 100644 index 00000000000000..3a86b74a70b5c2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(StringMarshalling = System.Runtime.InteropServices.StringMarshalling.Utf8)] + [Guid(IID)] + internal partial interface IRefStrings + { + public const string IID = "5146B7DB-0588-469B-B8E5-B38090A2FC15"; + void RefString(ref string value); + void InString(in string value); + void OutString(out string value); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs new file mode 100644 index 00000000000000..50a25b520939fb --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Microsoft.Win32.SafeHandles; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + internal partial interface ISafeFileHandle + { + void Method(SafeFileHandle p); + void MethodIn(in SafeFileHandle p); + void MethodOut(out SafeFileHandle p); + void MethodRef(ref SafeFileHandle p); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs new file mode 100644 index 00000000000000..89e5e6a31e5bbb --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatefulAllShapes + { + void Method(StatefulAllShapesType param); + void MethodIn(in StatefulAllShapesType param); + void MethodOut(out StatefulAllShapesType param); + void MethodRef(ref StatefulAllShapesType param); + StatefulAllShapesType Return(); + [PreserveSig] + StatefulAllShapesType ReturnPreserveSig(); + } + + [NativeMarshalling(typeof(AllStatefulMarshallerShapes))] + internal class StatefulAllShapesType + { + } + + internal unsafe struct StatefulAllShapesNative + { + } + + [CustomMarshaller(typeof(StatefulAllShapesType), MarshalMode.Default, typeof(AllStatefulMarshallerShapes))] + internal unsafe ref struct AllStatefulMarshallerShapes + { + public static ref nint GetPinnableReference(StatefulAllShapesType managed) => throw new NotImplementedException(); + public ref nint GetPinnableReference() => throw new NotImplementedException("This is not currently used anywhere"); + public static int BufferSize => sizeof(StatefulAllShapesNative); + public void FromManaged(StatefulAllShapesType managed, Span buffer) => throw new NotImplementedException(); + public void FromManaged(StatefulAllShapesType managed) => throw new NotImplementedException(); + public StatefulAllShapesNative* ToUnmanaged() => throw new NotImplementedException(); + public void FromUnmanaged(StatefulAllShapesNative* unmanaged) => throw new NotImplementedException(); + public StatefulAllShapesType ToManaged() => throw new NotImplementedException(); + public StatefulAllShapesType ToManagedFinally() => throw new NotImplementedException(); + public void Free() => throw new NotImplementedException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs new file mode 100644 index 00000000000000..a585a5456b4039 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulCallerAllocatedBufferMarshalling + { + void Method(StatefulCallerAllocatedBufferType param); + void MethodIn(in StatefulCallerAllocatedBufferType param); + void MethodOut(out StatefulCallerAllocatedBufferType param); + void MethodRef(ref StatefulCallerAllocatedBufferType param); + StatefulCallerAllocatedBufferType Return(); + [PreserveSig] + StatefulCallerAllocatedBufferType ReturnPreserveSig(); + } + + [NativeMarshalling(typeof(StatefulCallerAllocatedBufferTypeMarshaller))] + internal class StatefulCallerAllocatedBufferType + { + } + + [CustomMarshaller(typeof(StatefulCallerAllocatedBufferType), MarshalMode.Default, typeof(StatefulCallerAllocatedBufferTypeMarshaller))] + internal struct StatefulCallerAllocatedBufferTypeMarshaller + { + public static int BufferSize => 64; + + public void FromManaged(StatefulCallerAllocatedBufferType managed, Span buffer) + { + throw new NotImplementedException(); + } + + public nint ToUnmanaged() + { + throw new NotImplementedException(); + } + + public void FromUnmanaged(nint unmanaged) + { + throw new NotImplementedException(); + } + + public StatefulCallerAllocatedBufferType ToManaged() + { + throw new NotImplementedException(); + } + + public void Free() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs new file mode 100644 index 00000000000000..481ca0e78296db --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulCollectionAllShapes + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatefulCollectionAllShapes param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatefulCollectionAllShapes param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatefulCollectionAllShapes param, out int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatefulCollectionAllShapes param, int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollectionAllShapes Return(out int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollectionAllShapes ReturnPreserveSig(out int size); + } + + [NativeMarshalling(typeof(StatefulCollectionAllShapesMarshaller<,>))] + internal class StatefulCollectionAllShapes + { + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatefulCollectionAllShapes<>), MarshalMode.Default, typeof(StatefulCollectionAllShapesMarshaller<,>))] + internal unsafe struct StatefulCollectionAllShapesMarshaller where TUnmanagedElement : unmanaged + { + public StatefulCollectionAllShapesMarshaller() { } + public void OnInvoked() { } + + // ManagedToUnmanaged + public void FromManaged(StatefulCollectionAllShapes collection) => throw new NotImplementedException(); + + public ReadOnlySpan GetManagedValuesSource() => throw new NotImplementedException(); + + public Span GetUnmanagedValuesDestination() => throw new NotImplementedException(); + + public ref nint GetPinnableReference() => throw new NotImplementedException(); + + public TUnmanagedElement* ToUnmanaged() => throw new NotImplementedException(); + + public static ref TUnmanagedElement* GetPinnableReference(StatefulCollectionAllShapes collection) => throw new NotImplementedException(); + + + // ManagedToUnmanaged with Caller Allocated Buffer + public static int BufferSize { get; } + + public void FromManaged(StatefulCollectionAllShapes collection, Span buffer) => throw new NotImplementedException(); + + + // UnmanagedToManaged + public void FromUnmanaged(TUnmanagedElement* value) => throw new NotImplementedException(); // Should not throw exceptions. + + public ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw new NotImplementedException(); // Can throw exceptions. + + public Span GetManagedValuesDestination(int numElements) => throw new NotImplementedException(); // Can throw exceptions. + + public StatefulCollectionAllShapes ToManaged() => throw new NotImplementedException(); // Can throw exceptions + + public void Free() { } + + + // UnmanagedToManaged with guaranteed unmarshalling + public StatefulCollectionAllShapes ToManagedFinally() => throw new NotImplementedException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs new file mode 100644 index 00000000000000..e4df4476cfb7cc --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + partial interface IStatefulCollectionBlittableElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatefulCollection p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection ReturnPreserveSig(int size); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs new file mode 100644 index 00000000000000..f417e7bc314cca --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52BA7C-E08B-42A4-A1F4-1A2BF2C07E60")] + internal partial interface IStatefulCollectionPinnableReference + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatefulCollectionPinnableReference p, + int size); + + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatefulCollectionPinnableReference pIn, + in int size); + + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollectionPinnableReference pRef, + int size); + + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatefulCollectionPinnableReference pOut, + out int size); + + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollectionPinnableReference Return(int size); + } + + [NativeMarshalling(typeof(StatefulCollectionPinnableReferenceMarshaller<,>))] + internal class StatefulCollectionPinnableReference + { + } + + internal struct StatefulCollectionPinnableReferenceNative + { + public static unsafe explicit operator void*(StatefulCollectionPinnableReferenceNative _) => throw new NotImplementedException(); + public static unsafe explicit operator StatefulCollectionPinnableReferenceNative(void* _) => throw new NotImplementedException(); + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedRef, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedIn, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedOut, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] + internal static class StatefulCollectionPinnableReferenceMarshaller where TUnmanaged : unmanaged + { + public struct Bidirectional + { + public void FromManaged(StatefulCollectionPinnableReference managed) => throw new NotImplementedException(); + public StatefulCollectionPinnableReferenceNative ToUnmanaged() => throw new NotImplementedException(); + public ReadOnlySpan GetManagedValuesSource() => throw new NotImplementedException(); + public Span GetUnmanagedValuesDestination() => throw new NotImplementedException(); + + public void FromUnmanaged(StatefulCollectionPinnableReferenceNative unmanaged) => throw new NotImplementedException(); + public StatefulCollectionPinnableReference ToManaged() => throw new NotImplementedException(); + public ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw new NotImplementedException(); + public Span GetManagedValuesDestination(int numElements) => throw new NotImplementedException(); + + public void Free() => throw new NotImplementedException(); + } + + public struct UnmanagedToManaged + { + public void Free() => throw new NotImplementedException(); + public void FromUnmanaged(StatefulCollectionPinnableReferenceNative unmanaged) => throw new NotImplementedException(); + public StatefulCollectionPinnableReference ToManaged() => throw new NotImplementedException(); + public ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw new NotImplementedException(); + public Span GetManagedValuesDestination(int numElements) => throw new NotImplementedException(); + } + + public struct ManagedToUnmanaged + { + public void FromManaged(StatefulCollectionPinnableReference managed) => throw new NotImplementedException(); + public StatefulCollectionPinnableReferenceNative ToUnmanaged() => throw new NotImplementedException(); + public ReadOnlySpan GetManagedValuesSource() => throw new NotImplementedException(); + public Span GetUnmanagedValuesDestination() => throw new NotImplementedException(); + public void Free() => throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs new file mode 100644 index 00000000000000..d31c70856b6ebc --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + partial interface IStatefulCollectionStatelessElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatefulCollection p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatefulCollection ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatefulCollectionMarshaller<,>))] + internal class StatefulCollection + { + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatefulCollection<>), MarshalMode.Default, typeof(StatefulCollectionMarshaller<,>.Default))] + static unsafe class StatefulCollectionMarshaller where TUnmanagedElement : unmanaged + { + public ref struct Default + { + public byte* ToUnmanaged() => throw null; + public System.ReadOnlySpan GetManagedValuesSource() => throw null; + public System.Span GetUnmanagedValuesDestination() => throw null; + + public void FromUnmanaged(byte* value) => throw null; + public System.Span GetManagedValuesDestination(int numElements) => throw null; + public System.ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw null; + + public void Free() => throw null; + + public void FromManaged(StatefulCollection managed) => throw null; + + public StatefulCollection ToManaged() => throw null; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs new file mode 100644 index 00000000000000..3c9951c03fafe8 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulFinallyMarshalling + { + void Method(StatefulFinallyType param); + void MethodIn(in StatefulFinallyType param); + void MethodOut(out StatefulFinallyType param); + void MethodRef(ref StatefulFinallyType param); + StatefulFinallyType Return(); + [PreserveSig] + StatefulFinallyType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatefulFinallyMarshalling : IStatefulFinallyMarshalling + { + public void Method(StatefulFinallyType param) + { + _ = param.i; + } + public void MethodIn(in StatefulFinallyType param) + { + _ = param.i; + } + public void MethodOut(out StatefulFinallyType param) + { + param = new StatefulFinallyType() { i = 42 }; + } + public void MethodRef(ref StatefulFinallyType param) + { + _ = param.i; + param = new StatefulFinallyType() { i = 99 }; + } + public StatefulFinallyType Return() + => new StatefulFinallyType() { i = 8 }; + public StatefulFinallyType ReturnPreserveSig() + => new StatefulFinallyType() { i = 3 }; + } + + [NativeMarshalling(typeof(StatefulFinallyTypeMarshaller))] + internal class StatefulFinallyType + { + public int i; + } + + internal struct StatefulFinallyNative + { + public int i; + } + + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] + internal struct StatefulFinallyTypeMarshaller + { + internal struct Bidirectional + { + int managed_i; + int unmanaged_i; + + public void FromManaged(StatefulFinallyType managed) + { + managed_i = managed.i; + } + + public StatefulFinallyNative ToUnmanaged() + { + return new StatefulFinallyNative() { i = this.managed_i }; + } + + public void FromUnmanaged(StatefulFinallyNative unmanaged) + { + unmanaged_i = unmanaged.i; + } + + public StatefulFinallyType ToManagedFinally() + { + return new StatefulFinallyType() { i = unmanaged_i }; + } + + public void Free() { } + + public void OnInvoked() { } + } + + internal struct ManagedToUnmanaged + { + int managed_i; + + public void FromManaged(StatefulFinallyType managed) + { + managed_i = managed.i; + } + + public StatefulFinallyNative ToUnmanaged() + { + return new StatefulFinallyNative() { i = this.managed_i }; + } + + public void Free() { } + + public void OnInvoked() { } + } + + internal struct UnmanagedToManaged + { + int unmanaged_i; + + public void FromUnmanaged(StatefulFinallyNative unmanaged) + { + unmanaged_i = unmanaged.i; + } + public StatefulFinallyType ToManagedFinally() + { + return new StatefulFinallyType() { i = unmanaged_i }; + } + + public void Free() { } + + public void OnInvoked() { } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs new file mode 100644 index 00000000000000..47b6cc916c3384 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulMarshalling + { + void Method(StatefulType param); + void MethodIn(in StatefulType param); + void MethodOut(out StatefulType param); + void MethodRef(ref StatefulType param); + StatefulType Return(); + [PreserveSig] + StatefulType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatefulMarshalling : IStatefulMarshalling + { + public void Method(StatefulType param) => param.i++; + public void MethodIn(in StatefulType param) => param.i++; + public void MethodOut(out StatefulType param) => param = new StatefulType() { i = 1 }; + public void MethodRef(ref StatefulType param) { } + public StatefulType Return() => new StatefulType() { i = 1 }; + public StatefulType ReturnPreserveSig() => new StatefulType() { i = 1 }; + } + + [NativeMarshalling(typeof(StatefulTypeMarshaller))] + internal class StatefulType + { + public int i; + } + + internal struct StatefulNative + { + public int i; + } + + [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] + internal struct StatefulTypeMarshaller + { + public static int FreeCount => Bidirectional.FreeCount + ManagedToUnmanaged.FreeCount + UnmanagedToManaged.FreeCount; + internal struct Bidirectional + { + public static int FreeCount { get; private set; } + StatefulType? _managed; + bool _hasManaged; + StatefulNative _unmanaged; + bool _hasUnmanaged; + + public void FromManaged(StatefulType managed) + { + _hasManaged = true; + _managed = managed; + } + + public StatefulNative ToUnmanaged() + { + if (!_hasManaged) throw new InvalidOperationException(); + return new StatefulNative() { i = _managed.i }; + } + + public void FromUnmanaged(StatefulNative unmanaged) + { + _hasUnmanaged = true; + _unmanaged = unmanaged; + } + + public StatefulType ToManaged() + { + if (!_hasUnmanaged) + { + throw new InvalidOperationException(); + } + if (_hasManaged && _managed.i == _unmanaged.i) + { + return _managed; + } + return new StatefulType() { i = _unmanaged.i }; + } + + public void Free() + { + FreeCount++; + } + + public void OnInvoked() { } + } + + internal struct ManagedToUnmanaged + { + public static int FreeCount { get; private set; } + StatefulType? _managed; + bool _hasManaged; + public void FromManaged(StatefulType managed) + { + _hasManaged = true; + _managed = managed; + } + + public StatefulNative ToUnmanaged() + { + if (!_hasManaged) throw new InvalidOperationException(); + return new StatefulNative() { i = _managed.i }; + } + + public void Free() + { + FreeCount++; + } + + public void OnInvoked() { } + } + + internal struct UnmanagedToManaged + { + public static int FreeCount { get; private set; } + StatefulNative _unmanaged; + bool _hasUnmanaged; + + public void FromUnmanaged(StatefulNative unmanaged) + { + _hasUnmanaged = true; + _unmanaged = unmanaged; + } + + public StatefulType ToManaged() + { + if (!_hasUnmanaged) + { + throw new InvalidOperationException(); + } + return new StatefulType() { i = _unmanaged.i }; + } + + public void Free() + { + FreeCount++; + } + + public void OnInvoked() { } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs new file mode 100644 index 00000000000000..1347a2858e3725 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatefulPinnedMarshalling + { + void Method(StatefulPinnedType param); + void MethodIn(in StatefulPinnedType param); + void MethodOut(out StatefulPinnedType param); + void MethodRef(ref StatefulPinnedType param); + StatefulPinnedType Return(); + } + + [GeneratedComClass] + internal partial class StatefulPinnedMarshalling : IStatefulPinnedMarshalling + { + public void Method(StatefulPinnedType param) { param.I = 100; } + public void MethodIn(in StatefulPinnedType param) { param.I = 101; } + public void MethodOut(out StatefulPinnedType param) => param = new StatefulPinnedType() { I = 102 }; + public void MethodRef(ref StatefulPinnedType param) { param = new StatefulPinnedType() { I = 103 }; } + public StatefulPinnedType Return() => new StatefulPinnedType() { I = 104 }; + } + + [NativeMarshalling(typeof(StatefulPinnedTypeMarshaller))] + internal class StatefulPinnedType + { + public int I; + } + + internal unsafe struct StatefulPinnedNative + { + public int I; + } + + [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] + [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.UnmanagedToManagedOut, typeof(UnmanagedToManagedOut))] + [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManagedIn))] + [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedToUnmanagedOut))] + [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.ManagedToUnmanagedRef, typeof(ManagedToUnmanagedRef))] + [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.UnmanagedToManagedRef, typeof(UnmanagedToManagedRef))] + internal unsafe static class StatefulPinnedTypeMarshaller + { + public ref struct ManagedToUnmanagedIn + { + static bool s_mustPin; + public static void DisableNonPinnedPath() => s_mustPin = true; + public static void EnableNonPinnedPath() => s_mustPin = false; + + StatefulPinnedType _managed; + bool _hasManaged; + Span buffer; + nint _ptr; + bool _canFree; + bool _isPinned; + ref StatefulPinnedNative _refNativeStruct; + + public void FromManaged(StatefulPinnedType managed) + { + _hasManaged = true; + _managed = managed; + } + + public ref StatefulPinnedNative GetPinnableReference() + { + if (!_hasManaged) + throw new InvalidOperationException(); + buffer = new byte[sizeof(StatefulPinnedNative)]; + _isPinned = true; + _refNativeStruct = ref MemoryMarshal.AsRef(buffer); + return ref _refNativeStruct; + } + + public StatefulPinnedNative* ToUnmanaged() + { + if (!_hasManaged) + throw new InvalidOperationException(); + + _canFree = true; + if (_isPinned) + { + // Unsafe.AsPointer is safe, because the result from GetPinnableReference is pinned + _refNativeStruct = new StatefulPinnedNative() { I = _managed.I }; + return (StatefulPinnedNative*)Unsafe.AsPointer(ref _refNativeStruct); + } + + if (s_mustPin) + throw new InvalidOperationException("Expected to pin, but is instead converting with default ToUnmanaged."); + + _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); + *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; + return (StatefulPinnedNative*)_ptr; + } + + public void Free() + { + if (!_canFree) + throw new InvalidOperationException(); + + if (!_isPinned && _ptr != 0) + { + Marshal.FreeHGlobal(_ptr); + } + } + } + + public struct ManagedToUnmanagedOut + { + StatefulPinnedNative* _unmanaged; + bool _hasUnmanaged; + + public void FromUnmanaged(StatefulPinnedNative* unmanaged) + { + _unmanaged = unmanaged; + _hasUnmanaged = true; + } + + public StatefulPinnedType ToManaged() + { + if (!_hasUnmanaged) + throw new InvalidOperationException(); + return new StatefulPinnedType() { I = _unmanaged->I }; + } + + public void Free() + { + if (!_hasUnmanaged) + throw new InvalidOperationException(); + var ptr = (nint)_unmanaged; + if (ptr != 0) + Marshal.FreeHGlobal(ptr); + } + } + + public struct UnmanagedToManagedIn + { + StatefulPinnedNative* _unmanaged; + bool _hasUnmanaged; + public void FromUnmanaged(StatefulPinnedNative* unmanaged) + { + _unmanaged = unmanaged; + _hasUnmanaged = true; + } + + public StatefulPinnedType ToManaged() + { + if (!_hasUnmanaged) + throw new InvalidOperationException(); + return new StatefulPinnedType() { I = _unmanaged->I }; + } + + public void Free() + { + } + } + + public struct UnmanagedToManagedOut + { + StatefulPinnedType _managed; + bool _hasManaged; + nint _ptr; + + public void FromManaged(StatefulPinnedType managed) + { + _hasManaged = true; + _managed = managed; + } + + public StatefulPinnedNative* ToUnmanaged() + { + if (!_hasManaged) + throw new InvalidOperationException(); + _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); + *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; + return (StatefulPinnedNative*)_ptr; + } + + public void Free() + { + } + } + + + public struct ManagedToUnmanagedRef + { + StatefulPinnedNative* _unmanaged; + bool _hasUnmanaged; + public void FromUnmanaged(StatefulPinnedNative* unmanaged) + { + _unmanaged = unmanaged; + _hasUnmanaged = true; + } + + public StatefulPinnedType ToManaged() + { + if (!_hasUnmanaged) + throw new InvalidOperationException(); + return new StatefulPinnedType() { I = _unmanaged->I }; + } + + StatefulPinnedType _managed; + bool _hasManaged; + nint _ptr; + bool _hasAllocated; + + public void FromManaged(StatefulPinnedType managed) + { + _hasManaged = true; + _managed = managed; + } + + public StatefulPinnedNative* ToUnmanaged() + { + if (!_hasManaged) + throw new InvalidOperationException(); + _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); + _hasAllocated = true; + *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; + return (StatefulPinnedNative*)_ptr; + } + + public void Free() + { + if (_hasUnmanaged) + { + Marshal.FreeHGlobal((nint)_unmanaged); + } + else if (_hasAllocated) + { + Marshal.FreeHGlobal(_ptr); + } + } + } + public struct UnmanagedToManagedRef + { + StatefulPinnedNative* _unmanaged; + bool _hasUnmanaged; + public void FromUnmanaged(StatefulPinnedNative* unmanaged) + { + _unmanaged = unmanaged; + _hasUnmanaged = true; + } + + public StatefulPinnedType ToManaged() + { + if (!_hasUnmanaged) + throw new InvalidOperationException(); + return new StatefulPinnedType() { I = _unmanaged->I }; + } + + StatefulPinnedType _managed; + bool _hasManaged; + nint _ptr; + bool _hasAllocated; + + public void FromManaged(StatefulPinnedType managed) + { + _hasManaged = true; + _managed = managed; + } + + public StatefulPinnedNative* ToUnmanaged() + { + if (!_hasManaged) + throw new InvalidOperationException(); + _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); + _hasAllocated = true; + *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; + return (StatefulPinnedNative*)_ptr; + } + + public void Free() + { + if (_hasAllocated && _hasUnmanaged) + { + Marshal.FreeHGlobal((nint)_unmanaged); + } + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs new file mode 100644 index 00000000000000..42d1486a690b16 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + /// + /// This type uses a stateless marshaller with all methods from every marshaller shape present + /// + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessAllShapes + { + void Method(StatelessAllShapesType param); + void MethodIn(in StatelessAllShapesType param); + void MethodOut(out StatelessAllShapesType param); + void MethodRef(ref StatelessAllShapesType param); + StatelessAllShapesType Return(); + [PreserveSig] + StatelessAllShapesType ReturnPreserveSig(); + void Arrays( + int size, + [MarshalUsing(CountElementName = nameof(size))] + StatelessAllShapesType[] param, + [MarshalUsing(CountElementName = nameof(size))] + in StatelessAllShapesType[] paramIn, + [MarshalUsing(CountElementName = nameof(size))] + out StatelessAllShapesType[] paramOut, + [MarshalUsing(CountElementName = nameof(size))] + ref StatelessAllShapesType[] paramRef, + [MarshalUsing(CountElementName = nameof(size))] + [In] StatelessAllShapesType[] paramContentsIn, + [MarshalUsing(CountElementName = nameof(size))] + [Out] StatelessAllShapesType[] paramContentsOut, + [MarshalUsing(CountElementName = nameof(size))] + [In, Out] StatelessAllShapesType[] paramContentsInOut); + + } + + [NativeMarshalling(typeof(AllStatelessMarshallerShapes))] + internal class StatelessAllShapesType + { + } + + internal struct StatelessAllShapesNative + { + } + + [CustomMarshaller(typeof(StatelessAllShapesType), MarshalMode.Default, typeof(AllStatelessMarshallerShapes))] + internal unsafe static class AllStatelessMarshallerShapes + { + public static ref nint GetPinnableReference(StatelessAllShapesType managed) => throw new NotImplementedException(); + public static int BufferSize => 32; + public static StatelessAllShapesNative* ConvertToUnmanaged(StatelessAllShapesType managed, Span buffer) => throw new NotImplementedException(); + public static StatelessAllShapesNative* ConvertToUnmanaged(StatelessAllShapesType managed) => throw new NotImplementedException(); + public static StatelessAllShapesType ConvertToManaged(StatelessAllShapesNative* unmanaged) => throw new NotImplementedException(); + public static StatelessAllShapesType ConvertToManagedFinally(StatelessAllShapesNative* unmanaged) => throw new NotImplementedException(); + public static void Free(StatelessAllShapesNative* unmanaged) { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs new file mode 100644 index 00000000000000..6388ab2990113b --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs @@ -0,0 +1,165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessCallerAllocatedBufferMarshalling + { + void Method(StatelessCallerAllocatedBufferType param); + void MethodIn(in StatelessCallerAllocatedBufferType param); + void MethodOut(out StatelessCallerAllocatedBufferType param); + void MethodRef(ref StatelessCallerAllocatedBufferType param); + StatelessCallerAllocatedBufferType Return(); + [PreserveSig] + StatelessCallerAllocatedBufferType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessCallerAllocatedBufferMarshalling : IStatelessCallerAllocatedBufferMarshalling + { + public void Method(StatelessCallerAllocatedBufferType param) { } + public void MethodIn(in StatelessCallerAllocatedBufferType param) { } + public void MethodOut(out StatelessCallerAllocatedBufferType param) { param = new StatelessCallerAllocatedBufferType { I = 20 }; } + public void MethodRef(ref StatelessCallerAllocatedBufferType param) { param = new StatelessCallerAllocatedBufferType { I = 200 }; } + public StatelessCallerAllocatedBufferType Return() => new StatelessCallerAllocatedBufferType() { I = 201 }; + public StatelessCallerAllocatedBufferType ReturnPreserveSig() => new StatelessCallerAllocatedBufferType() { I = 202 }; + } + + [NativeMarshalling(typeof(StatelessCallerAllocatedBufferTypeMarshaller))] + internal class StatelessCallerAllocatedBufferType + { + public int I; + } + + internal struct StatelessCallerAllocatedBufferNative + { + public int I; + } + + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ElementIn, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ElementOut, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ElementRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] + [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.UnmanagedToManagedOut, typeof(UnmanagedToManagedOut))] + internal static unsafe class StatelessCallerAllocatedBufferTypeMarshaller + { + static bool _canAllocate = true; + public static void DisableAllocations() => _canAllocate = false; + public static void EnableAllocations() => _canAllocate = true; + public static void AssertAllPointersFreed() + { + if (_ptrs.Any()) throw new InvalidOperationException(); + } + + static HashSet _ptrs = new(); + + public static int FreeCount { get; private set; } + + public static class UnmanagedToManaged + { + public static StatelessCallerAllocatedBufferType ConvertToManaged(StatelessCallerAllocatedBufferNative* unmanaged) + { + return new StatelessCallerAllocatedBufferType() { I = unmanaged->I }; + } + + public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) + { + FreeCount++; + if (_ptrs.Contains((nint)unmanaged)) + { + Marshal.FreeHGlobal((nint)unmanaged); + _ptrs.Remove((nint)unmanaged); + } + } + } + + public static class ManagedToUnmanagedIn + { + public static int BufferSize => sizeof(StatelessCallerAllocatedBufferNative); + + public static StatelessCallerAllocatedBufferNative* ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed, Span buffer) + { + var unmanaged = new StatelessCallerAllocatedBufferNative() { I = managed.I }; + MemoryMarshal.Write(buffer, in unmanaged); + // Unsafe.AsPointer is safe since buffer is pinned + return (StatelessCallerAllocatedBufferNative*)Unsafe.AsPointer(ref MemoryMarshal.AsRef(buffer)); + } + + public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) + { + FreeCount++; + if (_ptrs.Contains((nint)unmanaged)) + { + Marshal.FreeHGlobal((nint)unmanaged); + _ptrs.Remove((nint)unmanaged); + } + } + } + + public static class UnmanagedToManagedOut + { + public static StatelessCallerAllocatedBufferNative* ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed) + { + if (!_canAllocate) + throw new InvalidOperationException("Marshalling used default ConverToUnmanaged when CallerAllocatedBuffer was expected"); + nint ptr = Marshal.AllocHGlobal(sizeof(StatelessCallerAllocatedBufferNative)); + _ptrs.Add(ptr); + var structPtr = (StatelessCallerAllocatedBufferNative*)ptr; + structPtr->I = managed.I; + return structPtr; + } + + public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) + { + FreeCount++; + if (_ptrs.Contains((nint)unmanaged)) + { + Marshal.FreeHGlobal((nint)unmanaged); + _ptrs.Remove((nint)unmanaged); + } + } + } + + public static class Bidirectional + { + public static StatelessCallerAllocatedBufferNative* ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed) + { + if (!_canAllocate) + throw new InvalidOperationException("Marshalling used default ConverToUnmanaged when CallerAllocatedBuffer was expected"); + nint ptr = Marshal.AllocHGlobal(sizeof(StatelessCallerAllocatedBufferNative)); + _ptrs.Add(ptr); + var structPtr = (StatelessCallerAllocatedBufferNative*)ptr; + structPtr->I = managed.I; + return structPtr; + } + + public static StatelessCallerAllocatedBufferType ConvertToManaged(StatelessCallerAllocatedBufferNative* unmanaged) + { + return new StatelessCallerAllocatedBufferType() { I = unmanaged->I }; + } + + public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) + { + FreeCount++; + if (_ptrs.Contains((nint)unmanaged)) + { + Marshal.FreeHGlobal((nint)unmanaged); + _ptrs.Remove((nint)unmanaged); + } + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs new file mode 100644 index 00000000000000..b9aeecd69d10a3 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] + internal partial interface IStatelessCollectionAllShapes + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessCollectionAllShapes param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessCollectionAllShapes param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessCollectionAllShapes param, out int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessCollectionAllShapes param, int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollectionAllShapes Return(out int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollectionAllShapes ReturnPreserveSig(out int size); + } + + [NativeMarshalling(typeof(StatelessCollectionAllShapesMarshaller<,>))] + internal class StatelessCollectionAllShapes + { + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatelessCollectionAllShapes<>), MarshalMode.Default, typeof(StatelessCollectionAllShapesMarshaller<,>))] + internal unsafe static class StatelessCollectionAllShapesMarshaller where TUnmanagedElement : unmanaged + { + public static void Free(TUnmanagedElement* unmanaged) { } + + // ToUnmanaged + public static TUnmanagedElement* AllocateContainerForUnmanagedElements(StatelessCollectionAllShapes managed, out int numElements) + => throw new NotImplementedException(); + + public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionAllShapes managed) // Can throw exceptions + => throw new NotImplementedException(); + + public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) // Can throw exceptions + => throw new NotImplementedException(); + + public static ref TUnmanagedElement* GetPinnableReference(StatelessCollectionAllShapes managed) + => throw new NotImplementedException(); + + + + // Caller Allocated buffer ToUnmanaged + public static int BufferSize { get; } + public static TUnmanagedElement* AllocateContainerForUnmanagedElements(StatelessCollectionAllShapes managed, Span buffer, out int numElements) + => throw new NotImplementedException(); + + + // ToManaged + public static StatelessCollectionAllShapes AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) + => throw new NotImplementedException(); + + public static Span GetManagedValuesDestination(StatelessCollectionAllShapes managed) + => throw new NotImplementedException(); + + public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) + => throw new NotImplementedException(); + + + //ToManaged Guaranteed marshalling + public static StatelessCollectionAllShapes AllocateContainerForManagedElementsFinally(TUnmanagedElement* unmanaged, int numElements) + => throw new NotImplementedException(); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs new file mode 100644 index 00000000000000..684352435e6269 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + partial interface IStatelessCollectionBlittableElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatelessCollection p, + int size); + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, + in int size); + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, + int size); + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, + out int size); + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection Return(int size); + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection ReturnPreserveSig(int size); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs new file mode 100644 index 00000000000000..c13caee66fa472 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + internal partial interface IStatelessCollectionCallerAllocatedBuffer + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatelessCollectionCallerAllocatedBuffer p, + int size); + + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollectionCallerAllocatedBuffer pIn, + in int size); + + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollectionCallerAllocatedBuffer pRef, + int size); + + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollectionCallerAllocatedBuffer pOut, + out int size); + + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollectionCallerAllocatedBuffer Return(int size); + + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollectionCallerAllocatedBuffer ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>))] + internal class StatelessCollectionCallerAllocatedBuffer + { + } + + internal struct StatelessCollectionCallerAllocatedBufferNative + { + + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.Bidirectional))] + internal static unsafe class StatelessCollectionCallerAllocatedBufferMarshaller where TUnmanagedElement : unmanaged + { + internal static class Bidirectional + { + public static int BufferSize => throw new NotImplementedException(); + public static StatelessCollectionCallerAllocatedBufferNative AllocateContainerForUnmanagedElements(StatelessCollectionCallerAllocatedBuffer managed, Span buffer, out int numElements) + { + throw new NotImplementedException(); + } + + // Bidirectional requires non-buffer version of this method + public static StatelessCollectionCallerAllocatedBufferNative AllocateContainerForUnmanagedElements(StatelessCollectionCallerAllocatedBuffer managed, out int numElements) + { + throw new NotImplementedException(); + } + + public static StatelessCollectionCallerAllocatedBuffer AllocateContainerForManagedElements(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionCallerAllocatedBuffer managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(StatelessCollectionCallerAllocatedBuffer managed) + { + throw new NotImplementedException(); + } + + public static void Free(StatelessCollectionCallerAllocatedBufferNative unmanaged) { } + } + + internal static class ManagedToUnmanaged + { + public static int BufferSize => throw new NotImplementedException(); + public static StatelessCollectionCallerAllocatedBufferNative AllocateContainerForUnmanagedElements(StatelessCollectionCallerAllocatedBuffer managed, Span buffer, out int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionCallerAllocatedBuffer managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static void Free(StatelessCollectionCallerAllocatedBufferNative unmanaged) => throw new NotImplementedException(); + } + + internal static class UnmanagedToManaged + { + public static StatelessCollectionCallerAllocatedBuffer AllocateContainerForManagedElements(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(StatelessCollectionCallerAllocatedBuffer managed) + { + throw new NotImplementedException(); + } + + public static void Free(StatelessCollectionCallerAllocatedBufferNative unmanaged) => throw new NotImplementedException(); + + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs new file mode 100644 index 00000000000000..33d478c597b9c6 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52BA7C-E08B-42A4-A1F4-1A2BF2C07E60")] + internal partial interface IStatelessCollectionPinnableReference + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatelessCollectionPinnableReference p, + int size); + + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollectionPinnableReference pIn, + in int size); + + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollectionPinnableReference pRef, + int size); + + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollectionPinnableReference pOut, + out int size); + + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollectionPinnableReference Return(int size); + } + + [NativeMarshalling(typeof(StatelessCollectionPinnableReferenceMarshaller<,>))] + internal class StatelessCollectionPinnableReference + { + } + + internal struct StatelessCollectionPinnableReferenceNative + { + public static unsafe explicit operator void*(StatelessCollectionPinnableReferenceNative _) => throw new NotImplementedException(); + public static unsafe explicit operator StatelessCollectionPinnableReferenceNative(void* _) => throw new NotImplementedException(); + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ElementIn, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ElementOut, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ElementRef, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] + internal static class StatelessCollectionPinnableReferenceMarshaller where TUnmanaged : unmanaged + { + public static class Bidirectional + { + public static ref StatelessCollectionPinnableReferenceNative GetPinnableReference(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); + public static StatelessCollectionPinnableReferenceNative AllocateContainerForUnmanagedElements(StatelessCollectionPinnableReference managed, out int numElements) => throw new NotImplementedException(); + public static StatelessCollectionPinnableReference AllocateContainerForManagedElements(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); + public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); + public static Span GetUnmanagedValuesDestination(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); + public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); + public static Span GetManagedValuesDestination(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); + public static void Free(StatelessCollectionPinnableReferenceNative native) { } + } + + public static class UnmanagedToManaged + { + public static StatelessCollectionPinnableReference AllocateContainerForManagedElements(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); + public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); + public static Span GetManagedValuesDestination(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); + public static void Free(StatelessCollectionPinnableReferenceNative native) { } + } + + public static class ManagedToUnmanaged + { + public static ref StatelessCollectionPinnableReferenceNative GetPinnableReference(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); + public static StatelessCollectionPinnableReferenceNative AllocateContainerForUnmanagedElements(StatelessCollectionPinnableReference managed, out int numElements) => throw new NotImplementedException(); + public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); + public static Span GetUnmanagedValuesDestination(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); + public static void Free(StatelessCollectionPinnableReferenceNative native) { } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs new file mode 100644 index 00000000000000..68ad9f409b3b84 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] + internal partial interface IStatelessCollectionStatelessElement + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatelessCollection p, + int size); + + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, + in int size); + + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, + int size); + + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, + out int size); + + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection Return(int size); + + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessCollection ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatelessCollectionMarshaller<,>))] + internal class StatelessCollection + { + } + + internal struct NativeCollection + { + + } + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessCollectionMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessCollectionMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessCollectionMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessCollectionMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementIn, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementOut, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] + internal static unsafe class StatelessCollectionMarshaller where TUnmanagedElement : unmanaged + { + internal static class Bidirectional + { + public static NativeCollection AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) + { + throw new NotImplementedException(); + } + + public static StatelessCollection AllocateContainerForManagedElements(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(StatelessCollection managed) + { + throw new NotImplementedException(); + } + + public static void Free(NativeCollection unmanaged) { } + } + + internal static class ManagedToUnmanaged + { + public static NativeCollection AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static void Free(NativeCollection unmanaged) => throw new NotImplementedException(); + } + + internal static class UnmanagedToManaged + { + public static StatelessCollection AllocateContainerForManagedElements(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(NativeCollection unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(StatelessCollection managed) + { + throw new NotImplementedException(); + } + + public static void Free(NativeCollection unmanaged) => throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs new file mode 100644 index 00000000000000..be7e1a9893a0b7 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A26-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessFinallyMarshalling + { + void Method(StatelessFinallyType param); + void MethodIn(in StatelessFinallyType param); + void MethodOut(out StatelessFinallyType param); + void MethodRef(ref StatelessFinallyType param); + StatelessFinallyType Return(); + [PreserveSig] + StatelessFinallyType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessFinallyMarshalling : IStatelessFinallyMarshalling + { + public void Method(StatelessFinallyType param) { _ = param.I; } + public void MethodIn(in StatelessFinallyType param) { _ = param.I; } + public void MethodOut(out StatelessFinallyType param) { param = new StatelessFinallyType { I = 42 }; } + public void MethodRef(ref StatelessFinallyType param) { _ = param.I; param = new StatelessFinallyType { I = 200 }; } + public StatelessFinallyType Return() => new StatelessFinallyType { I = 200 }; + public StatelessFinallyType ReturnPreserveSig() => new StatelessFinallyType { I = 200 }; + } + + [NativeMarshalling(typeof(StatelessFinallyTypeMarshaller))] + internal class StatelessFinallyType + { + public int I; + } + + internal struct StatelessFinallyNative + { + public int i; + } + + [CustomMarshaller(typeof(StatelessFinallyType), MarshalMode.Default, typeof(StatelessFinallyTypeMarshaller))] + internal static class StatelessFinallyTypeMarshaller + { + public static int FreeCount { get; private set; } + public static StatelessFinallyNative ConvertToUnmanaged(StatelessFinallyType managed) => new StatelessFinallyNative() { i = managed.I }; + + public static StatelessFinallyType ConvertToManagedFinally(StatelessFinallyNative unmanaged) => new StatelessFinallyType { I = unmanaged.i }; + + public static void Free(StatelessFinallyNative unmanaged) => FreeCount++; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs new file mode 100644 index 00000000000000..e063e77258cc6a --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessMarshalling + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessType param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessType param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType param, int size); + StatelessType Return(); + [PreserveSig] + StatelessType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessMarshalling : IStatelessMarshalling + { + public void Method(StatelessType param, int size) { } + public void MethodIn(in StatelessType param, int size) { } + public void MethodOut(out StatelessType param, int size) { param = new StatelessType { I = 42 }; } + public void MethodRef(ref StatelessType param, int size) { param = new StatelessType { I = 200 }; } + public StatelessType Return() => throw new NotImplementedException(); + public StatelessType ReturnPreserveSig() => throw new NotImplementedException(); + } + + [NativeMarshalling(typeof(StatelessTypeMarshaller))] + internal class StatelessType + { + public int I; + } + + [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementOut, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementIn, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] + [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] + internal static class StatelessTypeMarshaller + { + public static int AllFreeCount => Bidirectional.FreeCount + UnmanagedToManaged.FreeCount + ManagedToUnmanaged.FreeCount; + + internal static class Bidirectional + { + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; + + public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; + + public static void Free(nint unmanaged) => FreeCount++; + } + + internal static class ManagedToUnmanaged + { + public static int FreeCount { get; private set; } + public static void Free(nint unmanaged) => FreeCount++; + public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; + } + + internal static class UnmanagedToManaged + { + public static int FreeCount { get; private set; } + public static void Free(nint unmanaged) => FreeCount++; + public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs new file mode 100644 index 00000000000000..f625af2d27e3b1 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs @@ -0,0 +1,156 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("3BBB0C99-7D6C-4AD1-BE4C-ACB4C2127F02")] + internal partial interface IStatelessPinnableCollectionBlittableElements + { + void Method( + [MarshalUsing(CountElementName = nameof(size))] StatelessPinnableCollection p, + int size); + + void MethodIn( + [MarshalUsing(CountElementName = nameof(size))] in StatelessPinnableCollection pIn, + in int size); + + void MethodRef( + [MarshalUsing(CountElementName = nameof(size))] ref StatelessPinnableCollection pRef, + int size); + + void MethodOut( + [MarshalUsing(CountElementName = nameof(size))] out StatelessPinnableCollection pOut, + out int size); + + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessPinnableCollection Return(int size); + + [PreserveSig] + [return: MarshalUsing(CountElementName = nameof(size))] + StatelessPinnableCollection ReturnPreserveSig(int size); + } + + [NativeMarshalling(typeof(StatelessPinnableCollectionMarshaller<,>))] + internal class StatelessPinnableCollection where T : unmanaged + { + } + + internal unsafe struct StatelessPinnableCollectionNative where T : unmanaged + { + public static explicit operator StatelessPinnableCollectionNative(void* ptr) => new StatelessPinnableCollectionNative(); + public static explicit operator void*(StatelessPinnableCollectionNative ptr) => (void*)null; + } + + + [ContiguousCollectionMarshaller] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessPinnableCollectionMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessPinnableCollectionMarshaller<,>.ManagedToUnmanaged))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessPinnableCollectionMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessPinnableCollectionMarshaller<,>.UnmanagedToManaged))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ElementIn, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ElementOut, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] + [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ElementRef, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] + internal static unsafe class StatelessPinnableCollectionMarshaller + where T : unmanaged + where TUnmanagedElement : unmanaged + { + internal static class Bidirectional + { + public static StatelessPinnableCollectionNative AllocateContainerForUnmanagedElements(StatelessPinnableCollection managed, out int numElements) + { + throw new NotImplementedException(); + } + + public static StatelessPinnableCollection AllocateContainerForManagedElements(StatelessPinnableCollectionNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(StatelessPinnableCollection managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(StatelessPinnableCollectionNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(StatelessPinnableCollectionNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(StatelessPinnableCollection managed) + { + throw new NotImplementedException(); + } + + public static ref StatelessPinnableCollectionNative GetPinnableReference(StatelessPinnableCollection managed) + { + throw new NotImplementedException(); + } + + public static void Free(StatelessPinnableCollectionNative unmanaged) { } + } + + internal static class ManagedToUnmanaged + { + public static StatelessPinnableCollectionNative AllocateContainerForUnmanagedElements(StatelessPinnableCollection managed, out int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetManagedValuesSource(StatelessPinnableCollection managed) + { + throw new NotImplementedException(); + } + + public static Span GetUnmanagedValuesDestination(StatelessPinnableCollectionNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ref StatelessPinnableCollectionNative GetPinnableReference(StatelessPinnableCollection managed) + { + throw new NotImplementedException(); + } + + public static void Free(StatelessPinnableCollectionNative unmanaged) => throw new NotImplementedException(); + } + + internal static class UnmanagedToManaged + { + public static StatelessPinnableCollection AllocateContainerForManagedElements(StatelessPinnableCollectionNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static ReadOnlySpan GetUnmanagedValuesSource(StatelessPinnableCollectionNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + // Should be removed: https://github.com/dotnet/runtime/issues/89885 + public static Span GetUnmanagedValuesDestination(StatelessPinnableCollectionNative unmanaged, int numElements) + { + throw new NotImplementedException(); + } + + public static Span GetManagedValuesDestination(StatelessPinnableCollection managed) + { + throw new NotImplementedException(); + } + + public static void Free(StatelessPinnableCollectionNative unmanaged) => throw new NotImplementedException(); + + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs new file mode 100644 index 00000000000000..4419fc52c077cf --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface] + [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] + internal partial interface IStatelessPinnedMarshalling + { + void Method([MarshalUsing(CountElementName = nameof(size))] StatelessPinnedType param, int size); + void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessPinnedType param, int size); + void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessPinnedType param, int size); + void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessPinnedType param, int size); + StatelessPinnedType Return(); + [PreserveSig] + StatelessPinnedType ReturnPreserveSig(); + } + + [GeneratedComClass] + internal partial class StatelessPinnedMarshalling : IStatelessPinnedMarshalling + { + public void Method([MarshalUsing(CountElementName = "size")] StatelessPinnedType param, int size) { } + public void MethodIn([MarshalUsing(CountElementName = "size")] in StatelessPinnedType param, int size) { } + public void MethodOut([MarshalUsing(CountElementName = "size")] out StatelessPinnedType param, int size) { param = new StatelessPinnedType { I = 42 }; } + public void MethodRef([MarshalUsing(CountElementName = "size")] ref StatelessPinnedType param, int size) { param = new StatelessPinnedType { I = 200 }; } + public StatelessPinnedType Return() => throw new NotImplementedException(); + public StatelessPinnedType ReturnPreserveSig() => throw new NotImplementedException(); + } + + [NativeMarshalling(typeof(StatelessPinnedTypeMarshaller))] + internal class StatelessPinnedType + { + public int I; + } + + internal struct StatelessPinnedStruct + { + public int I; + } + + [CustomMarshaller(typeof(StatelessPinnedType), MarshalMode.Default, typeof(StatelessPinnedTypeMarshaller))] + internal static class StatelessPinnedTypeMarshaller + { + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(StatelessPinnedType managed) => managed.I; + + public static StatelessPinnedType ConvertToManaged(nint unmanaged) => new StatelessPinnedType { I = (int)unmanaged }; + + static StatelessPinnedStruct _field; + public static ref StatelessPinnedStruct GetPinnableReference(StatelessPinnedType unmanaged) + { + _field = new StatelessPinnedStruct() { I = unmanaged.I }; + return ref _field; + } + + public static void Free(nint unmanaged) => FreeCount++; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs new file mode 100644 index 00000000000000..df956009fce8be --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(StringMarshalling = System.Runtime.InteropServices.StringMarshalling.Utf8)] + [Guid(IID)] + internal partial interface IStringMarshallingOverride + { + public const string IID = "5146B7DB-0588-469B-B8E5-B38090A2FC15"; + string StringMarshallingUtf8(string input); + + [return: MarshalAs(UnmanagedType.LPWStr)] + string MarshalAsLPWString([MarshalAs(UnmanagedType.LPWStr)] string input); + + [return: MarshalUsing(typeof(Utf16StringMarshaller))] + string MarshalUsingUtf16([MarshalUsing(typeof(Utf16StringMarshaller))] string input); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs new file mode 100644 index 00000000000000..5022d42d13930f --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] + [Guid(IID)] + internal partial interface IStringMarshallingOverrideDerived : IStringMarshallingOverride + { + public new const string IID = "3AFFE3FD-D11E-4195-8250-0C73321977A0"; + string StringMarshallingUtf8_2(string input); + + [return: MarshalAs(UnmanagedType.LPWStr)] + string MarshalAsLPWString_2([MarshalAs(UnmanagedType.LPWStr)] string input); + + [return: MarshalUsing(typeof(Utf16StringMarshaller))] + string MarshalUsingUtf16_2([MarshalUsing(typeof(Utf16StringMarshaller))] string input); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs new file mode 100644 index 00000000000000..0e4584c0982587 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] + [Guid(IID)] + internal partial interface ISystem + { + // Make sure method names System and Microsoft don't interfere with framework type / method references + void Microsoft(int p); + void System(int p); + public const string IID = "3BFFE3FD-D11E-4195-8250-0C73321977A0"; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs new file mode 100644 index 00000000000000..1c123065a2ec1c --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [Guid(IID)] + [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] + internal partial interface IUTF16Marshalling + { + public string GetString(); + + public void SetString(string value); + + public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs new file mode 100644 index 00000000000000..08dbbdd5e68924 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces +{ + [Guid(IID)] + [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] + internal partial interface IUTF8Marshalling + { + public string GetString(); + + public void SetString(string value); + + public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs new file mode 100644 index 00000000000000..de5aea6228a144 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace SharedTypes.ComInterfaces +{ + internal class ManagedComMethodFailureException : Exception + { + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs new file mode 100644 index 00000000000000..0d331441e7dd9d --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + [GeneratedComInterface] + [Guid("A4857395-06FB-4A6E-81DB-35461BE999C5")] + internal partial interface ICollectionMarshallingFails + { + [return: MarshalUsing(ConstantElementCount = 10)] + [return: MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + public int[] GetConstSize(); + + [return: MarshalUsing(CountElementName = nameof(size))] + [return: MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + public int[] Get(out int size); + + public void Set( + [MarshalUsing(CountElementName = nameof(size))] + [MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + int[] value, int size); + } + + [GeneratedComClass] + internal partial class ICollectionMarshallingFailsImpl : ICollectionMarshallingFails, ISupportErrorInfo + { + int[] _data = new[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + public int[] Get(out int size) + { + size = _data.Length; + return _data; + } + + [return: MarshalUsing(ConstantElementCount = 10), MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] + public int[] GetConstSize() => new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + public void Set(int[] value, int size) + { + _data = new int[size]; + value.CopyTo(_data, 0); + } + + int ISupportErrorInfo.InterfaceSupportsErrorInfo(in Guid riid) + { + if (riid == typeof(ICollectionMarshallingFails).GUID) + { + return 0; // S_OK + } + return 1; // S_FALSE + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs new file mode 100644 index 00000000000000..1a4eda0dca222f --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + [GeneratedComInterface] + [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] + internal partial interface IJaggedIntArrayMarshallingFails + { + [return: MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + int[][] Get( + [MarshalUsing(CountElementName = nameof(length))] + out int[] widths, + out int length); + + int Get2( + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + out int[][] array, + [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] + out int[] widths); + + [return: MarshalUsing(ConstantElementCount = 10), + MarshalUsing(ElementIndirectionDepth = 1, ConstantElementCount = 10), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + int[][] GetConstSize(); + + void Set( + [MarshalUsing(CountElementName = nameof(length)), + MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), + MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + int[][] array, + [MarshalUsing(CountElementName = nameof(length))] + int[] widths, + int length); + } + + [GeneratedComClass] + internal partial class IJaggedIntArrayMarshallingFailsImpl : IJaggedIntArrayMarshallingFails, ISupportErrorInfo + { + int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; + int[] _widths = new int[] { 3, 2, 4 }; + public int[][] Get(out int[] widths, out int length) + { + widths = _widths; + length = _data.Length; + return _data; + } + public int Get2(out int[][] array, out int[] widths) + { + array = _data; + widths = _widths; + return array.Length; + } + + [return: MarshalUsing(ConstantElementCount = 10), MarshalUsing(ElementIndirectionDepth = 1, ConstantElementCount = 10), MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] + public int[][] GetConstSize() + { + int[][] values = new int[10][]; + for (int i = 0; i < 10; i++) + { + values[i] = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + } + return values; + } + + public void Set(int[][] array, int[] widths, int length) + { + _data = array; + _widths = widths; + } + + int ISupportErrorInfo.InterfaceSupportsErrorInfo(in Guid riid) + { + if (riid == typeof(IJaggedIntArrayMarshallingFails).GUID) + { + return 0; // S_OK + } + return 1; // S_FALSE + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs new file mode 100644 index 00000000000000..5492f147b137e2 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + /// + /// Has methods that marshal a string array in different ways + /// + [GeneratedComInterface(StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshallingFails))] + [Guid("BE11C211-76D5-496F-A117-82F5D13208F7")] + internal partial interface IStringArrayMarshallingFails + { + public void Param([MarshalUsing(ConstantElementCount = 10)] string[] value); + public void InParam([MarshalUsing(ConstantElementCount = 10)] in string[] value); + public void RefParam([MarshalUsing(ConstantElementCount = 10)] ref string[] value); + public void OutParam([MarshalUsing(ConstantElementCount = 10)] out string[] value); + public void ByValueOutParam([MarshalUsing(ConstantElementCount = 10)][Out] string[] value); + public void ByValueInOutParam([MarshalUsing(ConstantElementCount = 10)][In, Out] string[] value); + [return: MarshalUsing(ConstantElementCount = 10)] + public string[] ReturnValue(); + } + + /// + /// Implements IStringArrayMarshallingFails. + /// + [GeneratedComClass] + internal partial class IStringArrayMarshallingFailsImpl : IStringArrayMarshallingFails, ISupportErrorInfo + { + public static string[] StartingStrings { get; } = new string[] { "Hello", "World", "Lorem", "Ipsum", "Dolor", "Sample", "Text", ".Net", "Interop", "string" }; + private string[] _strings = StartingStrings; + public void ByValueInOutParam([In, MarshalUsing(ConstantElementCount = 10), Out] string[] value) => value[0] = _strings[0]; + public void ByValueOutParam([MarshalUsing(ConstantElementCount = 10), Out] string[] value) => value = _strings; + public void InParam([MarshalUsing(ConstantElementCount = 10)] in string[] value) => value[0] = _strings[0]; + public void OutParam([MarshalUsing(ConstantElementCount = 10)] out string[] value) => value = _strings; + public void Param([MarshalUsing(ConstantElementCount = 10)] string[] value) => value[0] = _strings[0]; + public void RefParam([MarshalUsing(ConstantElementCount = 10)] ref string[] value) => value[0] = _strings[0]; + [return: MarshalUsing(ConstantElementCount = 10)] + public string[] ReturnValue() => _strings; + + int ISupportErrorInfo.InterfaceSupportsErrorInfo(in Guid riid) + { + if (riid == typeof(IStringArrayMarshallingFails).GUID) + { + return 0; // S_OK + } + return 1; // S_FALSE + } + } + + /// + /// Marshals and unmarshals elements of string arrays, throwing an exception instead of marshalling the element number + /// + [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshallingFails))] + [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringMarshallingFails))] + [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringMarshallingFails))] + internal static unsafe class StringMarshallingFails + { + static int _marshalledCount = 0; + const int ThrowOnNthMarshalledElement = 4; + public static nint ConvertToUnmanaged(string managed) + { + if (++_marshalledCount == ThrowOnNthMarshalledElement) + { + _marshalledCount = 0; + throw new MarshallingFailureException("This marshaller throws on the Nth element marshalled where N = " + ThrowOnNthMarshalledElement); + } + return (nint)Utf8StringMarshaller.ConvertToUnmanaged(managed); + } + + public static string ConvertToManaged(nint unmanaged) + { + return Utf8StringMarshaller.ConvertToManaged((byte*)unmanaged); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs new file mode 100644 index 00000000000000..b34a824ab394b3 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + [GeneratedComInterface] + [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] + internal partial interface ISupportErrorInfo + { + [PreserveSig] + int InterfaceSupportsErrorInfo(in Guid riid); + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs new file mode 100644 index 00000000000000..b832266e409a46 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + internal class MarshallingFailureException : Exception + { + public MarshallingFailureException() : base() { } + public MarshallingFailureException(string message) : base(message) { } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs new file mode 100644 index 00000000000000..0bfb7e052d74a3 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices.Marshalling; + +namespace SharedTypes.ComInterfaces.MarshallingFails +{ + [CustomMarshaller(typeof(int), MarshalMode.ElementIn, typeof(ThrowOn4thElementMarshalled))] + [CustomMarshaller(typeof(int), MarshalMode.ElementOut, typeof(ThrowOn4thElementMarshalled))] + internal static class ThrowOn4thElementMarshalled + { + static int _marshalledCount = 0; + static int _unmarshalledCount = 0; + public static int FreeCount { get; private set; } + public static nint ConvertToUnmanaged(int managed) + { + if (_marshalledCount++ == 3) + { + _marshalledCount = 0; + throw new MarshallingFailureException("The element was the 4th element (with 0-based index 3)"); + } + return managed; + } + + public static int ConvertToManaged(nint unmanaged) + { + if (_unmarshalledCount++ == 3) + { + _unmarshalledCount = 0; + throw new MarshallingFailureException("The element was the 4th element (with 0-based index 3)"); + } + return (int)unmanaged; + } + public static void Free(nint unmanaged) => ++FreeCount; + } + +} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj new file mode 100644 index 00000000000000..a07418861d50d8 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj @@ -0,0 +1,72 @@ + + + true + $(NetCoreAppCurrent) + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs b/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs new file mode 100644 index 00000000000000..e5b3bb8a4129dd --- /dev/null +++ b/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs @@ -0,0 +1,222 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Security.Cryptography.Pkcs +{ + public sealed partial class CmsRecipient + { + public CmsRecipient(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, System.Security.Cryptography.RSAEncryptionPadding rsaEncryptionPadding) { } + public CmsRecipient(System.Security.Cryptography.Pkcs.SubjectIdentifierType recipientIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, System.Security.Cryptography.RSAEncryptionPadding rsaEncryptionPadding) { } + public System.Security.Cryptography.RSAEncryptionPadding? RSAEncryptionPadding { get { throw null; } } + } + public sealed partial class CmsSigner + { + public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.AsymmetricAlgorithm? privateKey) { } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.SlhDsa? privateKey) { } + public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.RSA? privateKey, System.Security.Cryptography.RSASignaturePadding? signaturePadding) { } + public System.Security.Cryptography.AsymmetricAlgorithm? PrivateKey { get { throw null; } set { } } + public System.Security.Cryptography.RSASignaturePadding? SignaturePadding { get { throw null; } set { } } + } + public sealed partial class ContentInfo + { + public static System.Security.Cryptography.Oid GetContentType(ReadOnlySpan encodedMessage) { throw null; } + } + public sealed partial class EnvelopedCms + { + public void Decode(System.ReadOnlySpan encodedMessage) { } + public void Decrypt(System.Security.Cryptography.Pkcs.RecipientInfo recipientInfo, System.Security.Cryptography.AsymmetricAlgorithm? privateKey) { } + } + public sealed partial class Pkcs12Builder + { + public Pkcs12Builder() { } + public bool IsSealed { get { throw null; } } + public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, byte[]? passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { } + public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { } + public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters) { } + public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, string? password, System.Security.Cryptography.PbeParameters pbeParameters) { } + public void AddSafeContentsUnencrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents) { } + public byte[] Encode() { throw null; } + public void SealWithMac(System.ReadOnlySpan password, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int iterationCount) { } + public void SealWithMac(string? password, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int iterationCount) { } + public void SealWithoutIntegrity() { } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + } + public sealed partial class Pkcs12CertBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag + { + public Pkcs12CertBag(System.Security.Cryptography.Oid certificateType, System.ReadOnlyMemory encodedCertificate) : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } + public System.ReadOnlyMemory EncodedCertificate { get { throw null; } } + public bool IsX509Certificate { get { throw null; } } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + public System.Security.Cryptography.X509Certificates.X509Certificate2 GetCertificate() { throw null; } + public System.Security.Cryptography.Oid GetCertificateType() { throw null; } + } + public enum Pkcs12ConfidentialityMode + { + None = 1, + Password = 2, + PublicKey = 3, + Unknown = 0, + } + public sealed partial class Pkcs12Info + { + internal Pkcs12Info() { } + public System.Collections.ObjectModel.ReadOnlyCollection AuthenticatedSafe { get { throw null; } } + public System.Security.Cryptography.Pkcs.Pkcs12IntegrityMode IntegrityMode { get { throw null; } } + public static System.Security.Cryptography.Pkcs.Pkcs12Info Decode(System.ReadOnlyMemory encodedBytes, out int bytesConsumed, bool skipCopy = false) { throw null; } + public bool VerifyMac(System.ReadOnlySpan password) { throw null; } + public bool VerifyMac(string? password) { throw null; } + } + public enum Pkcs12IntegrityMode + { + None = 1, + Password = 2, + PublicKey = 3, + Unknown = 0, + } + public sealed partial class Pkcs12KeyBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag + { + public Pkcs12KeyBag(System.ReadOnlyMemory pkcs8PrivateKey, bool skipCopy = false) : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } + public System.ReadOnlyMemory Pkcs8PrivateKey { get { throw null; } } + } + public abstract partial class Pkcs12SafeBag + { + protected Pkcs12SafeBag(string bagIdValue, System.ReadOnlyMemory encodedBagValue, bool skipCopy = false) { } + public System.Security.Cryptography.CryptographicAttributeObjectCollection Attributes { get { throw null; } } + public System.ReadOnlyMemory EncodedBagValue { get { throw null; } } + public byte[] Encode() { throw null; } + public System.Security.Cryptography.Oid GetBagId() { throw null; } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + } + public sealed partial class Pkcs12SafeContents + { + public Pkcs12SafeContents() { } + public System.Security.Cryptography.Pkcs.Pkcs12ConfidentialityMode ConfidentialityMode { get { throw null; } } + public bool IsReadOnly { get { throw null; } } + public System.Security.Cryptography.Pkcs.Pkcs12CertBag AddCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { throw null; } + public System.Security.Cryptography.Pkcs.Pkcs12KeyBag AddKeyUnencrypted(System.Security.Cryptography.AsymmetricAlgorithm key) { throw null; } + public System.Security.Cryptography.Pkcs.Pkcs12SafeContentsBag AddNestedContents(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents) { throw null; } + public void AddSafeBag(System.Security.Cryptography.Pkcs.Pkcs12SafeBag safeBag) { } + public System.Security.Cryptography.Pkcs.Pkcs12SecretBag AddSecret(System.Security.Cryptography.Oid secretType, System.ReadOnlyMemory secretValue) { throw null; } + public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, byte[]? passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } + public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } + public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } + public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, string? password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } + public void Decrypt(byte[]? passwordBytes) { } + public void Decrypt(System.ReadOnlySpan passwordBytes) { } + public void Decrypt(System.ReadOnlySpan password) { } + public void Decrypt(string? password) { } + public System.Collections.Generic.IEnumerable GetBags() { throw null; } + } + public sealed partial class Pkcs12SafeContentsBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag + { + internal Pkcs12SafeContentsBag() : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } + public System.Security.Cryptography.Pkcs.Pkcs12SafeContents? SafeContents { get { throw null; } } + } + public sealed partial class Pkcs12SecretBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag + { + internal Pkcs12SecretBag() : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } + public System.ReadOnlyMemory SecretValue { get { throw null; } } + public System.Security.Cryptography.Oid GetSecretType() { throw null; } + } + public sealed partial class Pkcs12ShroudedKeyBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag + { + public Pkcs12ShroudedKeyBag(System.ReadOnlyMemory encryptedPkcs8PrivateKey, bool skipCopy = false) : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } + public System.ReadOnlyMemory EncryptedPkcs8PrivateKey { get { throw null; } } + } + public sealed partial class Pkcs8PrivateKeyInfo + { + public Pkcs8PrivateKeyInfo(System.Security.Cryptography.Oid algorithmId, System.ReadOnlyMemory? algorithmParameters, System.ReadOnlyMemory privateKey, bool skipCopies = false) { } + public System.Security.Cryptography.Oid AlgorithmId { get { throw null; } } + public System.ReadOnlyMemory? AlgorithmParameters { get { throw null; } } + public System.Security.Cryptography.CryptographicAttributeObjectCollection Attributes { get { throw null; } } + public System.ReadOnlyMemory PrivateKeyBytes { get { throw null; } } + public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo Create(System.Security.Cryptography.AsymmetricAlgorithm privateKey) { throw null; } + public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo Decode(System.ReadOnlyMemory source, out int bytesRead, bool skipCopy = false) { throw null; } + public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo DecryptAndDecode(System.ReadOnlySpan passwordBytes, System.ReadOnlyMemory source, out int bytesRead) { throw null; } + public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo DecryptAndDecode(System.ReadOnlySpan password, System.ReadOnlyMemory source, out int bytesRead) { throw null; } + public byte[] Encode() { throw null; } + public byte[] Encrypt(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } + public byte[] Encrypt(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + public bool TryEncrypt(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + public bool TryEncrypt(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } + } + public sealed partial class Pkcs9LocalKeyId : System.Security.Cryptography.Pkcs.Pkcs9AttributeObject + { + public Pkcs9LocalKeyId() { } + public Pkcs9LocalKeyId(byte[] keyId) { } + public Pkcs9LocalKeyId(System.ReadOnlySpan keyId) { } + public System.ReadOnlyMemory KeyId { get { throw null; } } + } + public sealed partial class Rfc3161TimestampRequest + { + internal Rfc3161TimestampRequest() { } + public bool HasExtensions { get { throw null; } } + public System.Security.Cryptography.Oid HashAlgorithmId { get { throw null; } } + public System.Security.Cryptography.Oid? RequestedPolicyId { get { throw null; } } + public bool RequestSignerCertificate { get { throw null; } } + public int Version { get { throw null; } } + public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromData(System.ReadOnlySpan data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } + public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromHash(System.ReadOnlyMemory hash, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } + public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromHash(System.ReadOnlyMemory hash, System.Security.Cryptography.Oid hashAlgorithmId, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } + public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromSignerInfo(System.Security.Cryptography.Pkcs.SignerInfo signerInfo, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } + public byte[] Encode() { throw null; } + public System.Security.Cryptography.X509Certificates.X509ExtensionCollection GetExtensions() { throw null; } + public System.ReadOnlyMemory GetMessageHash() { throw null; } + public System.ReadOnlyMemory? GetNonce() { throw null; } + public System.Security.Cryptography.Pkcs.Rfc3161TimestampToken ProcessResponse(System.ReadOnlyMemory responseBytes, out int bytesConsumed) { throw null; } + public static bool TryDecode(System.ReadOnlyMemory encodedBytes, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest? request, out int bytesConsumed) { throw null; } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + } + public sealed partial class Rfc3161TimestampToken + { + internal Rfc3161TimestampToken() { } + public System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo TokenInfo { get { throw null; } } + public System.Security.Cryptography.Pkcs.SignedCms AsSignedCms() { throw null; } + public static bool TryDecode(System.ReadOnlyMemory encodedBytes, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.Pkcs.Rfc3161TimestampToken? token, out int bytesConsumed) { throw null; } + public bool VerifySignatureForData(System.ReadOnlySpan data, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } + public bool VerifySignatureForHash(System.ReadOnlySpan hash, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } + public bool VerifySignatureForHash(System.ReadOnlySpan hash, System.Security.Cryptography.Oid hashAlgorithmId, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } + public bool VerifySignatureForSignerInfo(System.Security.Cryptography.Pkcs.SignerInfo signerInfo, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } + } + public sealed partial class Rfc3161TimestampTokenInfo + { + public Rfc3161TimestampTokenInfo(System.Security.Cryptography.Oid policyId, System.Security.Cryptography.Oid hashAlgorithmId, System.ReadOnlyMemory messageHash, System.ReadOnlyMemory serialNumber, System.DateTimeOffset timestamp, long? accuracyInMicroseconds = default(long?), bool isOrdering = false, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), System.ReadOnlyMemory? timestampAuthorityName = default(System.ReadOnlyMemory?), System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { } + public long? AccuracyInMicroseconds { get { throw null; } } + public bool HasExtensions { get { throw null; } } + public System.Security.Cryptography.Oid HashAlgorithmId { get { throw null; } } + public bool IsOrdering { get { throw null; } } + public System.Security.Cryptography.Oid PolicyId { get { throw null; } } + public System.DateTimeOffset Timestamp { get { throw null; } } + public int Version { get { throw null; } } + public byte[] Encode() { throw null; } + public System.Security.Cryptography.X509Certificates.X509ExtensionCollection GetExtensions() { throw null; } + public System.ReadOnlyMemory GetMessageHash() { throw null; } + public System.ReadOnlyMemory? GetNonce() { throw null; } + public System.ReadOnlyMemory GetSerialNumber() { throw null; } + public System.ReadOnlyMemory? GetTimestampAuthorityName() { throw null; } + public static bool TryDecode(System.ReadOnlyMemory encodedBytes, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo? timestampTokenInfo, out int bytesConsumed) { throw null; } + public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } + } + public sealed partial class SignedCms + { + public void AddCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + public void Decode(System.ReadOnlySpan encodedMessage) { } + public void RemoveCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + } + public sealed partial class SignerInfo + { + public System.Security.Cryptography.Oid SignatureAlgorithm { get { throw null; } } + public byte[] GetSignature() { throw null; } + public void AddUnsignedAttribute(System.Security.Cryptography.AsnEncodedData unsignedAttribute) { } + public void RemoveUnsignedAttribute(System.Security.Cryptography.AsnEncodedData unsignedAttribute) { } + } + public sealed partial class SubjectIdentifier + { + public bool MatchesCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { throw null; } + } +} diff --git a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs new file mode 100644 index 00000000000000..03c846a15a7706 --- /dev/null +++ b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if NET8_0_OR_GREATER +using System.ComponentModel; +#endif +using System.Runtime.Serialization; + +namespace System.Threading.Channels +{ + /// Exception thrown when a channel is used after it's been closed. + [Serializable] + public partial class ChannelClosedException : InvalidOperationException + { + /// Initializes a new instance of the class with serialized data. + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. +#if NET8_0_OR_GREATER + [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + protected ChannelClosedException(SerializationInfo info, StreamingContext context) : + base(info, context) + { + } + } +} diff --git a/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs b/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs new file mode 100644 index 00000000000000..8dcdd9ef11af4a --- /dev/null +++ b/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +namespace System.Threading.Tasks.Dataflow +{ + public static partial class DataflowBlock + { + public static System.Collections.Generic.IAsyncEnumerable ReceiveAllAsync(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock source, System.Threading.CancellationToken cancellationToken = default) { throw null; } + } + + public sealed partial class TransformManyBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock + { + public TransformManyBlock(System.Func> transform) { } + public TransformManyBlock(System.Func> transform, System.Threading.Tasks.Dataflow.ExecutionDataflowBlockOptions dataflowBlockOptions) { } + } +} diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs new file mode 100644 index 00000000000000..07dcd2a41e3a9b --- /dev/null +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace System.Threading.Tasks.Dataflow +{ + public static partial class DataflowBlock + { + /// Creates an that enables receiving all of the data from the source. + /// Specifies the type of data contained in the source. + /// The source from which to asynchronously receive. + /// The which may be used to cancel the receive operation. + /// The created async enumerable. + /// The is null (Nothing in Visual Basic). + public static IAsyncEnumerable ReceiveAllAsync(this IReceivableSourceBlock source, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(source); + + return Core(source, cancellationToken); + + static async IAsyncEnumerable Core(IReceivableSourceBlock source, [EnumeratorCancellation] CancellationToken cancellationToken) + { + while (await source.OutputAvailableAsync(cancellationToken).ConfigureAwait(false)) + { + while (source.TryReceive(out TOutput? item)) + { + yield return item; + } + } + } + } + } +} diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs new file mode 100644 index 00000000000000..393b7f2fb8e8c9 --- /dev/null +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks.Dataflow.Internal; + +namespace System.Threading.Tasks.Dataflow +{ + public partial class TransformManyBlock + { + /// Initializes the with the specified function. + /// + /// The function to invoke with each data element received. All of the data from the returned + /// will be made available as output from this . + /// + /// The is . + public TransformManyBlock(Func> transform) : + this(transform, ExecutionDataflowBlockOptions.Default) + { + } + + /// Initializes the with the specified function and . + /// + /// The function to invoke with each data element received. All of the data from the returned + /// will be made available as output from this . + /// + /// The options with which to configure this . + /// The or is . + public TransformManyBlock(Func> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) + { + ArgumentNullException.ThrowIfNull(transform); + + Initialize(messageWithId => + { + Task t = ProcessMessageAsync(transform, messageWithId); +#if DEBUG + // Task returned from ProcessMessageAsync is explicitly ignored. + // That function handles all exceptions. + t.ContinueWith(static t => Debug.Assert(t.IsCompletedSuccessfully), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); +#endif + }, dataflowBlockOptions, ref _source, ref _target, ref _reorderingBuffer, TargetCoreOptions.UsesAsyncCompletion); + } + + // Note: + // Enumerating the IAsyncEnumerable is done with ConfigureAwait(true), using the default behavior of + // paying attention to the current context/scheduler. This makes it so that the enumerable code runs on the target scheduler. + // For this to work correctly, there can't be any ConfigureAwait(false) in the same method prior to + // these await foreach loops, nor in the call chain prior to the method invocation. + + /// Processes the message with a user-provided transform function that returns an async enumerable. + /// The transform function to use to process the message. + /// The message to be processed. + private async Task ProcessMessageAsync(Func> transformFunction, KeyValuePair messageWithId) + { + try + { + // Run the user transform and store the results. + IAsyncEnumerable outputItems = transformFunction(messageWithId.Key); + await StoreOutputItemsAsync(messageWithId, outputItems).ConfigureAwait(false); + } + catch (Exception exc) + { + // Enumerating the user's collection failed. If this exception represents cancellation, + // swallow it rather than shutting down the block. + if (!Common.IsCooperativeCancellation(exc)) + { + // The exception was not for cancellation. We must add the exception before declining + // and signaling completion, as the exception is part of the operation, and the completion + // conditions depend on this. + Common.StoreDataflowMessageValueIntoExceptionData(exc, messageWithId.Key); + _target.Complete(exc, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false); + } + } + finally + { + // Let the target know that one of the asynchronous operations it launched has completed. + _target.SignalOneAsyncMessageCompleted(); + } + } + + /// + /// Stores the output items, either into the reordering buffer or into the source half. + /// Ensures that the bounding count is correctly updated. + /// + /// The message with id. + /// The output items to be persisted. + private async Task StoreOutputItemsAsync( + KeyValuePair messageWithId, IAsyncEnumerable? outputItems) + { + // If there's a reordering buffer, pass the data along to it. + // The reordering buffer will handle all details, including bounding. + if (_reorderingBuffer is not null) + { + await StoreOutputItemsReorderedAsync(messageWithId.Value, outputItems).ConfigureAwait(false); + } + // Otherwise, output the data directly. + else if (outputItems is not null) + { + await StoreOutputItemsNonReorderedWithIterationAsync(outputItems).ConfigureAwait(false); + } + else if (_target.IsBounded) + { + // outputItems is null and there's no reordering buffer + // and we're bounding, so decrement the bounding count to + // signify that the input element we already accounted for + // produced no output + _target.ChangeBoundingCount(count: -1); + } + // else there's no reordering buffer, there are no output items, and we're not bounded, + // so there's nothing more to be done. + } + + /// Stores the next item using the reordering buffer. + /// The ID of the item. + /// The async enumerable. + private async Task StoreOutputItemsReorderedAsync(long id, IAsyncEnumerable? item) + { + Debug.Assert(_reorderingBuffer is not null, "Expected a reordering buffer"); + Debug.Assert(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out."); + + // Grab info about the transform + TargetCore target = _target; + bool isBounded = target.IsBounded; + + // Handle invalid items (null enumerables) by delegating to the base + if (item is null) + { + _reorderingBuffer.AddItem(id, null, false); + if (isBounded) + { + target.ChangeBoundingCount(count: -1); + } + return; + } + + // By this point, either we're not the next item, in which case we need to make a copy of the + // data and store it, or we are the next item and can store it immediately but we need to enumerate + // the items and store them individually because we don't want to enumerate while holding a lock. + List? itemCopy = null; + try + { + // If this is the next item, we can output it now. + if (_reorderingBuffer.IsNext(id)) + { + await StoreOutputItemsNonReorderedWithIterationAsync(item).ConfigureAwait(false); + // here itemCopy remains null, so that base.AddItem will finish our interactions with the reordering buffer + } + else + { + // We're not the next item, and we're not trusted, so copy the data into a list. + // We need to enumerate outside of the lock in the base class. + int itemCount = 0; + try + { + itemCopy = new List(); + await foreach (TOutput element in item.ConfigureAwait(true)) + { + itemCopy.Add(element); + } + itemCount = itemCopy.Count; + } + finally + { + // If we're here successfully, then itemCount is the number of output items + // we actually received, and we should update the bounding count with it. + // If we're here because ToList threw an exception, then itemCount will be 0, + // and we still need to update the bounding count with this in order to counteract + // the increased bounding count for the corresponding input. + if (isBounded) + { + UpdateBoundingCountWithOutputCount(count: itemCount); + } + } + } + // else if the item isn't valid, the finally block will see itemCopy as null and output invalid + } + finally + { + // Tell the base reordering buffer that we're done. If we already output + // all of the data, itemCopy will be null, and we just pass down the invalid item. + // If we haven't, pass down the real thing. We do this even in the case of an exception, + // in which case this will be a dummy element. + _reorderingBuffer.AddItem(id, itemCopy, itemIsValid: itemCopy is not null); + } + } + + /// + /// Stores the untrusted async enumerable into the source core. + /// This method does not go through the reordering buffer. + /// + /// The untrusted enumerable. + private async Task StoreOutputItemsNonReorderedWithIterationAsync(IAsyncEnumerable outputItems) + { + // The _source we're adding to isn't thread-safe, so we need to determine + // whether we need to lock. If the block is configured with a max degree + // of parallelism of 1, then only one transform can run at a time, and so + // we don't need to lock. Similarly, if there's a reordering buffer, then + // it guarantees that we're invoked serially, and we don't need to lock. + bool isSerial = + _target.DataflowBlockOptions.MaxDegreeOfParallelism == 1 || + _reorderingBuffer is not null; + + // If we're bounding, we need to increment the bounded count + // for each individual item as we enumerate it. + if (_target.IsBounded) + { + // When the input item that generated this + // output was loaded, we incremented the bounding count. If it only + // output a single a item, then we don't need to touch the bounding count. + // Otherwise, we need to adjust the bounding count accordingly. + bool outputFirstItem = false; + try + { + await foreach (TOutput item in outputItems.ConfigureAwait(true)) + { + if (outputFirstItem) + { + _target.ChangeBoundingCount(count: 1); + } + outputFirstItem = true; + + if (isSerial) + { + _source.AddMessage(item); + } + else + { + lock (ParallelSourceLock) // don't hold lock while enumerating + { + _source.AddMessage(item); + } + } + } + } + finally + { + if (!outputFirstItem) + { + _target.ChangeBoundingCount(count: -1); + } + } + } + // If we're not bounding, just output each individual item. + else + { + if (isSerial) + { + await foreach (TOutput item in outputItems.ConfigureAwait(true)) + { + _source.AddMessage(item); + } + } + else + { + await foreach (TOutput item in outputItems.ConfigureAwait(true)) + { + lock (ParallelSourceLock) // don't hold lock while enumerating + { + _source.AddMessage(item); + } + } + } + } + } + } +} diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index f4f323c52a291a..6f06d5958fd7e2 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -261,9 +261,6 @@ CommonSystem\Experimentals.cs - - - diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.Mono.cs deleted file mode 100644 index 477ee0f08c9c5c..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.Mono.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Threading -{ - internal sealed unsafe partial class LowLevelLifoSemaphore : IDisposable - { - private IntPtr lifo_semaphore; - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern IntPtr InitInternal(); - -#pragma warning disable IDE0060 - private void Create(int maximumSignalCount) -#pragma warning restore IDE0060 - { - lifo_semaphore = InitInternal(); - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void DeleteInternal(IntPtr semaphore); - - public void Dispose() - { - DeleteInternal(lifo_semaphore); - lifo_semaphore = IntPtr.Zero; - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern int TimedWaitInternal(IntPtr semaphore, int timeoutMs); - - private bool WaitCore(int timeoutMs) - { - return TimedWaitInternal(lifo_semaphore, timeoutMs) != 0; - } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern void ReleaseInternal(IntPtr semaphore, int count); - - private void ReleaseCore(int count) - { - ReleaseInternal(lifo_semaphore, count); - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs new file mode 100644 index 00000000000000..cc3f606fe6273d --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading; + +internal sealed partial class PortableThreadPool +{ + private static partial class WorkerThread + { + private static bool IsIOPending => false; + } + + private struct CpuUtilizationReader + { +#pragma warning disable CA1822 + public double CurrentUtilization => 0.0; // FIXME: can we do better +#pragma warning restore CA1822 + } +} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs new file mode 100644 index 00000000000000..7933e49db422b9 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Threading +{ + public static partial class ThreadPool + { + // Indicates that the threadpool should yield the thread from the dispatch loop to the + // runtime periodically. We use this to return back to the JS event loop so that the JS + // event queue can be drained + internal static bool YieldFromDispatchLoop => true; + } +} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs new file mode 100644 index 00000000000000..0d082fd863cb95 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Win32.SafeHandles; + +namespace System.Threading +{ +#if FEATURE_WASM_MANAGED_THREADS +#error when compiled with FEATURE_WASM_MANAGED_THREADS +#endif + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + public sealed class RegisteredWaitHandle : MarshalByRefObject + { + internal RegisteredWaitHandle() + { + } + +#pragma warning disable CA1822 // Mark members as static + internal bool Repeating => false; +#pragma warning restore CA1822 + + public bool Unregister(WaitHandle? waitObject) + { + throw new PlatformNotSupportedException(); + } + } + + public static partial class ThreadPool + { + // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that + // the runtime may use the thread for processing other work + internal static bool YieldFromDispatchLoop => true; + + private const bool IsWorkerTrackingEnabledInConfig = false; + + public static bool SetMaxThreads(int workerThreads, int completionPortThreads) + { + if (workerThreads == 1 && completionPortThreads == 1) + return true; + return false; + } + + public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) + { + workerThreads = 1; + completionPortThreads = 1; + } + + public static bool SetMinThreads(int workerThreads, int completionPortThreads) + { + if (workerThreads == 1 && completionPortThreads == 1) + return true; + return false; + } + + public static void GetMinThreads(out int workerThreads, out int completionPortThreads) + { + workerThreads = 1; + completionPortThreads = 1; + } + + public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) + { + workerThreads = 1; + completionPortThreads = 1; + } + + public static int ThreadCount => 1; + + public static long CompletedWorkItemCount => 0; + + internal static unsafe void RequestWorkerThread() + { + } + + internal static void NotifyWorkItemProgress() + { + } + + internal static bool NotifyThreadBlocked() => false; + + internal static void NotifyThreadUnblocked() + { + } + + internal static object? GetOrCreateThreadLocalCompletionCountObject() => null; + + internal static bool NotifyWorkItemComplete(object? _1, int _2) => true; + + private static RegisteredWaitHandle RegisterWaitForSingleObject( + WaitHandle? waitObject, + WaitOrTimerCallback? callBack, + object? state, + uint millisecondsTimeOutInterval, + bool executeOnlyOnce, + bool flowExecutionContext) + { + throw new PlatformNotSupportedException(); + } + + private static unsafe void NativeOverlappedCallback(nint overlappedPtr) => + IOCompletionCallbackHelper.PerformSingleIOCompletionCallback(0, 0, (NativeOverlapped*)overlappedPtr); + + [CLSCompliant(false)] + [SupportedOSPlatform("windows")] + public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) + { + throw new PlatformNotSupportedException(); + } + + [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] + [SupportedOSPlatform("windows")] + public static bool BindHandle(IntPtr osHandle) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle + } + + [SupportedOSPlatform("windows")] + public static bool BindHandle(SafeHandle osHandle) + { + throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle + } + +#pragma warning disable IDE0060 + [Conditional("unnecessary")] + internal static void ReportThreadStatus(bool isWorking) + { + + } +#pragma warning restore IDE0060 + } +} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs new file mode 100644 index 00000000000000..c1bcfa75b8c2f0 --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public sealed partial class ThreadPoolBoundHandle : IDisposable + { + private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); + } + } +} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs new file mode 100644 index 00000000000000..d5af01ca588caf --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; +using WasiPollWorld.wit.imports.wasi.clocks.v0_2_0; + +namespace System.Threading +{ +#if FEATURE_WASM_MANAGED_THREADS +#error when compiled with FEATURE_WASM_MANAGED_THREADS, we will use TimerQueue.Portable.cs +#endif + // + // Wasi implementation of Timer, single-threaded, on top of pollable and wasi:clocks + // Based on TimerQueue.Portable.cs + // Not thread safe + // + internal sealed partial class TimerQueue + { + private static long TickCount64 => Environment.TickCount64; + private static List? s_scheduledTimers; + private static List? s_scheduledTimersToFire; + private static long s_shortestDueTimeMs = long.MaxValue; + + // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback + private bool _isScheduled; + private long _scheduledDueTimeMs; + private TimerQueue(int _) + { + } + + private static void TimerHandler(object _) + { + try + { + s_shortestDueTimeMs = long.MaxValue; + + long currentTimeMs = TickCount64; + SetNextTimer(PumpTimerQueue(currentTimeMs), currentTimeMs); + } + catch (Exception e) + { + Environment.FailFast("TimerQueue.TimerHandler failed", e); + } + } + + // this is called with shortest of timers scheduled on the particular TimerQueue + private bool SetTimer(uint actualDuration) + { + Debug.Assert((int)actualDuration >= 0); + long currentTimeMs = TickCount64; + if (!_isScheduled) + { + s_scheduledTimers ??= new List(Instances.Length); + s_scheduledTimersToFire ??= new List(Instances.Length); + s_scheduledTimers.Add(this); + _isScheduled = true; + } + + _scheduledDueTimeMs = currentTimeMs + (int)actualDuration; + + SetNextTimer(ShortestDueTime(), currentTimeMs); + + return true; + } + + // shortest time of all TimerQueues + private static unsafe void SetNextTimer(long shortestDueTimeMs, long currentTimeMs) + { + if (shortestDueTimeMs == long.MaxValue) + { + return; + } + + // this also covers s_shortestDueTimeMs = long.MaxValue when none is scheduled + if (s_shortestDueTimeMs > shortestDueTimeMs) + { + s_shortestDueTimeMs = shortestDueTimeMs; + ulong shortestWaitMs = (ulong)Math.Max((long)(shortestDueTimeMs - currentTimeMs), 0); + + // `SubscribeDuration` expects nanoseconds: + var pollable = MonotonicClockInterop.SubscribeDuration(shortestWaitMs * 1000 * 1000); + Task task = WasiEventLoop.RegisterWasiPollable(pollable, true, CancellationToken.None); + task.ContinueWith(TimerHandler, TaskScheduler.Default); + } + } + + private static long ShortestDueTime() + { + if (s_scheduledTimers == null) + { + return long.MaxValue; + } + + long shortestDueTimeMs = long.MaxValue; + var timers = s_scheduledTimers!; + for (int i = timers.Count - 1; i >= 0; --i) + { + TimerQueue timer = timers[i]; + if (timer._scheduledDueTimeMs < shortestDueTimeMs) + { + shortestDueTimeMs = timer._scheduledDueTimeMs; + } + } + + return shortestDueTimeMs; + } + + private static long PumpTimerQueue(long currentTimeMs) + { + if (s_scheduledTimersToFire == null) + { + return ShortestDueTime(); + } + + List timersToFire = s_scheduledTimersToFire!; + List timers; + timers = s_scheduledTimers!; + long shortestDueTimeMs = long.MaxValue; + for (int i = timers.Count - 1; i >= 0; --i) + { + TimerQueue timer = timers[i]; + long waitDurationMs = timer._scheduledDueTimeMs - currentTimeMs; + if (waitDurationMs <= 0) + { + timer._isScheduled = false; + timersToFire.Add(timer); + + int lastIndex = timers.Count - 1; + if (i != lastIndex) + { + timers[i] = timers[lastIndex]; + } + timers.RemoveAt(lastIndex); + continue; + } + + if (timer._scheduledDueTimeMs < shortestDueTimeMs) + { + shortestDueTimeMs = timer._scheduledDueTimeMs; + } + } + + if (timersToFire.Count > 0) + { + foreach (TimerQueue timerToFire in timersToFire) + { + timerToFire.FireNextTimers(); + } + timersToFire.Clear(); + } + + return shortestDueTimeMs; + } + } +} diff --git a/src/mono/mono/metadata/icall-decl.h b/src/mono/mono/metadata/icall-decl.h index ea8336b4f7bc0f..387282931aa5a1 100644 --- a/src/mono/mono/metadata/icall-decl.h +++ b/src/mono/mono/metadata/icall-decl.h @@ -181,11 +181,6 @@ ICALL_EXPORT void ves_icall_Mono_SafeStringMarshal_GFree (void *c_str); ICALL_EXPORT char* ves_icall_Mono_SafeStringMarshal_StringToUtf8 (MonoString *volatile* s); ICALL_EXPORT MonoType* ves_icall_Mono_RuntimeClassHandle_GetTypeFromClass (MonoClass *klass); -ICALL_EXPORT gpointer ves_icall_System_Threading_LowLevelLifoSemaphore_InitInternal (void); -ICALL_EXPORT void ves_icall_System_Threading_LowLevelLifoSemaphore_DeleteInternal (gpointer sem_ptr); -ICALL_EXPORT gint32 ves_icall_System_Threading_LowLevelLifoSemaphore_TimedWaitInternal (gpointer sem_ptr, gint32 timeout_ms); -ICALL_EXPORT void ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseInternal (gpointer sem_ptr, gint32 count); - #ifdef TARGET_AMD64 ICALL_EXPORT void ves_icall_System_Runtime_Intrinsics_X86_X86Base___cpuidex (int abcd[4], int function_id, int subfunction_id); #endif diff --git a/src/mono/mono/metadata/icall-def.h b/src/mono/mono/metadata/icall-def.h index 3ab137aa253788..fd135f5046fcf6 100644 --- a/src/mono/mono/metadata/icall-def.h +++ b/src/mono/mono/metadata/icall-def.h @@ -568,13 +568,6 @@ NOHANDLES(ICALL(ILOCK_21, "Increment(long&)", ves_icall_System_Threading_Interlo NOHANDLES(ICALL(ILOCK_22, "MemoryBarrierProcessWide", ves_icall_System_Threading_Interlocked_MemoryBarrierProcessWide)) NOHANDLES(ICALL(ILOCK_23, "Read(long&)", ves_icall_System_Threading_Interlocked_Read_Long)) -ICALL_TYPE(LIFOSEM, "System.Threading.LowLevelLifoSemaphore", LIFOSEM_1) -NOHANDLES(ICALL(LIFOSEM_1, "DeleteInternal", ves_icall_System_Threading_LowLevelLifoSemaphore_DeleteInternal)) -NOHANDLES(ICALL(LIFOSEM_2, "InitInternal", ves_icall_System_Threading_LowLevelLifoSemaphore_InitInternal)) -NOHANDLES(ICALL(LIFOSEM_3, "ReleaseInternal", ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseInternal)) -NOHANDLES(ICALL(LIFOSEM_4, "TimedWaitInternal", ves_icall_System_Threading_LowLevelLifoSemaphore_TimedWaitInternal)) - - ICALL_TYPE(MONIT, "System.Threading.Monitor", MONIT_0) HANDLES(MONIT_0, "Enter", ves_icall_System_Threading_Monitor_Monitor_Enter, void, 1, (MonoObject)) HANDLES(MONIT_1, "InternalExit", mono_monitor_exit_icall, void, 1, (MonoObject)) diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index 9100051a13185b..78473d8b3681ca 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -60,7 +60,6 @@ #include #include #include -#include #include #ifdef HAVE_SYS_WAIT_H @@ -4883,30 +4882,3 @@ ves_icall_System_Threading_Thread_GetCurrentOSThreadId (MonoError *error) { return mono_native_thread_os_id_get (); } - -gpointer -ves_icall_System_Threading_LowLevelLifoSemaphore_InitInternal (void) -{ - return (gpointer)mono_lifo_semaphore_init (); -} - -void -ves_icall_System_Threading_LowLevelLifoSemaphore_DeleteInternal (gpointer sem_ptr) -{ - LifoSemaphore *sem = (LifoSemaphore *)sem_ptr; - mono_lifo_semaphore_delete (sem); -} - -gint32 -ves_icall_System_Threading_LowLevelLifoSemaphore_TimedWaitInternal (gpointer sem_ptr, gint32 timeout_ms) -{ - LifoSemaphore *sem = (LifoSemaphore *)sem_ptr; - return mono_lifo_semaphore_timed_wait (sem, timeout_ms); -} - -void -ves_icall_System_Threading_LowLevelLifoSemaphore_ReleaseInternal (gpointer sem_ptr, gint32 count) -{ - LifoSemaphore *sem = (LifoSemaphore *)sem_ptr; - mono_lifo_semaphore_release (sem, count); -} diff --git a/src/mono/mono/utils/CMakeLists.txt b/src/mono/mono/utils/CMakeLists.txt index 8c124a1aaa952f..4940aa4732114e 100644 --- a/src/mono/mono/utils/CMakeLists.txt +++ b/src/mono/mono/utils/CMakeLists.txt @@ -104,8 +104,6 @@ set(utils_common_sources mono-stack-unwinding.h hazard-pointer.c hazard-pointer.h - lifo-semaphore.c - lifo-semaphore.h lock-free-queue.c lock-free-queue.h lock-free-alloc.c diff --git a/src/mono/mono/utils/lifo-semaphore.c b/src/mono/mono/utils/lifo-semaphore.c deleted file mode 100644 index 1f3f6c4410b9f2..00000000000000 --- a/src/mono/mono/utils/lifo-semaphore.c +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include -#include - -#if defined(HOST_BROWSER) && !defined(DISABLE_THREADS) -#include -#include -#endif - -LifoSemaphore * -mono_lifo_semaphore_init (void) -{ - LifoSemaphore *semaphore = g_new0 (LifoSemaphore, 1); - if (semaphore == NULL) - return NULL; - - mono_coop_mutex_init (&semaphore->mutex); - - return semaphore; -} - -void -mono_lifo_semaphore_delete (LifoSemaphore *semaphore) -{ - g_assert (semaphore->head == NULL); - mono_coop_mutex_destroy (&semaphore->mutex); - g_free (semaphore); -} - -int32_t -mono_lifo_semaphore_timed_wait (LifoSemaphore *semaphore, int32_t timeout_ms) -{ - LifoSemaphoreWaitEntry wait_entry = {0}; - - mono_coop_cond_init (&wait_entry.condition); - mono_coop_mutex_lock (&semaphore->mutex); - - if (semaphore->pending_signals > 0) { - --semaphore->pending_signals; - mono_coop_cond_destroy (&wait_entry.condition); - mono_coop_mutex_unlock (&semaphore->mutex); - return 1; - } - - // Enqueue out entry into the LIFO wait list - wait_entry.previous = NULL; - wait_entry.next = semaphore->head; - if (semaphore->head != NULL) - semaphore->head->previous = &wait_entry; - semaphore->head = &wait_entry; - - // Wait for a signal or timeout - int wait_error = 0; - do { - wait_error = mono_coop_cond_timedwait (&wait_entry.condition, &semaphore->mutex, timeout_ms); - } while (wait_error == 0 && !wait_entry.signaled); - - if (wait_error == -1) { - if (semaphore->head == &wait_entry) - semaphore->head = wait_entry.next; - if (wait_entry.next != NULL) - wait_entry.next->previous = wait_entry.previous; - if (wait_entry.previous != NULL) - wait_entry.previous->next = wait_entry.next; - } - - mono_coop_cond_destroy (&wait_entry.condition); - mono_coop_mutex_unlock (&semaphore->mutex); - - return wait_entry.signaled; -} - -void -mono_lifo_semaphore_release (LifoSemaphore *semaphore, uint32_t count) -{ - mono_coop_mutex_lock (&semaphore->mutex); - - while (count > 0) { - LifoSemaphoreWaitEntry *wait_entry = semaphore->head; - if (wait_entry != NULL) { - semaphore->head = wait_entry->next; - if (semaphore->head != NULL) - semaphore->head->previous = NULL; - wait_entry->previous = NULL; - wait_entry->next = NULL; - wait_entry->signaled = 1; - mono_coop_cond_signal (&wait_entry->condition); - --count; - } else { - semaphore->pending_signals += count; - count = 0; - } - } - - mono_coop_mutex_unlock (&semaphore->mutex); -} diff --git a/src/mono/mono/utils/lifo-semaphore.h b/src/mono/mono/utils/lifo-semaphore.h deleted file mode 100644 index ad0492c6defb30..00000000000000 --- a/src/mono/mono/utils/lifo-semaphore.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __MONO_LIFO_SEMAPHORE_H__ -#define __MONO_LIFO_SEMAPHORE_H__ - -#include - -typedef struct _LifoSemaphore LifoSemaphore; -typedef struct _LifoSemaphoreWaitEntry LifoSemaphoreWaitEntry; - -struct _LifoSemaphoreWaitEntry { - LifoSemaphoreWaitEntry *previous; - LifoSemaphoreWaitEntry *next; - MonoCoopCond condition; - int signaled; -}; - -struct _LifoSemaphore { - MonoCoopMutex mutex; - uint32_t pending_signals; - LifoSemaphoreWaitEntry *head; -}; - -LifoSemaphore * -mono_lifo_semaphore_init (void); - -void -mono_lifo_semaphore_delete (LifoSemaphore *semaphore); - -int32_t -mono_lifo_semaphore_timed_wait (LifoSemaphore *semaphore, int32_t timeout_ms); - -void -mono_lifo_semaphore_release (LifoSemaphore *semaphore, uint32_t count); - -#endif // __MONO_LIFO_SEMAPHORE_H__ From ec1c5eb716882890111940144d5c8bbcf81f577e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 22 Jul 2025 15:41:18 +0000 Subject: [PATCH 06/33] Hook up the "thread exiting" callback --- .../src/System/Threading/Thread.CoreCLR.cs | 9 +++++++++ src/coreclr/vm/corelib.h | 1 + src/coreclr/vm/threads.cpp | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index df8bd21b98d119..4aecd3ab6ec2c0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -557,6 +557,15 @@ WaitSubsystem.ThreadWaitInfo AllocateWaitInfo() } #endif + private void OnThreadExiting() + { +#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI + // Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by + // the thread. + _waitInfo?.OnThreadExiting(); +#endif + } + [StructLayout(LayoutKind.Sequential)] private struct NativeThreadClass { diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 0b979353253f72..8befc7bbf119c1 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -969,6 +969,7 @@ DEFINE_CLASS(DIRECTONTHREADLOCALDATA, Threading, Thread+DirectOnThreadLocalData) DEFINE_CLASS(THREAD, Threading, Thread) DEFINE_METHOD(THREAD, START_CALLBACK, StartCallback, IM_RetVoid) DEFINE_METHOD(THREAD, POLLGC, PollGC, NoSig) +DEFINE_METHOD(THREAD, ON_THREAD_EXITING, OnThreadExiting, IM_RetVoid) #ifdef FEATURE_OBJCMARSHAL DEFINE_CLASS(AUTORELEASEPOOL, Threading, AutoreleasePool) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 92344beb931331..4f82f34f45eb0f 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2725,6 +2725,15 @@ void Thread::CooperativeCleanup() GCX_COOP(); + if (!IsGCSpecial()) + { + // Allow managed subsystems to clean up on thread exit. + PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__ON_THREAD_EXITING); + DECLARE_ARGHOLDER_ARRAY(args, 1); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(GetExposedObject()); + CALL_MANAGED_METHOD_NORET(args); + } + // Clear any outstanding stale EH state that maybe still active on the thread. #ifdef FEATURE_EH_FUNCLETS ExInfo::PopTrackers((void*)-1); From e92905fb5f51d1b2849ee0e2c8d9ea95fe8e4758 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 29 Jul 2025 21:07:49 +0000 Subject: [PATCH 07/33] Delete Mutex support from the PAL --- .../dlls/mscordac/mscordac_unixexports.src | 2 - src/coreclr/pal/inc/pal.h | 28 -- src/coreclr/pal/src/CMakeLists.txt | 1 - src/coreclr/pal/src/include/pal/corunix.hpp | 1 - src/coreclr/pal/src/include/pal/mutex.hpp | 54 --- .../pal/src/include/pal/synchobjects.hpp | 1 - .../pal/src/include/pal/threadsusp.hpp | 1 - .../pal/src/synchmgr/synchcontrollers.cpp | 4 - src/coreclr/pal/src/synchmgr/wait.cpp | 7 - src/coreclr/pal/src/synchobj/mutex.cpp | 406 ------------------ src/coreclr/pal/src/thread/thread.cpp | 1 - src/coreclr/pal/src/thread/threadsusp.cpp | 49 ++- src/coreclr/pal/tests/palsuite/CMakeLists.txt | 20 - .../pal/tests/palsuite/compilableTests.txt | 11 - .../mutex/nonshared/main.cpp | 229 ---------- .../mutex/nonshared/mutex.cpp | 326 -------------- .../object_management/mutex/shared/main.cpp | 264 ------------ .../object_management/mutex/shared/mutex.cpp | 342 --------------- .../composite/object_management/readme.txt | 8 +- .../tests/palsuite/composite/wfmo/main.cpp | 238 ---------- .../tests/palsuite/composite/wfmo/mutex.cpp | 350 --------------- .../tests/palsuite/composite/wfmo/readme.txt | 22 - .../pal/tests/palsuite/paltestlist.txt | 7 - .../palsuite/paltestlist_to_be_reviewed.txt | 4 - .../threading/CreateEventW/test1/test1.cpp | 92 ---- .../threading/CreateEventW/test2/test2.cpp | 84 ---- .../threading/CreateEventW/test3/test3.cpp | 231 ---------- .../test1/CreateMutexW.cpp | 343 --------------- .../test2/CreateMutexW.cpp | 165 ------- .../DuplicateHandle/test11/childprocess.cpp | 73 ---- .../DuplicateHandle/test11/myexitcode.h | 12 - .../DuplicateHandle/test11/test11.cpp | 321 -------------- .../threading/DuplicateHandle/test4/test4.cpp | 238 ---------- .../threading/OpenEventW/test4/test4.cpp | 111 ----- .../OpenProcess/test1/childProcess.cpp | 30 -- .../threading/OpenProcess/test1/test1.cpp | 41 -- .../ReleaseMutex/test3/ReleaseMutex.cpp | 102 ----- .../SignalObjectAndWaitTest.cpp | 55 +-- .../WaitForMultipleObjectsEx/test3/test3.cpp | 105 ----- .../WaitForMultipleObjectsEx/test4/test4.cpp | 100 ----- .../WFSOExMutexTest/WFSOExMutexTest.cpp | 213 --------- .../WFSOMutexTest/WFSOMutexTest.cpp | 183 -------- 42 files changed, 52 insertions(+), 4823 deletions(-) delete mode 100644 src/coreclr/pal/src/include/pal/mutex.hpp delete mode 100644 src/coreclr/pal/src/synchobj/mutex.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/main.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/wfmo/main.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/wfmo/mutex.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/wfmo/readme.txt delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateEventW/test1/test1.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateEventW/test2/test2.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateEventW/test3/test3.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h delete mode 100644 src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/OpenEventW/test4/test4.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.cpp diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 0b53a46fbb72a6..2d23a21d23a6fc 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -64,8 +64,6 @@ nativeStringResourceTable_mscorrc #CreateFileMappingW #CreateFileA #CreateFileW -#CreateMutexW -#CreateMutexExW #CreateEventW #CreateEventExW #CreateProcessW diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 810baeb4f44af8..63ec2e9ffd4419 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -742,34 +742,6 @@ OpenEventW( #define OpenEvent OpenEventW #endif -PALIMPORT -HANDLE -PALAPI -CreateMutexW( - IN LPSECURITY_ATTRIBUTES lpMutexAttributes, - IN BOOL bInitialOwner, - IN LPCWSTR lpName); - -PALIMPORT -HANDLE -PALAPI -CreateMutexExW( - IN LPSECURITY_ATTRIBUTES lpMutexAttributes, - IN LPCWSTR lpName, - IN DWORD dwFlags, - IN DWORD dwDesiredAccess); - -// CreateMutexExW: dwFlags -#define CREATE_MUTEX_INITIAL_OWNER ((DWORD)0x1) - -#define CreateMutex CreateMutexW - -PALIMPORT -BOOL -PALAPI -ReleaseMutex( - IN HANDLE hMutex); - PALIMPORT DWORD PALAPI diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index 00e0b770d51080..f2ff28312ecdb1 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -200,7 +200,6 @@ set(SOURCES safecrt/wmakepath_s.cpp synchobj/event.cpp synchobj/semaphore.cpp - synchobj/mutex.cpp synchmgr/synchcontrollers.cpp synchmgr/synchmanager.cpp synchmgr/wait.cpp diff --git a/src/coreclr/pal/src/include/pal/corunix.hpp b/src/coreclr/pal/src/include/pal/corunix.hpp index 3bb843cdfa374f..69f4071752757f 100644 --- a/src/coreclr/pal/src/include/pal/corunix.hpp +++ b/src/coreclr/pal/src/include/pal/corunix.hpp @@ -159,7 +159,6 @@ namespace CorUnix { otiAutoResetEvent = 0, otiManualResetEvent, - otiMutex, otiSemaphore, otiFile, otiFileMapping, diff --git a/src/coreclr/pal/src/include/pal/mutex.hpp b/src/coreclr/pal/src/include/pal/mutex.hpp deleted file mode 100644 index c1f9aab7b08cb9..00000000000000 --- a/src/coreclr/pal/src/include/pal/mutex.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - - - -Module Name: - - mutex.hpp - -Abstract: - - Mutex object structure definition. - - - ---*/ - -#ifndef _PAL_MUTEX_H_ -#define _PAL_MUTEX_H_ - -#include "corunix.hpp" - -#include - -namespace CorUnix -{ - extern CObjectType otMutex; - - PAL_ERROR - InternalCreateMutex( - CPalThread *pThread, - LPSECURITY_ATTRIBUTES lpMutexAttributes, - BOOL bInitialOwner, - HANDLE *phMutex - ); - - PAL_ERROR - InternalReleaseMutex( - CPalThread *pThread, - HANDLE hMutex - ); -} - -#define SYNCSPINLOCK_F_ASYMMETRIC 1 - -#define SPINLOCKInit(lock) (*(lock) = 0) -#define SPINLOCKDestroy SPINLOCKInit - -void SPINLOCKAcquire (LONG * lock, unsigned int flags); -void SPINLOCKRelease (LONG * lock); -DWORD SPINLOCKTryAcquire (LONG * lock); -#endif //_PAL_MUTEX_H_ diff --git a/src/coreclr/pal/src/include/pal/synchobjects.hpp b/src/coreclr/pal/src/include/pal/synchobjects.hpp index 5adb4ca34d7e23..e984e600ad03db 100644 --- a/src/coreclr/pal/src/include/pal/synchobjects.hpp +++ b/src/coreclr/pal/src/include/pal/synchobjects.hpp @@ -21,7 +21,6 @@ Module Name: #include "corunix.hpp" #include "threadinfo.hpp" -#include "mutex.hpp" #include "list.h" #include diff --git a/src/coreclr/pal/src/include/pal/threadsusp.hpp b/src/coreclr/pal/src/include/pal/threadsusp.hpp index 4608ea372c2f4b..f182a9fd675052 100644 --- a/src/coreclr/pal/src/include/pal/threadsusp.hpp +++ b/src/coreclr/pal/src/include/pal/threadsusp.hpp @@ -24,7 +24,6 @@ Module Name: #include "pal/threadinfo.hpp" #include "pal/thread.hpp" -#include "pal/mutex.hpp" #include "pal/init.h" #if !HAVE_MACH_EXCEPTIONS #include diff --git a/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp b/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp index 614944198d46bd..52f38913d69dcd 100644 --- a/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp +++ b/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp @@ -964,10 +964,6 @@ namespace CorUnix "Conflicting values for SignalCount [%d] and " "OwnershipCount [%d]\n", GetOwnershipCount(), GetSignalCount()); - - _ASSERT_MSG(otiMutex != m_otiObjectTypeId || m_lSignalCount <= 1, - "Mutex with invalid singal count\n"); - return; } diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index 4fa283394639ae..781cf1bce3a34e 100644 --- a/src/coreclr/pal/src/synchmgr/wait.cpp +++ b/src/coreclr/pal/src/synchmgr/wait.cpp @@ -24,7 +24,6 @@ Revision History: #include "pal/synchobjects.hpp" #include "pal/handlemgr.hpp" #include "pal/event.hpp" -#include "pal/mutex.hpp" #include "pal/semaphore.hpp" #include "pal/dbgmsg.h" #include @@ -39,7 +38,6 @@ static PalObjectTypeId sg_rgWaitObjectsIds[] = { otiAutoResetEvent, otiManualResetEvent, - otiMutex, otiSemaphore, otiProcess, otiThread @@ -51,7 +49,6 @@ static PalObjectTypeId sg_rgSignalableObjectIds[] = { otiAutoResetEvent, otiManualResetEvent, - otiMutex, otiSemaphore }; static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, ARRAY_SIZE(sg_rgSignalableObjectIds)); @@ -679,10 +676,6 @@ DWORD CorUnix::InternalSignalObjectAndWait( palError = InternalSetEvent(thread, hObjectToSignal, true /* fSetEvent */); break; - case otiMutex: - palError = InternalReleaseMutex(thread, hObjectToSignal); - break; - case otiSemaphore: palError = InternalReleaseSemaphore(thread, hObjectToSignal, 1 /* lReleaseCount */, nullptr /* lpPreviousCount */); break; diff --git a/src/coreclr/pal/src/synchobj/mutex.cpp b/src/coreclr/pal/src/synchobj/mutex.cpp deleted file mode 100644 index b189f7269d6879..00000000000000 --- a/src/coreclr/pal/src/synchobj/mutex.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - -Module Name: - - mutex.ccpp - -Abstract: - - Implementation of mutex synchroniztion object as described in - the WIN32 API - -Revision History: - ---*/ - -#include "pal/dbgmsg.h" - -SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first - -#include "pal/mutex.hpp" -#include "pal/file.hpp" -#include "pal/thread.hpp" -#include "pal/utils.h" - -#include "../synchmgr/synchmanager.hpp" - -#include -#include - -#include -#include -#include -#include "minipal/time.h" - -using namespace CorUnix; - -/* ------------------- Definitions ------------------------------*/ - -CObjectType CorUnix::otMutex( - otiMutex, - NULL, // No cleanup routine - 0, // No immutable data - NULL, // No immutable data copy routine - NULL, // No immutable data cleanup routine - 0, // No process local data - NULL, // No process local data cleanup routine - CObjectType::WaitableObject, - CObjectType::ObjectCanBeUnsignaled, - CObjectType::ThreadReleaseAltersSignalCount, - CObjectType::OwnershipTracked - ); - -static CAllowedObjectTypes aotMutex(otiMutex); - -/*++ -Function: - CreateMutexW - -Note: - lpMutexAttributes currently ignored: - -- Win32 object security not supported - -- handles to mutex objects are not inheritable - - See MSDN docs on CreateMutexW for all other parameters. ---*/ - -HANDLE -PALAPI -CreateMutexW( - IN LPSECURITY_ATTRIBUTES lpMutexAttributes, - IN BOOL bInitialOwner, - IN LPCWSTR lpName) -{ - _ASSERTE(lpName == nullptr); - HANDLE hMutex = NULL; - PAL_ERROR palError; - CPalThread *pthr = NULL; - - PERF_ENTRY(CreateMutexW); - ENTRY("CreateMutexW(lpMutexAttributes=%p, bInitialOwner=%d\n", - lpMutexAttributes, - bInitialOwner - ); - - pthr = InternalGetCurrentThread(); - - { - palError = InternalCreateMutex( - pthr, - nullptr, // lpMutexAttributes currently ignored - bInitialOwner, - &hMutex - ); - } - - // - // We always need to set last error, even on success: - // we need to protect ourselves from the situation - // where last error is set to ERROR_ALREADY_EXISTS on - // entry to the function - // - - pthr->SetLastError(palError); - - LOGEXIT("CreateMutexW returns HANDLE %p\n", hMutex); - PERF_EXIT(CreateMutexW); - return hMutex; -} - -/*++ -Function: -CreateMutexExW - -Note: -lpMutexAttributes currently ignored: --- Win32 object security not supported --- handles to mutex objects are not inheritable - -Parameters: -See MSDN doc. ---*/ - -HANDLE -PALAPI -CreateMutexExW( - IN LPSECURITY_ATTRIBUTES lpMutexAttributes, - IN LPCWSTR lpName, - IN DWORD dwFlags, - IN DWORD dwDesiredAccess) -{ - return CreateMutexW(lpMutexAttributes, (dwFlags & CREATE_MUTEX_INITIAL_OWNER) != 0, lpName); -} - -/*++ -Function: - InternalCreateMutex - -Note: - lpMutexAttributes currently ignored: - -- Win32 object security not supported - -- handles to mutex objects are not inheritable - -Parameters: - errors -- An optional wrapper for system call errors, for more detailed error information. - pthr -- thread data for calling thread - phEvent -- on success, receives the allocated mutex handle - - See MSDN docs on CreateMutex for all other parameters. ---*/ - -PAL_ERROR -CorUnix::InternalCreateMutex( - CPalThread *pthr, - LPSECURITY_ATTRIBUTES lpMutexAttributes, - BOOL bInitialOwner, - HANDLE *phMutex - ) -{ - CObjectAttributes oa(nullptr, lpMutexAttributes); - PAL_ERROR palError = NO_ERROR; - IPalObject *pobjMutex = NULL; - IPalObject *pobjRegisteredMutex = NULL; - ISynchStateController *pssc = NULL; - HANDLE hMutex = nullptr; - - _ASSERTE(NULL != pthr); - _ASSERTE(NULL != phMutex); - - ENTRY("InternalCreateMutex(pthr=%p, lpMutexAttributes=%p, bInitialOwner=%d" - ", phMutex=%p)\n", - pthr, - lpMutexAttributes, - bInitialOwner, - phMutex - ); - - palError = g_pObjectManager->AllocateObject( - pthr, - &otMutex, - &oa, - &pobjMutex - ); - - if (NO_ERROR != palError) - { - goto InternalCreateMutexExit; - } - - palError = pobjMutex->GetSynchStateController( - pthr, - &pssc - ); - - if (NO_ERROR != palError) - { - ASSERT("Unable to create state controller (%d)\n", palError); - goto InternalCreateMutexExit; - } - - if (bInitialOwner) - { - palError = pssc->SetOwner(pthr); - } - else - { - palError = pssc->SetSignalCount(1); - } - - pssc->ReleaseController(); - - if (NO_ERROR != palError) - { - ASSERT("Unable to set initial mutex state (%d)\n", palError); - goto InternalCreateMutexExit; - } - - palError = g_pObjectManager->RegisterObject( - pthr, - pobjMutex, - &aotMutex, - &hMutex, - &pobjRegisteredMutex - ); - _ASSERTE(palError != ERROR_ALREADY_EXISTS); // Mutexes can't have names - _ASSERTE(palError != NO_ERROR || pobjRegisteredMutex == pobjMutex); - _ASSERTE((palError == NO_ERROR) == (hMutex != nullptr)); - - // When RegisterObject succeeds, the object would have an additional reference from the handle, and one reference is - // released below through pobjRegisteredMutex. When RegisterObject fails, it releases the initial reference to the object. - // Either way, pobjMutex is invalidated by the above call to RegisterObject. - pobjMutex = nullptr; - - if (palError != NO_ERROR) - { - goto InternalCreateMutexExit; - } - - pobjRegisteredMutex->ReleaseReference(pthr); - pobjRegisteredMutex = nullptr; - - *phMutex = hMutex; - hMutex = nullptr; - -InternalCreateMutexExit: - - _ASSERTE(pobjRegisteredMutex == nullptr); - _ASSERTE(hMutex == nullptr); - - if (pobjMutex != nullptr) - { - pobjMutex->ReleaseReference(pthr); - } - - LOGEXIT("InternalCreateMutex returns %i\n", palError); - - return palError; -} - -/*++ -Function: - ReleaseMutex - -Parameters: - See MSDN doc. ---*/ - -BOOL -PALAPI -ReleaseMutex( IN HANDLE hMutex ) -{ - PAL_ERROR palError = NO_ERROR; - CPalThread *pthr = NULL; - - PERF_ENTRY(ReleaseMutex); - ENTRY("ReleaseMutex(hMutex=%p)\n", hMutex); - - pthr = InternalGetCurrentThread(); - - palError = InternalReleaseMutex(pthr, hMutex); - - if (NO_ERROR != palError) - { - pthr->SetLastError(palError); - } - - LOGEXIT("ReleaseMutex returns BOOL %d\n", (NO_ERROR == palError)); - PERF_EXIT(ReleaseMutex); - return (NO_ERROR == palError); -} - -/*++ -Function: - InternalReleaseMutex - -Parameters: - pthr -- thread data for calling thread - - See MSDN docs on ReleaseMutex for all other parameters ---*/ - -PAL_ERROR -CorUnix::InternalReleaseMutex( - CPalThread *pthr, - HANDLE hMutex - ) -{ - PAL_ERROR palError = NO_ERROR; - IPalObject *pobjMutex = NULL; - ISynchStateController *pssc = NULL; - PalObjectTypeId objectTypeId; - - _ASSERTE(NULL != pthr); - - ENTRY("InternalReleaseMutex(pthr=%p, hMutex=%p)\n", - pthr, - hMutex - ); - - palError = g_pObjectManager->ReferenceObjectByHandle( - pthr, - hMutex, - &aotMutex, - &pobjMutex - ); - - if (NO_ERROR != palError) - { - ERROR("Unable to obtain object for handle %p (error %d)!\n", hMutex, palError); - goto InternalReleaseMutexExit; - } - - palError = pobjMutex->GetSynchStateController( - pthr, - &pssc - ); - - if (NO_ERROR != palError) - { - ASSERT("Error %d obtaining synch state controller\n", palError); - goto InternalReleaseMutexExit; - } - - palError = pssc->DecrementOwnershipCount(); - - if (NO_ERROR != palError) - { - ERROR("Error %d decrementing mutex ownership count\n", palError); - goto InternalReleaseMutexExit; - } - -InternalReleaseMutexExit: - - if (NULL != pssc) - { - pssc->ReleaseController(); - } - - if (NULL != pobjMutex) - { - pobjMutex->ReleaseReference(pthr); - } - - LOGEXIT("InternalReleaseMutex returns %i\n", palError); - - return palError; -} - -/* Basic spinlock implementation */ -void SPINLOCKAcquire (LONG * lock, unsigned int flags) -{ - size_t loop_seed = 1, loop_count = 0; - - if (flags & SYNCSPINLOCK_F_ASYMMETRIC) - { - loop_seed = ((size_t)pthread_self() % 10) + 1; - } - while (InterlockedCompareExchange(lock, 1, 0)) - { - if (!(flags & SYNCSPINLOCK_F_ASYMMETRIC) || (++loop_count % loop_seed)) - { -#if PAL_IGNORE_NORMAL_THREAD_PRIORITY - struct timespec tsSleepTime; - tsSleepTime.tv_sec = 0; - tsSleepTime.tv_nsec = 1; - nanosleep(&tsSleepTime, NULL); -#else - sched_yield(); -#endif - } - } - -} - -void SPINLOCKRelease (LONG * lock) -{ - VolatileStore(lock, 0); -} - -DWORD SPINLOCKTryAcquire (LONG * lock) -{ - return InterlockedCompareExchange(lock, 1, 0); - // only returns 0 or 1. -} diff --git a/src/coreclr/pal/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp index ec6920f922cade..fbe9d8501d03f1 100644 --- a/src/coreclr/pal/src/thread/thread.cpp +++ b/src/coreclr/pal/src/thread/thread.cpp @@ -15,7 +15,6 @@ SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do #include "pal/corunix.hpp" #include "pal/context.h" #include "pal/thread.hpp" -#include "pal/mutex.hpp" #include "pal/handlemgr.hpp" #include "pal/seh.hpp" #include "pal/signal.hpp" diff --git a/src/coreclr/pal/src/thread/threadsusp.cpp b/src/coreclr/pal/src/thread/threadsusp.cpp index 867f46b3fa38f2..74462ce4fc8d9f 100644 --- a/src/coreclr/pal/src/thread/threadsusp.cpp +++ b/src/coreclr/pal/src/thread/threadsusp.cpp @@ -22,7 +22,6 @@ Revision History: #include "pal/corunix.hpp" #include "pal/thread.hpp" -#include "pal/mutex.hpp" #include "pal/seh.hpp" #include "pal/init.h" #include "pal/dbgmsg.h" @@ -50,7 +49,53 @@ CONST BYTE WAKEUPCODE=0x2A; suspension mutex or spinlock. The downside is that it restricts us to only performing one suspension or resumption in the PAL at a time. */ #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION -static LONG g_ssSuspensionLock = 0; + +#define SYNCSPINLOCK_F_ASYMMETRIC 1 + +#define SPINLOCKInit(lock) (*(lock) = 0) + +namespace +{ + LONG g_ssSuspensionLock = 0; + + /* Basic spinlock implementation */ + void SPINLOCKAcquire (LONG * lock, unsigned int flags) + { + size_t loop_seed = 1, loop_count = 0; + + if (flags & SYNCSPINLOCK_F_ASYMMETRIC) + { + loop_seed = ((size_t)pthread_self() % 10) + 1; + } + while (InterlockedCompareExchange(lock, 1, 0)) + { + if (!(flags & SYNCSPINLOCK_F_ASYMMETRIC) || (++loop_count % loop_seed)) + { +#if PAL_IGNORE_NORMAL_THREAD_PRIORITY + struct timespec tsSleepTime; + tsSleepTime.tv_sec = 0; + tsSleepTime.tv_nsec = 1; + nanosleep(&tsSleepTime, NULL); +#else + sched_yield(); +#endif + } + } + + } + + void SPINLOCKRelease (LONG * lock) + { + VolatileStore(lock, 0); + } + + DWORD SPINLOCKTryAcquire (LONG * lock) + { + return InterlockedCompareExchange(lock, 1, 0); + // only returns 0 or 1. + } + +} #endif /*++ diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index 5b96ab5fa81568..3a8ca5394f78dc 100644 --- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt @@ -40,16 +40,10 @@ add_executable_clr(paltests #composite/object_management/event/nonshared/main.cpp #composite/object_management/event/shared/event.cpp #composite/object_management/event/shared/main.cpp - #composite/object_management/mutex/nonshared/main.cpp - #composite/object_management/mutex/nonshared/mutex.cpp - #composite/object_management/mutex/shared/main.cpp - #composite/object_management/mutex/shared/mutex.cpp #composite/object_management/semaphore/nonshared/main.cpp #composite/object_management/semaphore/nonshared/semaphore.cpp #composite/object_management/semaphore/shared/main.cpp #composite/object_management/semaphore/shared/semaphore.cpp - #composite/wfmo/main.cpp - #composite/wfmo/mutex.cpp c_runtime/atof/test1/test1.cpp c_runtime/atoi/test1/test1.cpp c_runtime/isalnum/test1/test1.cpp @@ -376,11 +370,6 @@ add_executable_clr(paltests # pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test2_neg/reg_unreg_libraryw_neg.cpp samples/test1/test.cpp samples/test2/test.cpp - threading/CreateEventW/test1/test1.cpp - threading/CreateEventW/test2/test2.cpp - threading/CreateEventW/test3/test3.cpp - threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.cpp - threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.cpp threading/CreateProcessW/test1/childProcess.cpp threading/CreateProcessW/test1/parentProcess.cpp threading/CreateProcessW/test2/childprocess.cpp @@ -393,12 +382,9 @@ add_executable_clr(paltests threading/CreateThread/test3/test3.cpp threading/DuplicateHandle/test1/test1.cpp threading/DuplicateHandle/test10/test10.cpp - threading/DuplicateHandle/test11/childprocess.cpp - threading/DuplicateHandle/test11/test11.cpp threading/DuplicateHandle/test12/test12.cpp threading/DuplicateHandle/test2/test2.cpp threading/DuplicateHandle/test3/test3.cpp - threading/DuplicateHandle/test4/test4.cpp threading/DuplicateHandle/test7/test7.cpp threading/DuplicateHandle/test8/test8.cpp # threading/DuplicateHandle/test9/test9.cpp @@ -419,7 +405,6 @@ add_executable_clr(paltests threading/OpenEventW/test2/test2.cpp threading/OpenEventW/test3/childprocess.cpp threading/OpenEventW/test3/test3.cpp - threading/OpenEventW/test4/test4.cpp threading/OpenEventW/test5/test5.cpp threading/OpenProcess/test1/childProcess.cpp threading/OpenProcess/test1/test1.cpp @@ -431,7 +416,6 @@ add_executable_clr(paltests threading/QueueUserAPC/test5/test5.cpp threading/QueueUserAPC/test6/test6.cpp threading/QueueUserAPC/test7/test7.cpp - threading/ReleaseMutex/test3/ReleaseMutex.cpp threading/releasesemaphore/test1/test.cpp threading/ResetEvent/test1/test1.cpp threading/ResetEvent/test2/test2.cpp @@ -453,15 +437,11 @@ add_executable_clr(paltests threading/WaitForMultipleObjects/test1/test1.cpp threading/WaitForMultipleObjectsEx/test1/test1.cpp threading/WaitForMultipleObjectsEx/test2/test2.cpp - threading/WaitForMultipleObjectsEx/test3/test3.cpp - threading/WaitForMultipleObjectsEx/test4/test4.cpp threading/WaitForMultipleObjectsEx/test5/helper.cpp threading/WaitForMultipleObjectsEx/test5/test5.cpp threading/WaitForSingleObject/test1/test1.cpp - threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.cpp threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.cpp threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.cpp - threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.cpp threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.cpp threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.cpp diff --git a/src/coreclr/pal/tests/palsuite/compilableTests.txt b/src/coreclr/pal/tests/palsuite/compilableTests.txt index 930d5979fa0318..d3f96b642ca5e2 100644 --- a/src/coreclr/pal/tests/palsuite/compilableTests.txt +++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt @@ -278,11 +278,6 @@ pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test1/paltest_pal_registerl pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test2_neg/paltest_reg_unreg_libraryw_neg samples/test1/paltest_samples_test1 samples/test2/paltest_samples_test2 -threading/CreateEventW/test1/paltest_createeventw_test1 -threading/CreateEventW/test2/paltest_createeventw_test2 -threading/CreateEventW/test3/paltest_createeventw_test3 -threading/CreateMutexW_ReleaseMutex/test1/paltest_createmutexw_releasemutex_test1 -threading/CreateMutexW_ReleaseMutex/test2/paltest_createmutexw_releasemutex_test2 threading/CreateProcessW/test1/paltest_createprocessw_test1 threading/CreateProcessW/test2/paltest_createprocessw_test2 threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1 @@ -293,11 +288,9 @@ threading/CreateThread/test2/paltest_createthread_test2 threading/CreateThread/test3/paltest_createthread_test3 threading/DuplicateHandle/test1/paltest_duplicatehandle_test1 threading/DuplicateHandle/test10/paltest_duplicatehandle_test10 -threading/DuplicateHandle/test11/paltest_duplicatehandle_test11 threading/DuplicateHandle/test12/paltest_duplicatehandle_test12 threading/DuplicateHandle/test2/paltest_duplicatehandle_test2 threading/DuplicateHandle/test3/paltest_duplicatehandle_test3 -threading/DuplicateHandle/test4/paltest_duplicatehandle_test4 threading/DuplicateHandle/test7/paltest_duplicatehandle_test7 threading/DuplicateHandle/test8/paltest_duplicatehandle_test8 threading/DuplicateHandle/test9/paltest_duplicatehandle_test9 @@ -315,7 +308,6 @@ threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1 threading/OpenEventW/test1/paltest_openeventw_test1 threading/OpenEventW/test2/paltest_openeventw_test2 threading/OpenEventW/test3/paltest_openeventw_test3 -threading/OpenEventW/test4/paltest_openeventw_test4 threading/OpenEventW/test5/paltest_openeventw_test5 threading/OpenProcess/test1/paltest_openprocess_test1 threading/QueryThreadCycleTime/test1/paltest_querythreadcycletime_test1 @@ -326,7 +318,6 @@ threading/QueueUserAPC/test4/paltest_queueuserapc_test4 threading/QueueUserAPC/test5/paltest_queueuserapc_test5 threading/QueueUserAPC/test6/paltest_queueuserapc_test6 threading/QueueUserAPC/test7/paltest_queueuserapc_test7 -threading/ReleaseMutex/test3/paltest_releasemutex_test3 threading/releasesemaphore/test1/paltest_releasesemaphore_test1 threading/ResetEvent/test1/paltest_resetevent_test1 threading/ResetEvent/test2/paltest_resetevent_test2 @@ -349,11 +340,9 @@ threading/WaitForMultipleObjects/test1/paltest_waitformultipleobjects_test1 threading/WaitForMultipleObjectsEx/test1/paltest_waitformultipleobjectsex_test1 threading/WaitForMultipleObjectsEx/test2/paltest_waitformultipleobjectsex_test2 threading/WaitForMultipleObjectsEx/test3/paltest_waitformultipleobjectsex_test3 -threading/WaitForMultipleObjectsEx/test4/paltest_waitformultipleobjectsex_test4 threading/WaitForMultipleObjectsEx/test5/paltest_waitformultipleobjectsex_test5 threading/WaitForMultipleObjectsEx/test6/paltest_waitformultipleobjectsex_test6 threading/WaitForSingleObject/test1/paltest_waitforsingleobject_test1 -threading/WaitForSingleObject/WFSOExMutexTest/paltest_waitforsingleobject_wfsoexmutextest threading/WaitForSingleObject/WFSOExSemaphoreTest/paltest_waitforsingleobject_wfsoexsemaphoretest threading/WaitForSingleObject/WFSOExThreadTest/paltest_waitforsingleobject_wfsoexthreadtest threading/WaitForSingleObject/WFSOMutexTest/paltest_waitforsingleobject_wfsomutextest diff --git a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.cpp b/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.cpp deleted file mode 100644 index 3878df1fe9ef7b..00000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/main.cpp +++ /dev/null @@ -1,229 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source Code: main.c and mutex.c -** main.c creates process and waits for all processes to get over -** mutex.c creates a mutex and then calls threads which will contend for the mutex -** -** This test is for Object Management Test case for Mutex where Object type is not shareable. -** Algorithm -** o Create PROCESS_COUNT processes. -** o Main Thread of each process creates OBJECT_TYPE Object -** -** Author: ShamitP -**============================================================ -*/ - -#include -#include "resulttime.h" - -/* Test Input Variables */ -unsigned int PROCESS_COUNT = 2; -unsigned int THREAD_COUNT = 20; -unsigned int REPEAT_COUNT = 4000; -unsigned int RELATION_ID = 1001; - - -struct TestStats{ - DWORD operationTime; - unsigned int relationId; - unsigned int processCount; - unsigned int threadCount; - unsigned int repeatCount; - char* buildNumber; - -}; - -int GetParameters( int argc, char **argv) -{ - if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite Object Management Mutex Test\n"); - printf("Usage:\n"); - printf("main\n\t[PROCESS_COUNT [greater than 1] \n"); - printf("\t[THREAD_COUNT [greater than 1] \n"); - printf("\t[REPEAT_COUNT [greater than 1]\n"); - printf("\t[RELATION_ID [greater than 1]\n"); - - - return -1; - } - - PROCESS_COUNT = atoi(argv[1]); - if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - THREAD_COUNT = atoi(argv[2]); - if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n"); - return -1; - } - - return 0; -} - -PALTEST(composite_object_management_mutex_nonshared_paltest_mutex_nonshared, "composite/object_management/mutex/nonshared/paltest_mutex_nonshared") -{ - unsigned int i = 0; - HANDLE hProcess[MAXIMUM_WAIT_OBJECTS]; - HANDLE hMutexHandle[MAXIMUM_WAIT_OBJECTS]; - - STARTUPINFO si[MAXIMUM_WAIT_OBJECTS]; - PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS]; - - const char *ObjName = "Mutex"; - char lpCommandLine[MAX_PATH] = ""; - - int returnCode = 0; - DWORD processReturnCode = 0; - int testReturnCode = PASS; - - char fileName[MAX_PATH]; - FILE *pFile = NULL; - DWORD dwStartTime; - struct TestStats testStats; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - /* Register the start time */ - dwStartTime = (DWORD)minipal_lowres_ticks(); - testStats.relationId = RELATION_ID; - testStats.processCount = PROCESS_COUNT; - testStats.threadCount = THREAD_COUNT; - testStats.repeatCount = REPEAT_COUNT; - testStats.buildNumber = getBuildNumber(); - - - _snprintf(fileName, MAX_PATH, "main_mutex_%d_.txt", RELATION_ID); - pFile = fopen(fileName, "w+"); - if(pFile == NULL) - { - Fail("Error in opening main file for write\n"); - } - - for( i = 0; i < PROCESS_COUNT; i++ ) - { - ZeroMemory( lpCommandLine, MAX_PATH ); - if ( _snprintf( lpCommandLine, MAX_PATH-1, "mutex %d %d %d %d", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID) < 0 ) - { - Fail("Error Insufficient mutex name string length for %s for iteration [%d]\n", ObjName, i); - } - - /* Zero the data structure space */ - ZeroMemory ( &pi[i], sizeof(pi[i]) ); - ZeroMemory ( &si[i], sizeof(si[i]) ); - - /* Set the process flags and standard io handles */ - si[i].cb = sizeof(si[i]); - - if(!CreateProcess( NULL, /* lpApplicationName*/ - lpCommandLine, /* lpCommandLine */ - NULL, /* lpProcessAttributes */ - NULL, /* lpThreadAttributes */ - TRUE, /* bInheritHandles */ - 0, /* dwCreationFlags, */ - NULL, /* lpEnvironment */ - NULL, /* pCurrentDirectory */ - &si[i], /* lpStartupInfo */ - &pi[i] /* lpProcessInformation */ - )) - { - Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError()); - } - else - { - hProcess[i] = pi[i].hProcess; -// Trace("Process created for [%d]\n", i); - - } - - //Create Process - - } - - returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE); - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError()); - testReturnCode = FAIL; - } - - for( i = 0; i < PROCESS_COUNT; i++ ) - { - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) ) - { - Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n", - i, GetLastError() ); - - testReturnCode = FAIL; - } - - if(processReturnCode == FAIL) - { - Trace( "Process [%d] failed and returned FAIL\n", i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hThread)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hProcess) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i); - testReturnCode = FAIL; - } - } - - testStats.operationTime = GetTimeDiff(dwStartTime); - fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber); - if(fclose(pFile)) - { - Trace("Error: fclose failed for pFile\n"); - testReturnCode = FAIL; - } - - if( testReturnCode == PASS) - { - Trace("Test Passed\n"); - } - else - { - Trace("Test Failed\n"); - } - - PAL_Terminate(); - return testReturnCode; -} diff --git a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.cpp b/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.cpp deleted file mode 100644 index d22381c288313d..00000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/nonshared/mutex.cpp +++ /dev/null @@ -1,326 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source Code: main.c and mutex.c -** main.c creates process and waits for all processes to get over -** mutex.c creates a mutex and then calls threads which will contend for the mutex -** -** This test is for Object Management Test case for Mutex where Object type is not shareable. -** Algorithm -** o Create PROCESS_COUNT processes. -** o Main Thread of each process creates OBJECT_TYPE Object -** -** Author: ShamitP -**============================================================ -*/ - -#include -#include "resultbuffer.h" -#include "resulttime.h" - -#define TIMEOUT 5000 -/* Test Input Variables */ -unsigned int USE_PROCESS_COUNT = 0; -unsigned int THREAD_COUNT = 0; -unsigned int REPEAT_COUNT = 0; -unsigned int RELATION_ID = 1001; - -/* Capture statistics at per thread basis */ -struct statistics{ - unsigned int processId; - unsigned int operationsFailed; - unsigned int operationsPassed; - unsigned int operationsTotal; - DWORD operationTime; - unsigned int relationId; -}; - -struct ProcessStats{ - unsigned int processId; - DWORD operationTime; - unsigned int relationId; -}; - -HANDLE StartTestsEvHandle = NULL; -HANDLE hMutexHandle = NULL; - -/* Results Buffer */ -ResultBuffer *resultBuffer = NULL; - -int testStatus; - -void PALAPI Run_Thread_mutex_nonshared(LPVOID lpParam); - -int GetParameters( int argc, char **argv) -{ - if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite Object Management Mutex Test\n"); - printf("Usage:\n"); - printf("mutex\n\t[USE_PROCESS_COUNT ( greater than 1] \n"); - printf("\t[THREAD_COUNT ( greater than 1] \n"); - printf("\t[REPEAT_COUNT ( greater than 1]\n"); - printf("\t[RELATION_ID [greater than 1]\n"); - return -1; - } - - USE_PROCESS_COUNT = atoi(argv[1]); - if( USE_PROCESS_COUNT < 0) - { - printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n"); - return -1; - } - - THREAD_COUNT = atoi(argv[2]); - if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n"); - return -1; - } - - return 0; -} - -PALTEST(composite_object_management_mutex_nonshared_paltest_mutex_nonshared, "composite/object_management/mutex/nonshared/paltest_mutex_nonshared") -{ - unsigned int i = 0; - HANDLE hThread[MAXIMUM_WAIT_OBJECTS]; - DWORD threadId[MAXIMUM_WAIT_OBJECTS]; - - const char sTmpEventName[MAX_PATH] = "StartTestEvent"; - - DWORD dwParam = 0; - - int returnCode = 0; - - /* Variables to capture the file name and the file pointer at thread level*/ - char fileName[MAX_PATH]; - FILE *pFile = NULL; - struct statistics* buffer = NULL; - int statisticsSize = 0; - - /* Variables to capture the file name and the file pointer at process level*/ - char processFileName[MAX_PATH]; - FILE *pProcessFile = NULL; - struct ProcessStats processStats; - DWORD dwStartTime; - - testStatus = PASS; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - /* Register the start time */ - dwStartTime = (DWORD)minipal_lowres_ticks(); - processStats.relationId = RELATION_ID; - processStats.processId = USE_PROCESS_COUNT; - - _snprintf(processFileName, MAX_PATH, "%d_process_mutex_%d_.txt", USE_PROCESS_COUNT,RELATION_ID); - pProcessFile = fopen(processFileName, "w+"); - if(pProcessFile == NULL) - { - Fail("Error in opening process File file for write for process [%d]\n", USE_PROCESS_COUNT); - } - - statisticsSize = sizeof(struct statistics); - - _snprintf(fileName, MAX_PATH, "%d_thread_mutex_%d_.txt", USE_PROCESS_COUNT, RELATION_ID); - pFile = fopen(fileName, "w+"); - if(pFile == NULL) - { - Fail("Error in opening file for write for process [%d]\n", USE_PROCESS_COUNT); - } - // For each thread we will log operations failed (int), passed (int), total (int) - // and number of ticks (DWORD) for the operations - resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize); - - StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/ - TRUE, /* bManualReset */ - FALSE, /* bInitialState */ - NULL - ); /* name of Event */ - - if( StartTestsEvHandle == NULL ) - { - Fail("Error:%d: Unexpected failure " - "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT ); - - } - - /* Create StartTest Event */ - - hMutexHandle = CreateMutex( - NULL, - FALSE, /* bInitialOwner, owns initially */ - NULL - ); - - if( hMutexHandle == NULL) - { - Fail("Unable to create Mutex handle for process id [%d], returned error [%d]\n", i, GetLastError()); - } - /* We already assume that the mutex was created previously*/ - - for( i = 0; i < THREAD_COUNT; i++ ) - { - dwParam = (int) i; - //Create thread - hThread[i] = CreateThread( - NULL, /* no security attributes */ - 0, /* use default stack size */ - (LPTHREAD_START_ROUTINE)Run_Thread_mutex_nonshared,/* thread function */ - (LPVOID)dwParam, /* argument to thread function */ - 0, /* use default creation flags */ - &threadId[i] /* returns the thread identifier*/ - ); - - if(hThread[i] == NULL) - { - Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError()); - } - - } - - if (!SetEvent(StartTestsEvHandle)) - { - Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError()); - } - - /* Test running */ - returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE); - - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError()); - testStatus = FAIL; - } - - processStats.operationTime = GetTimeDiff(dwStartTime); - - /* Write to a file*/ - if(pFile!= NULL) - { - for( i = 0; i < THREAD_COUNT; i++ ) - { - buffer = (struct statistics *)resultBuffer->getResultBuffer(i); - returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId ); - } - } - fclose(pFile); - - fprintf(pProcessFile, "%d,%d,%d\n", USE_PROCESS_COUNT, processStats.operationTime, processStats.relationId ); - fclose(pProcessFile); - - /* Logging for the test case over, clean up the handles */ - - for( i = 0; i < THREAD_COUNT; i++ ) - { - if(!CloseHandle(hThread[i]) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i); - testStatus = FAIL; - } - } - - if(!CloseHandle(StartTestsEvHandle)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT); - testStatus = FAIL; - } - - if(!CloseHandle(hMutexHandle)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hMutexHandle\n", GetLastError(), USE_PROCESS_COUNT); - testStatus = FAIL; - } - - PAL_Terminate(); - return testStatus; -} - -void PALAPI Run_Thread_mutex_nonshared (LPVOID lpParam) -{ - unsigned int i = 0; - DWORD dwWaitResult; - - struct statistics stats; - DWORD dwStartTime; - - stats.relationId = RELATION_ID; - stats.processId = USE_PROCESS_COUNT; - stats.operationsFailed = 0; - stats.operationsPassed = 0; - stats.operationsTotal = 0; - stats.operationTime = 0; - - int Id=(int)lpParam; - - dwWaitResult = WaitForSingleObject( - StartTestsEvHandle, // handle to mutex - TIMEOUT); - - if(dwWaitResult != WAIT_OBJECT_0) - { - Trace("Error while waiting for StartTest Event@ thread %d\n", Id); - testStatus = FAIL; - } - - dwStartTime = (DWORD)minipal_lowres_ticks(); - - for( i = 0; i < REPEAT_COUNT; i++ ) - { - dwWaitResult = WaitForSingleObject( - hMutexHandle, // handle to mutex - TIMEOUT); - - if(dwWaitResult != WAIT_OBJECT_0) - { - stats.operationsFailed += 1; - stats.operationsTotal += 1; - testStatus = FAIL; - continue; - } - if (! ReleaseMutex(hMutexHandle)) - { - // Deal with error. - stats.operationsFailed += 1; - stats.operationsTotal += 1; - // Probably need to have while true loop to attempt to release mutex... - testStatus = FAIL; - continue; - } - - stats.operationsTotal += 1; - stats.operationsPassed += 1; - } - - stats.operationTime = GetTimeDiff(dwStartTime); - if(resultBuffer->LogResult(Id, (char *)&stats)) - { - Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT); - } -} diff --git a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/main.cpp b/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/main.cpp deleted file mode 100644 index c42b95d9f5b381..00000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/main.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** This test is for Object Management Test case for Mutex where Object type is shareable. -** -** Source Code: main.c and mutex.c -** main.c creates a mutex, creates processes and waits for all processes to get over -** mutex.c create threads which will contend for the mutex -** -** This test is for Object Management Test case for Mutex where Object type is not shareable. -** Algorithm -** o Main Process Creates OBJECT_TYPE Object -** o Create PROCESS_COUNT processes aware of the Shared Object -** -** Author: ShamitP -** -** -**============================================================ -*/ - -#include -#include "resulttime.h" - -/* Test Input Variables */ -unsigned int PROCESS_COUNT = 2; -unsigned int THREAD_COUNT = 2; -unsigned int REPEAT_COUNT = 40000; -unsigned int RELATION_ID = 1001; - - -char objectSuffix[MAX_PATH]; - -struct TestStats{ - DWORD operationTime; - unsigned int relationId; - unsigned int processCount; - unsigned int threadCount; - unsigned int repeatCount; - char* buildNumber; - -}; - -int GetParameters( int argc, char **argv) -{ - if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite Object Management event Test\n"); - printf("Usage:\n"); - printf("main\n\t[PROCESS_COUNT (greater than 1)] \n"); - printf("\t[THREAD_COUNT (greater than 1)] \n"); - printf("\t[REPEAT_COUNT (greater than 1)]\n"); - printf("\t[RELATION_ID [greater than 1]\n"); - printf("\t[Object Name Suffix]\n"); - return -1; - } - - PROCESS_COUNT = atoi(argv[1]); - if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - THREAD_COUNT = atoi(argv[2]); - if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n"); - return -1; - } - - if(argc == 6) - { - strncpy(objectSuffix, argv[5], MAX_PATH-1); - } - - return 0; -} - -PALTEST(composite_object_management_mutex_shared_paltest_mutex_shared, "composite/object_management/mutex/shared/paltest_mutex_shared") -{ - unsigned int i = 0; - HANDLE hProcess[MAXIMUM_WAIT_OBJECTS]; - HANDLE hMutexHandle; - - STARTUPINFO si[MAXIMUM_WAIT_OBJECTS]; - PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS]; - - char ObjName[MAX_PATH] = "SHARED_MUTEX"; - char lpCommandLine[MAX_PATH] = ""; - - int returnCode = 0; - DWORD processReturnCode = 0; - int testReturnCode = PASS; - - char fileName[MAX_PATH]; - FILE *pFile = NULL; - DWORD dwStartTime; - struct TestStats testStats; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - ZeroMemory( objectSuffix, MAX_PATH ); - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - if(argc == 5) - { - strncat(ObjName, objectSuffix, MAX_PATH - (sizeof(ObjName) + 1) ); - } - - /* Register the start time */ - dwStartTime = (DWORD)minipal_lowres_ticks(); - testStats.relationId = RELATION_ID; - testStats.processCount = PROCESS_COUNT; - testStats.threadCount = THREAD_COUNT; - testStats.repeatCount = REPEAT_COUNT; - testStats.buildNumber = getBuildNumber(); - - - _snprintf(fileName, MAX_PATH, "main_mutex_%d_.txt", RELATION_ID); - pFile = fopen(fileName, "w+"); - if(pFile == NULL) - { - Fail("Error in opening main file for write\n"); - } - - hMutexHandle = CreateMutex( - NULL, - FALSE, /* bInitialOwner, owns initially */ - ObjName - ); - - if( hMutexHandle == NULL) - { - Fail("Unable to create Mutex handle for Main thread returned error [%d]\n", GetLastError()); - } - - for( i = 0; i < PROCESS_COUNT; i++ ) - { - ZeroMemory( lpCommandLine, MAX_PATH ); - if ( _snprintf( lpCommandLine, MAX_PATH-1, "mutex %d %d %d %d %s", i, THREAD_COUNT, REPEAT_COUNT, RELATION_ID, objectSuffix) < 0 ) - { - Fail ("Error Insufficient mutex name string length for %s for iteration [%d]\n", ObjName, i); - } - - - /* Zero the data structure space */ - ZeroMemory ( &pi[i], sizeof(pi[i]) ); - ZeroMemory ( &si[i], sizeof(si[i]) ); - - /* Set the process flags and standard io handles */ - si[i].cb = sizeof(si[i]); - - //Create Process - if(!CreateProcess( NULL, /* lpApplicationName*/ - lpCommandLine, /* lpCommandLine */ - NULL, /* lpProcessAttributes */ - NULL, /* lpThreadAttributes */ - TRUE, /* bInheritHandles */ - 0, /* dwCreationFlags, */ - NULL, /* lpEnvironment */ - NULL, /* pCurrentDirectory */ - &si[i], /* lpStartupInfo */ - &pi[i] /* lpProcessInformation */ - )) - { - Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError()); - } - else - { - hProcess[i] = pi[i].hProcess; -// Trace("Process created for [%d]\n", i); - - } - } - - returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE); - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError()); - testReturnCode = FAIL; - } - - for( i = 0; i < PROCESS_COUNT; i++ ) - { - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) ) - { - Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n", - i, GetLastError() ); - - testReturnCode = FAIL; - } - - if(processReturnCode == FAIL) - { - Trace( "Process [%d] failed and returned FAIL\n", i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hThread)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hProcess) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i); - testReturnCode = FAIL; - } - } - - testStats.operationTime = GetTimeDiff(dwStartTime); - fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber ); - if(fclose(pFile)) - { - Trace("Error: fclose failed for pFile\n"); - testReturnCode = FAIL; - } - - if(!CloseHandle(hMutexHandle)) - { - Trace("Error:%d: CloseHandle failed for hMutexHandle\n", GetLastError()); - testReturnCode = FAIL; - - } - - if( testReturnCode == PASS) - { - Trace("Test Passed\n"); - } - else - { - Trace("Test Failed\n"); - } - - PAL_Terminate(); - return testReturnCode; -} - diff --git a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.cpp b/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.cpp deleted file mode 100644 index 8bc6645c04f4f1..00000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/object_management/mutex/shared/mutex.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** This test is for Object Management Test case for Mutex where Object type is shareable. -** -** Source Code: main.c and mutex.c -** main.c creates a mutex, creates processes and waits for all processes to get over -** mutex.c create threads which will contend for the mutex -** -** This test is for Object Management Test case for Mutex where Object type is not shareable. -** Algorithm -** o Main Process Creates OBJECT_TYPE Object -** o Create PROCESS_COUNT processes aware of the Shared Object -** -** Author: ShamitP -** -** -**============================================================ -*/ - -#include -#include "resultbuffer.h" -#include "resulttime.h" - -#define TIMEOUT 5000 -/* Test Input Variables */ -unsigned int USE_PROCESS_COUNT = 0; -unsigned int THREAD_COUNT = 0; -unsigned int REPEAT_COUNT = 0; -unsigned int RELATION_ID = 0; - - -/* Capture statistics at per thread basis */ -struct statistics{ - unsigned int processId; - unsigned int operationsFailed; - unsigned int operationsPassed; - unsigned int operationsTotal; - DWORD operationTime; - unsigned int relationId; -}; - -struct ProcessStats{ - unsigned int processId; - DWORD operationTime; - unsigned int relationId; -}; - -HANDLE StartTestsEvHandle = NULL; -HANDLE hMutexHandle = NULL; - -/* Results Buffer */ -ResultBuffer *resultBuffer = NULL; - -int testStatus; - -const char sTmpEventName[MAX_PATH] = "StartTestEvent"; -char objectSuffix[MAX_PATH]; - -void PALAPI Run_Thread_mutex_shared(LPVOID lpParam); - -int GetParameters( int argc, char **argv) -{ - if( (!((argc == 5) || (argc == 6) ) )|| ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite Object Management event Test\n"); - printf("Usage:\n"); - printf("main\n\t[USE_PROCESS_COUNT (greater than 1)] \n"); - printf("\t[THREAD_COUNT (greater than 1)] \n"); - printf("\t[REPEAT_COUNT (greater than 1)]\n"); - printf("\t[RELATION_ID [greater than 1]\n"); - printf("\t[Object Name Suffix]\n"); - - return -1; - } - - USE_PROCESS_COUNT = atoi(argv[1]); - if( USE_PROCESS_COUNT < 0) - { - printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n"); - return -1; - } - - THREAD_COUNT = atoi(argv[2]); - if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n"); - return -1; - } - - if(argc == 6) - { - strncpy(objectSuffix, argv[5], MAX_PATH-1); - } - - return 0; -} - -PALTEST(composite_object_management_mutex_shared_paltest_mutex_shared, "composite/object_management/mutex/shared/paltest_mutex_shared") -{ - unsigned int i = 0; - HANDLE hThread[MAXIMUM_WAIT_OBJECTS]; - DWORD threadId[MAXIMUM_WAIT_OBJECTS]; - - char ObjName[MAX_PATH] = "SHARED_MUTEX"; - DWORD dwParam = 0; - - int returnCode = 0; - - /* Variables to capture the file name and the file pointer*/ - char fileName[MAX_PATH]; - FILE *pFile = NULL; - struct statistics* buffer = NULL; - int statisticsSize = 0; - - /* Variables to capture the file name and the file pointer at process level*/ - char processFileName[MAX_PATH]; - FILE *pProcessFile = NULL; - struct ProcessStats processStats; - DWORD dwStartTime; - - testStatus = PASS; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - ZeroMemory( objectSuffix, MAX_PATH ); - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - if(argc == 5) - { - strncat(ObjName, objectSuffix, MAX_PATH - (sizeof(ObjName) + 1) ); - } - - /* Register the start time */ - dwStartTime = (DWORD)minipal_lowres_ticks(); - processStats.relationId = RELATION_ID; - processStats.processId = USE_PROCESS_COUNT; - - _snprintf(processFileName, MAX_PATH, "%d_process_mutex_%d_.txt", USE_PROCESS_COUNT, RELATION_ID); - pProcessFile = fopen(processFileName, "w+"); - if(pProcessFile == NULL) - { - Fail("Error in opening process File file for write for process [%d]\n", USE_PROCESS_COUNT); - } statisticsSize = sizeof(struct statistics); - - _snprintf(fileName, MAX_PATH, "%d_thread_mutex_%d_.txt", USE_PROCESS_COUNT, RELATION_ID); - pFile = fopen(fileName, "w+"); - if(pFile == NULL) - { - Fail("Error in opening file for write for process [%d]\n", USE_PROCESS_COUNT); - } - // For each thread we will log operations failed (int), passed (int), total (int) - // and number of ticks (DWORD) for the operations - resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize); - - /* Create StartTest Event */ - StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/ - TRUE, /* bManualReset */ - FALSE, /* bInitialState */ - NULL); /* name of Event */ - - if( StartTestsEvHandle == NULL ) - { - Fail("Error:%d: Unexpected failure " - "to create %s Event for process count %d\n", GetLastError(), sTmpEventName, USE_PROCESS_COUNT ); - - } - - hMutexHandle = CreateMutex( - NULL, - FALSE, /* bInitialOwner, owns initially */ - ObjName - ); - - if( (hMutexHandle == NULL)|| (GetLastError() != ERROR_ALREADY_EXISTS)) - { - Fail("Unable to create Mutex handle for process id [%d], returned error [%d], expected ERROR_ALREADY_EXISTS\n", i, GetLastError()); - } - /* We already assume that the mutex was created previously*/ - - for( i = 0; i < THREAD_COUNT; i++ ) - { - dwParam = (int) i; - //Create thread - hThread[i] = CreateThread( - NULL, /* no security attributes */ - 0, /* use default stack size */ - (LPTHREAD_START_ROUTINE)Run_Thread_mutex_shared,/* thread function */ - (LPVOID)dwParam, /* argument to thread function */ - 0, /* use default creation flags */ - &threadId[i] /* returns the thread identifier*/ - ); - - if(hThread[i] == NULL) - { - Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError()); - } - - } - - if (!SetEvent(StartTestsEvHandle)) - { - Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError()); - } - - /* Test running */ - returnCode = WaitForMultipleObjects( THREAD_COUNT, hThread, TRUE, INFINITE); - - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError()); - testStatus = FAIL; - } - - processStats.operationTime = GetTimeDiff(dwStartTime); - - /* Write to a file*/ - if(pFile!= NULL) - { - for( i = 0; i < THREAD_COUNT; i++ ) - { - buffer = (struct statistics *)resultBuffer->getResultBuffer(i); - returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId ); - } - } - fclose(pFile); - - fprintf(pProcessFile, "%d,%d,%d\n", USE_PROCESS_COUNT, processStats.operationTime, processStats.relationId ); - fclose(pProcessFile); - - /* Logging for the test case over, clean up the handles */ - - for( i = 0; i < THREAD_COUNT; i++ ) - { - if(!CloseHandle(hThread[i]) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i); - testStatus = FAIL; - } - } - - if(!CloseHandle(StartTestsEvHandle)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT); - testStatus = FAIL; - } - - if(!CloseHandle(hMutexHandle)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hMutexHandle\n", GetLastError(), USE_PROCESS_COUNT); - testStatus = FAIL; - } - - PAL_Terminate(); - return testStatus; -} - -void PALAPI Run_Thread_mutex_shared (LPVOID lpParam) -{ - unsigned int i = 0; - DWORD dwWaitResult; - - struct statistics stats; - DWORD dwStartTime; - - stats.relationId = RELATION_ID; - stats.processId = USE_PROCESS_COUNT; - stats.operationsFailed = 0; - stats.operationsPassed = 0; - stats.operationsTotal = 0; - stats.operationTime = 0; - - int Id=(int)lpParam; - - dwWaitResult = WaitForSingleObject( - StartTestsEvHandle, // handle to mutex - TIMEOUT); - - if(dwWaitResult != WAIT_OBJECT_0) - { - Trace("Error while waiting for StartTest Event@ thread %d\n", Id); - testStatus = FAIL; - } - - dwStartTime = (DWORD)minipal_lowres_ticks(); - - for( i = 0; i < REPEAT_COUNT; i++ ) - { - dwWaitResult = WaitForSingleObject( - hMutexHandle, // handle to mutex - TIMEOUT); - - if(dwWaitResult != WAIT_OBJECT_0) - { - stats.operationsFailed += 1; - stats.operationsTotal += 1; - testStatus = FAIL; - continue; - } - if (! ReleaseMutex(hMutexHandle)) - { - // Deal with error. - stats.operationsFailed += 1; - stats.operationsTotal += 1; - // Probably need to have while true loop to attempt to release mutex... - testStatus = FAIL; - continue; - } - - stats.operationsTotal += 1; - stats.operationsPassed += 1; - } - stats.operationTime = GetTimeDiff(dwStartTime); - if(resultBuffer->LogResult(Id, (char *)&stats)) - { - Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT); - } -} diff --git a/src/coreclr/pal/tests/palsuite/composite/object_management/readme.txt b/src/coreclr/pal/tests/palsuite/composite/object_management/readme.txt index 6bae5f105d634e..669b61f79fe3d7 100644 --- a/src/coreclr/pal/tests/palsuite/composite/object_management/readme.txt +++ b/src/coreclr/pal/tests/palsuite/composite/object_management/readme.txt @@ -2,8 +2,6 @@ To compile: 1) create a dat file (say object_management.dat) with contents: -PAL,Composite,palsuite\composite\object_management\mutex\nonshared,mutex=main.c mutex.c,,, -PAL,Composite,palsuite\composite\object_management\mutex\shared,mutex=main.c mutex.c,,, PAL,Composite,palsuite\composite\object_management\semaphore\nonshared,semaphore=main.c semaphore.c,,, PAL,Composite,palsuite\composite\object_management\semaphore\shared,semaphore=main.c semaphore.c,,, PAL,Composite,palsuite\composite\object_management\event\nonshared,event=main.c event.c,,, @@ -19,10 +17,10 @@ main [PROCESS_COUNT] [THREAD_COUNT] [REPEAT_COUNT] Output: -The performance numbers will be in _[event|semaphore|mutex].txt -(will be at palsuite\composite\object_management\[mutex|event|semaphore]\[shared|nonshared]\obj[r|c|d] directory if u use rrunmod.pl) +The performance numbers will be in _[event|semaphore].txt +(will be at palsuite\composite\object_management\[event|semaphore]\[shared|nonshared]\obj[r|c|d] directory if u use rrunmod.pl) -So if process_count is 3, you will have files 0_mutex.txt, 1_mutex.txt and so on… +So if process_count is 3, you will have files 0_event.txt, 1_event.txt and so on� For each process txt file created, each row represents a thread data (process id, number of failures, number of pass, total number of repeated operations and an integer that will be used to identify a run diff --git a/src/coreclr/pal/tests/palsuite/composite/wfmo/main.cpp b/src/coreclr/pal/tests/palsuite/composite/wfmo/main.cpp deleted file mode 100644 index e12cb5d596b88e..00000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/wfmo/main.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** Source Code: main.c and mutex.c -** main.c creates process and waits for all processes to get over -** mutex.c creates a mutex and then calls threads which will contend for the mutex -** -** This test is for WFMO Test case for Mutex -** Algorithm -** o Create PROCESS_COUNT processes. -** o Main Thread of each process creates OBJECT_TYPE Object -** -** Author: ShamitP -** -** -**============================================================ -*/ - -#include -#include "resulttime.h" - -/* Test Input Variables */ -unsigned int PROCESS_COUNT = 3; -unsigned int THREAD_COUNT = 30; -unsigned int REPEAT_COUNT = 40; -unsigned int SLEEP_LENGTH = 4; -unsigned int RELATION_ID = 1001; - - - -struct TestStats{ - DWORD operationTime; - unsigned int relationId; - unsigned int processCount; - unsigned int threadCount; - unsigned int repeatCount; - char* buildNumber; - -}; - -int GetParameters( int argc, char **argv) -{ - if( (argc != 6) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite WFMO Test\n"); - printf("Usage:\n"); - printf("main\n\t[PROCESS_COUNT [greater than 0] \n"); - printf("\t[THREAD_COUNT [greater than 0] \n"); - printf("\t[REPEAT_COUNT [greater than 0]\n"); - printf("\t[SLEEP_LENGTH [greater than 0]\n"); - printf("\t[RELATION_ID [greater than 0]\n"); - - - - return -1; - } - - PROCESS_COUNT = atoi(argv[1]); - if( (PROCESS_COUNT < 1) || (PROCESS_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nMain Process:Invalid PROCESS_COUNT number, Pass greater than 1 and less than PROCESS_COUNT %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - THREAD_COUNT = atoi(argv[2]); - if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nMain Process:Invalid REPEAT_COUNT number, Pass greater than 1\n"); - return -1; - } - - SLEEP_LENGTH = atoi(argv[4]); - if( SLEEP_LENGTH < 1) - { - printf("\nMain Process:Invalid SLEEP_LENGTH number, Pass greater than 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[5]); - if( RELATION_ID < 1) - { - printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n"); - return -1; - } - - - - return 0; -} - -PALTEST(composite_wfmo_paltest_composite_wfmo, "composite/wfmo/paltest_composite_wfmo") -{ - unsigned int i = 0; - HANDLE hProcess[MAXIMUM_WAIT_OBJECTS]; - - STARTUPINFO si[MAXIMUM_WAIT_OBJECTS]; - PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS]; - - char lpCommandLine[MAX_PATH] = ""; - - int returnCode = 0; - DWORD processReturnCode = 0; - int testReturnCode = PASS; - - char fileName[MAX_PATH]; - FILE *pFile = NULL; - DWORD dwStartTime; - struct TestStats testStats; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - /* Register the start time */ - dwStartTime = (DWORD)minipal_lowres_ticks(); - testStats.relationId = 0; - testStats.relationId = RELATION_ID; - testStats.processCount = PROCESS_COUNT; - testStats.threadCount = THREAD_COUNT; - testStats.repeatCount = REPEAT_COUNT; - testStats.buildNumber = getBuildNumber(); - - - - _snprintf(fileName, MAX_PATH, "main_wfmo_%d_.txt",testStats.relationId); - pFile = fopen(fileName, "w+"); - if(pFile == NULL) - { - Fail("Error in opening main file for write\n"); - } - - for( i = 0; i < PROCESS_COUNT; i++ ) - { - - ZeroMemory( lpCommandLine, MAX_PATH ); - if ( _snprintf( lpCommandLine, MAX_PATH-1, "mutex %d %d %d %d %d", i, THREAD_COUNT, REPEAT_COUNT, SLEEP_LENGTH, RELATION_ID) < 0 ) - { - Trace ("Error: Insufficient commandline string length for iteration [%d]\n", i); - } - - /* Zero the data structure space */ - ZeroMemory ( &pi[i], sizeof(pi[i]) ); - ZeroMemory ( &si[i], sizeof(si[i]) ); - - /* Set the process flags and standard io handles */ - si[i].cb = sizeof(si[i]); - - //Create Process - if(!CreateProcess( NULL, /* lpApplicationName*/ - lpCommandLine, /* lpCommandLine */ - NULL, /* lpProcessAttributes */ - NULL, /* lpThreadAttributes */ - TRUE, /* bInheritHandles */ - 0, /* dwCreationFlags, */ - NULL, /* lpEnvironment */ - NULL, /* pCurrentDirectory */ - &si[i], /* lpStartupInfo */ - &pi[i] /* lpProcessInformation */ - )) - { - Fail("Process Not created for [%d], the error code is [%d]\n", i, GetLastError()); - } - else - { - hProcess[i] = pi[i].hProcess; - // Trace("Process created for [%d]\n", i); - } - - } - - returnCode = WaitForMultipleObjects( PROCESS_COUNT, hProcess, TRUE, INFINITE); - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", PROCESS_COUNT, returnCode, GetLastError()); - } - - for( i = 0; i < PROCESS_COUNT; i++ ) - { - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) ) - { - Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n", - i, GetLastError() ); - - testReturnCode = FAIL; - } - - if(processReturnCode == FAIL) - { - Trace( "Process [%d] failed and returned FAIL\n", i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hThread)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i); - } - - if(!CloseHandle(pi[i].hProcess) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i); - } - } - - testStats.operationTime = GetTimeDiff(dwStartTime); - fprintf(pFile, "%d,%d,%d,%d,%d,%s\n", testStats.operationTime, testStats.relationId, testStats.processCount, testStats.threadCount, testStats.repeatCount, testStats.buildNumber); - if(fclose(pFile)) - { - Trace("Error: fclose failed for pFile\n"); - testReturnCode = FAIL; - } - - if( testReturnCode == PASS) - { - Trace("Test Passed\n"); - } - else - { - Trace("Test Failed\n"); - } - PAL_Terminate(); - return testReturnCode; -} diff --git a/src/coreclr/pal/tests/palsuite/composite/wfmo/mutex.cpp b/src/coreclr/pal/tests/palsuite/composite/wfmo/mutex.cpp deleted file mode 100644 index ed5de0fafd16ea..00000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/wfmo/mutex.cpp +++ /dev/null @@ -1,350 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -**Source Code: main.c and mutex.c -** main.c creates process and waits for all processes to get over -** mutex.c creates a mutex and then calls threads which will -** contend for the mutex -** -** This test is for WFMO Test case for Mutex -** Algorithm -** o Create PROCESS_COUNT processes. -** o Main Thread of each process creates OBJECT_TYPE Object -** -** Author: ShamitP -** -** -**============================================================ -*/ - -#include -#include "resultbuffer.h" -#include "resulttime.h" - -/* Test Input Variables */ -unsigned int USE_PROCESS_COUNT = 0; -unsigned int THREAD_COUNT = 0; -unsigned int REPEAT_COUNT = 0; -unsigned int SLEEP_LENGTH = 0; -unsigned int RELATION_ID = 1001; - - -/* Capture statistics at per thread basis */ -struct statistics{ - unsigned int processId; - unsigned int operationsFailed; - unsigned int operationsPassed; - unsigned int operationsTotal; - DWORD operationTime; - unsigned int relationId; - -}; - -struct ProcessStats{ - unsigned int processId; - DWORD operationTime; - unsigned int relationId; -}; - -/* Handle to signal threads to start the tests */ -HANDLE StartTestsEvHandle = NULL; -/* Handle to mutex which will be contended by threads */ -HANDLE hMutexHandle = NULL; -/* Results Buffer */ -ResultBuffer *resultBuffer = NULL; - -int testStatus; - -void PALAPI Run_Thread_composite_wfmo(LPVOID lpParam); - -int GetParameters( int argc, char **argv) -{ - if( (argc != 6) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite WFMO Test\n"); - printf("Usage:\n"); - printf("mutex\n\t[USE_PROCESS_COUNT [greater than 0] \n"); - printf("\t[THREAD_COUNT [greater than 0] \n"); - printf("\t[REPEAT_COUNT [greater than 0]\n"); - printf("\t[SLEEP_LENGTH [greater than 0]\n"); - printf("\t[RELATION_ID [greater than 0]\n"); - - return -1; - } - - USE_PROCESS_COUNT = atoi(argv[1]); - if( USE_PROCESS_COUNT < 0) - { - printf("\nInvalid USE_PROCESS_COUNT number, Pass greater than 1\n"); - return -1; - } - - THREAD_COUNT = atoi(argv[2]); - if( (THREAD_COUNT < 1) || (THREAD_COUNT > MAXIMUM_WAIT_OBJECTS) ) - { - printf("\nInvalid THREAD_COUNT number, Pass greater than 1 and less than %d\n", MAXIMUM_WAIT_OBJECTS); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nInvalid REPEAT_COUNT number, Pass greater than 1\n"); - return -1; - } - - SLEEP_LENGTH = atoi(argv[4]); - if( SLEEP_LENGTH < 1) - { - printf("\nMain Process:Invalid SLEEP_LENGTH number, Pass greater than 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[5]); - if( RELATION_ID < 1) - { - printf("\nMain Process:Invalid RELATION_ID number, Pass greater than 1\n"); - return -1; - } - - return 0; -} - -PALTEST(composite_wfmo_paltest_composite_wfmo, "composite/wfmo/paltest_composite_wfmo") -{ - unsigned int i = 0; - HANDLE hThread[MAXIMUM_WAIT_OBJECTS]; - DWORD threadId[MAXIMUM_WAIT_OBJECTS]; - int returnCode = 0; - - DWORD dwParam = 0; - - /* Variables to capture the file name and the file pointer at thread level*/ - char fileName[MAX_PATH]; - FILE *pFile = NULL; - struct statistics* buffer = NULL; - int statisticsSize = 0; - - /* Variables to capture the file name and the file pointer at process level*/ - char processFileName[MAX_PATH]; - FILE *pProcessFile = NULL; - struct ProcessStats processStats; - DWORD dwStartTime; - - testStatus = PASS; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - /* Register the start time */ - dwStartTime = (DWORD)minipal_lowres_ticks(); - processStats.relationId = RELATION_ID; - processStats.processId = USE_PROCESS_COUNT; - - _snprintf(processFileName, MAX_PATH, "%d_process_wfmo_%d_.txt", USE_PROCESS_COUNT, RELATION_ID); - pProcessFile = fopen(processFileName, "w+"); - if(pProcessFile == NULL) - { - Fail("Error:%d: in opening Process File for write for process [%d]\n", GetLastError(), USE_PROCESS_COUNT); - } - - statisticsSize = sizeof(struct statistics); - - _snprintf(fileName, MAX_PATH, "%d_thread_wfmo_%d_.txt", USE_PROCESS_COUNT, RELATION_ID); - pFile = fopen(fileName, "w+"); - if(pFile == NULL) - { - Fail("Error in opening file for write for process [%d], error [%d]\n", USE_PROCESS_COUNT, GetLastError()); - } - // For each thread we will log operations failed (int), passed (int), total (int) - // and number of ticks (DWORD) for the operations - resultBuffer = new ResultBuffer( THREAD_COUNT, statisticsSize); - - StartTestsEvHandle = CreateEvent( NULL, /* lpEventAttributes*/ - TRUE, /* bManualReset */ - FALSE, /* bInitialState */ - NULL); /* name of Event */ - - if( StartTestsEvHandle == NULL ) - { - Fail("Error:%d: Unexpected failure " - "to create start tests Event for process count %d\n", GetLastError(), USE_PROCESS_COUNT ); - - } - - /* Create StartTest Event */ - hMutexHandle = CreateMutex( - NULL, - FALSE, /* bInitialOwner, owns initially */ - NULL - ); - - if( hMutexHandle == NULL) - { - Fail("Unable to create Mutex handle for process id [%d], returned error [%d]\n", i, GetLastError()); - } - - /* We already assume that the mutex was created previously*/ - for( i = 0; i < THREAD_COUNT; i++ ) - { - dwParam = (int) i; - //Create thread - hThread[i] = CreateThread( - NULL, /* no security attributes */ - 0, /* use default stack size */ - (LPTHREAD_START_ROUTINE)Run_Thread_composite_wfmo,/* thread function */ - (LPVOID)dwParam, /* argument to thread function */ - 0, /* use default creation flags */ - &threadId[i] /* returns the thread identifier*/ - ); - - if(hThread[i] == NULL) - { - Fail("Create Thread failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError()); - } - } - - if (!SetEvent(StartTestsEvHandle)) - { - Fail("Set Event for Start Tests failed for %d process, and GetLastError value is %d\n", USE_PROCESS_COUNT, GetLastError()); - } - /* Test running */ - - if( THREAD_COUNT != 1 ) - { - returnCode = WaitForMultipleObjects(THREAD_COUNT, hThread, TRUE, INFINITE); - } - else - { - returnCode = WaitForSingleObject(hThread[0], INFINITE); - } - - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) for %d process returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError()); - testStatus = FAIL; - } - - processStats.operationTime = GetTimeDiff(dwStartTime); - - /* Write to a file*/ - if(pFile!= NULL) - { - for( i = 0; i < THREAD_COUNT; i++ ) - { - buffer = (struct statistics *)resultBuffer->getResultBuffer(i); - returnCode = fprintf(pFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId ); - } - } - if(fclose(pFile)) - { - Trace("Error: fclose failed for pFile at Process %d\n", USE_PROCESS_COUNT); - testStatus = FAIL; - } - - fprintf(pProcessFile, "%d,%d,%d\n", USE_PROCESS_COUNT, processStats.operationTime, processStats.relationId ); - if(fclose(pProcessFile)) - { - Trace("Error: fclose failed for pProcessFile at Process %d\n", USE_PROCESS_COUNT); - testStatus = FAIL; - } - - /* Logging for the test case over, clean up the handles */ - for( i = 0; i < THREAD_COUNT; i++ ) - { - if(!CloseHandle(hThread[i]) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread[%d]\n", GetLastError(), USE_PROCESS_COUNT, i); - testStatus = FAIL; - } - } - - if(!CloseHandle(StartTestsEvHandle)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] StartTestsEvHandle\n", GetLastError(), USE_PROCESS_COUNT); - testStatus = FAIL; - } - - PAL_Terminate(); - return testStatus; -} - -void PALAPI Run_Thread_composite_wfmo (LPVOID lpParam) -{ - unsigned int i = 0; - struct statistics stats; - - DWORD dwWaitResult; - DWORD dwStartTime; - - stats.relationId = RELATION_ID; - stats.processId = USE_PROCESS_COUNT; - stats.operationsFailed = 0; - stats.operationsPassed = 0; - stats.operationsTotal = 0; - stats.operationTime = 0; - - int Id=(int)lpParam; - - dwWaitResult = WaitForSingleObject( - StartTestsEvHandle, // handle to mutex - INFINITE); - - if(dwWaitResult != WAIT_OBJECT_0) - { - Trace("Error:%d: while waiting for StartTest Event@ thread %d\n", GetLastError(), Id); - testStatus = FAIL; - } - - /* Register the start time */ - dwStartTime = (DWORD)minipal_lowres_ticks(); - - /* Run the tests repeat count times */ - for( i = 0; i < REPEAT_COUNT; i++ ) - { - dwWaitResult = WaitForSingleObject( - hMutexHandle, // handle to mutex - INFINITE); - - if(dwWaitResult != WAIT_OBJECT_0) - { - Trace("Error:%d: while waiting for onject @ thread %d, # iter %d\n", GetLastError(), Id, i); - stats.operationsFailed += 1; - stats.operationsTotal += 1; - testStatus = FAIL; - continue; - } - - Sleep(SLEEP_LENGTH); - - if (!ReleaseMutex(hMutexHandle)) - { - // Deal with error. - Trace("Error:%d: while releasing mutex @ thread %d # iter %d\n", GetLastError(), Id, i); - stats.operationsFailed += 1; - stats.operationsTotal += 1; - // do we need to have while true loop to attempt to release mutex...? - testStatus = FAIL; - continue; - } - - stats.operationsTotal += 1; - stats.operationsPassed += 1; - } - - stats.operationTime = GetTimeDiff(dwStartTime); - - if(resultBuffer->LogResult(Id, (char *)&stats)) - { - Fail("Error:%d: while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", GetLastError(), Id, USE_PROCESS_COUNT); - } -} diff --git a/src/coreclr/pal/tests/palsuite/composite/wfmo/readme.txt b/src/coreclr/pal/tests/palsuite/composite/wfmo/readme.txt deleted file mode 100644 index 6be55d8ff42c08..00000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/wfmo/readme.txt +++ /dev/null @@ -1,22 +0,0 @@ -To compile: - -1) create a dat file (say wfmo.dat) with contents: -PAL,Composite,palsuite\composite\wfmo,wfmo=main.c mutex.c,,, - -2) perl rrunmod.pl -r wfmo.dat - - -To execute: -main [PROCESS_COUNT] [THREAD_COUNT] [REPEAT_COUNT] [SLEEP_LENGTH] - -Output: -The performance numbers will be in _wfmo.txt -(will be at palsuite\composite\wfmo\obj[r|c|d] directory if u use rrunmod.pl) - -So if process_count is 3, you will have files 0_wfmo.txt, 1_wfmo.txt and so on… - -For each process txt file created, -each row represents a thread data (process id, number of failures, number of pass, total number of repeated operations and an integer that will be used to identify a run -(currently zero)). - - diff --git a/src/coreclr/pal/tests/palsuite/paltestlist.txt b/src/coreclr/pal/tests/palsuite/paltestlist.txt index 62a943e4726b7c..50a4d063420d8b 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt @@ -248,9 +248,6 @@ miscellaneous/SetLastError/test1/paltest_setlasterror_test1 pal_specific/PAL_Initialize_Terminate/test1/paltest_pal_initialize_terminate_test1 pal_specific/PAL_Initialize_Terminate/test2/paltest_pal_initialize_terminate_test2 samples/test1/paltest_samples_test1 -threading/CreateEventW/test1/paltest_createeventw_test1 -threading/CreateEventW/test2/paltest_createeventw_test2 -threading/CreateMutexW_ReleaseMutex/test1/paltest_createmutexw_releasemutex_test1 threading/CreateProcessW/test1/paltest_createprocessw_test1 threading/CreateProcessW/test2/paltest_createprocessw_test2 threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1 @@ -259,7 +256,6 @@ threading/CreateThread/test1/paltest_createthread_test1 threading/CreateThread/test3/paltest_createthread_test3 threading/DuplicateHandle/test10/paltest_duplicatehandle_test10 threading/DuplicateHandle/test2/paltest_duplicatehandle_test2 -threading/DuplicateHandle/test4/paltest_duplicatehandle_test4 threading/DuplicateHandle/test7/paltest_duplicatehandle_test7 threading/DuplicateHandle/test8/paltest_duplicatehandle_test8 threading/ExitProcess/test1/paltest_exitprocess_test1 @@ -276,7 +272,6 @@ threading/QueueUserAPC/test4/paltest_queueuserapc_test4 threading/QueueUserAPC/test5/paltest_queueuserapc_test5 threading/QueueUserAPC/test6/paltest_queueuserapc_test6 threading/QueueUserAPC/test7/paltest_queueuserapc_test7 -threading/ReleaseMutex/test3/paltest_releasemutex_test3 threading/releasesemaphore/test1/paltest_releasesemaphore_test1 threading/ResetEvent/test1/paltest_resetevent_test1 threading/ResetEvent/test2/paltest_resetevent_test2 @@ -293,9 +288,7 @@ threading/WaitForMultipleObjects/test1/paltest_waitformultipleobjects_test1 threading/WaitForMultipleObjectsEx/test1/paltest_waitformultipleobjectsex_test1 threading/WaitForMultipleObjectsEx/test2/paltest_waitformultipleobjectsex_test2 threading/WaitForMultipleObjectsEx/test3/paltest_waitformultipleobjectsex_test3 -threading/WaitForMultipleObjectsEx/test4/paltest_waitformultipleobjectsex_test4 threading/WaitForSingleObject/test1/paltest_waitforsingleobject_test1 -threading/WaitForSingleObject/WFSOExMutexTest/paltest_waitforsingleobject_wfsoexmutextest threading/WaitForSingleObject/WFSOExSemaphoreTest/paltest_waitforsingleobject_wfsoexsemaphoretest threading/WaitForSingleObject/WFSOExThreadTest/paltest_waitforsingleobject_wfsoexthreadtest threading/WaitForSingleObject/WFSOMutexTest/paltest_waitforsingleobject_wfsomutextest diff --git a/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt b/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt index d861a469b37070..0c512626040870 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt @@ -69,12 +69,9 @@ miscellaneous/IsBadWritePtr/test3/paltest_isbadwriteptr_test3 pal_specific/PAL_get_stdout/test1/paltest_pal_get_stdout_test1 pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test1/paltest_pal_registerlibraryw_unregisterlibraryw_test1 samples/test2/paltest_samples_test2 -threading/CreateEventW/test3/paltest_createeventw_test3 -threading/CreateMutexW_ReleaseMutex/test2/paltest_createmutexw_releasemutex_test2 threading/CreateSemaphoreW_ReleaseSemaphore/test3/paltest_createsemaphorew_releasesemaphore_test3 threading/CreateThread/test2/paltest_createthread_test2 threading/DuplicateHandle/test1/paltest_duplicatehandle_test1 -threading/DuplicateHandle/test11/paltest_duplicatehandle_test11 threading/DuplicateHandle/test12/paltest_duplicatehandle_test12 threading/DuplicateHandle/test3/paltest_duplicatehandle_test3 threading/DuplicateHandle/test9/paltest_duplicatehandle_test9 @@ -85,7 +82,6 @@ threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1 threading/OpenEventW/test1/paltest_openeventw_test1 threading/OpenEventW/test2/paltest_openeventw_test2 threading/OpenEventW/test3/paltest_openeventw_test3 -threading/OpenEventW/test4/paltest_openeventw_test4 threading/OpenEventW/test5/paltest_openeventw_test5 threading/OpenProcess/test1/paltest_openprocess_test1 threading/QueueUserAPC/test1/paltest_queueuserapc_test1 diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test1/test1.cpp deleted file mode 100644 index 361c58d1ad1db1..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test1/test1.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: test1.c -** -** Purpose: Test for CreateEventW -** -** -**=========================================================*/ - -/* - * Note: From the rotor_pal documentation: lpEventAttributes will - * always be NULL, bManualReset can be either TRUE or FALSE, - * bInitialState can be either TRUE or FALSE, the lpName may be - * non-NULL. -*/ -#define UNICODE -#include - -BOOL CreateEventTest_CreateEvent_test1() -{ - BOOL bRet = FALSE; - DWORD dwRet = 0; - - LPSECURITY_ATTRIBUTES lpEventAttributes = NULL; - BOOL bManualReset = TRUE; - BOOL bInitialState = TRUE; - - /* - * Call CreateEvent, and check to ensure the returned HANDLE is a - * valid event HANDLE - */ - - HANDLE hEvent = CreateEventW(lpEventAttributes, - bManualReset, - bInitialState, - NULL); - - if (hEvent != NULL) - { - /* - * Wait for the Object (for 0 time) and ensure that it returns - * the value indicating that the event is signaled. - */ - dwRet = WaitForSingleObject(hEvent,0); - - if (dwRet != WAIT_OBJECT_0) - { - Trace("CreateEventTest:WaitForSingleObject failed (%x)\n", GetLastError()); - } - else - { - /* - * If we make it here, and CloseHandle succeeds, then the - * entire test has passed. Otherwise bRet will still show - * failure - */ - bRet = CloseHandle(hEvent); - - if (!bRet) - { - Trace("CreateEventTest:CloseHandle failed (%x)\n", GetLastError()); - } - } - } - else - { - Trace("CreateEventTest:CreateEvent failed (%x)\n", GetLastError()); - } - - return bRet; -} - -PALTEST(threading_CreateEventW_test1_paltest_createeventw_test1, "threading/CreateEventW/test1/paltest_createeventw_test1") -{ - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - if(!CreateEventTest_CreateEvent_test1()) - { - Fail ("Test failed\n"); - } - - PAL_Terminate(); - return ( PASS ); - -} diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test2/test2.cpp deleted file mode 100644 index c40581718fd569..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test2/test2.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: test2.c -** -** Purpose: Test for CreateEventW. Create the event with the -** initial state being not signaled. Check to ensure that it -** times out when the event is triggered. -** -** -**=========================================================*/ -#define UNICODE -#include - -BOOL CreateEventTest_CreateEvent_test2() -{ - BOOL bRet = FALSE; - DWORD dwRet = 0; - - LPSECURITY_ATTRIBUTES lpEventAttributes = 0; - BOOL bManualReset = TRUE; - BOOL bInitialState = FALSE; - - - /* Create an event with the Initial State set to FALSE */ - - HANDLE hEvent = CreateEventW(lpEventAttributes, - bManualReset, - bInitialState, - NULL); - - if (hEvent != NULL) - { - /* This should ensure that the object is reset, or - non-signaled. - */ - - dwRet = WaitForSingleObject(hEvent,0); - - if (dwRet != WAIT_TIMEOUT) - { - Trace("CloseEventTest:WaitForSingleObject failed (%x)\n", GetLastError()); - } - else - { - /* At this point, we've tested the function with success. - So long as the HANDLE can be closed, this test should - pass. - */ - - bRet = CloseHandle(hEvent); - - if (!bRet) - { - Trace("CloseEventTest:CloseHandle failed (%x)\n", GetLastError()); - } - } - } - else - { - Trace("CloseEventTest:CreateEvent failed (%x)\n", GetLastError()); - } - - return bRet; -} - -PALTEST(threading_CreateEventW_test2_paltest_createeventw_test2, "threading/CreateEventW/test2/paltest_createeventw_test2") -{ - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - if(!CreateEventTest_CreateEvent_test2()) - { - Fail ("Test failed\n"); - } - - PAL_Terminate(); - return ( PASS ); - -} diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test3/test3.cpp deleted file mode 100644 index 559348727b723e..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateEventW/test3/test3.cpp +++ /dev/null @@ -1,231 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: test3.c -** -** Purpose: Tests for CreateEvent. Create an unnamed event, create -** an event with an empty name, create an event with a name longer than -** MAX_PATH, MAX_PATH + 1, create an event with a name already taken -** by a non-event object, create an event with a name already taken -** by an event object. -** -** -**=========================================================*/ -#include - -#define SWAPPTR ((VOID *) (-1)) - -struct testCase -{ - LPSECURITY_ATTRIBUTES lpEventAttributes; - BOOL bManualReset; - BOOL bInitialState; - WCHAR lpName[MAX_PATH + 2]; - DWORD dwNameLen; - DWORD lastError; - BOOL bResult; -}; - -PALTEST(threading_CreateEventW_test3_paltest_createeventw_test3, "threading/CreateEventW/test3/paltest_createeventw_test3") -{ - struct testCase testCases[]= - { - {0, TRUE, FALSE, {'\0'}, 0, ERROR_SUCCESS, PASS}, - {0, TRUE, FALSE, {'\0'}, 5, ERROR_SUCCESS, PASS}, - {0, TRUE, FALSE, {'\0'}, 5, ERROR_ALREADY_EXISTS, PASS}, - {0, TRUE, FALSE, {'\0'}, 6, ERROR_INVALID_HANDLE, PASS}, - {0, TRUE, FALSE, {'\0'}, MAX_PATH - 1 - 60, ERROR_SUCCESS, PASS}, - {0, TRUE, FALSE, {'\0'}, MAX_PATH - 60, ERROR_SUCCESS, PASS}, - }; - - HANDLE hEvent[sizeof(testCases)/sizeof(struct testCase)]; - - DWORD result[sizeof(testCases)/sizeof(struct testCase)]; - - BOOL bRet = TRUE; - WCHAR nonEventName[] = {'a','a','a','a','a','a','\0'}; - char name[MAX_PATH + 2]; - WCHAR *wName; - HANDLE hFMap = NULL; - HANDLE hUnnamedEvent; - DWORD dwRet; - int i; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - hUnnamedEvent = CreateEventW(0, TRUE, FALSE, NULL); - - if ( NULL == hUnnamedEvent ) - { - bRet = FALSE; - Trace ( "PALSUITE ERROR: CreateEventW (%d, %d, %d, NULL) call " - "returned NULL.\nGetLastError returned %u.\n", 0, TRUE, FALSE, - GetLastError()); - goto done; - } - - if (!CloseHandle(hUnnamedEvent)) - { - bRet = FALSE; - Trace("PALSUITE ERROR: CreateEventW: CloseHandle(%lp); call " - "failed\nGetLastError returned '%u'.\n", hUnnamedEvent, - GetLastError()); - } - - /* Create non-event with the same name as one of the testCases */ - hFMap = CreateFileMappingW( SWAPPTR, NULL, PAGE_READONLY, 0, 1, - nonEventName ); - - if ( NULL == hFMap ) - { - bRet = FALSE; - Trace ( "PALSUITE ERROR: CreateFileMapping (%p, %p, %d, %d, %d, %S)" - " call returned NULL.\nGetLastError returned %u\n", - SWAPPTR, NULL, PAGE_READONLY, 0, 0, nonEventName, - GetLastError()); - } - - /* Create Events */ - for (i = 0; i < sizeof(testCases)/sizeof(struct testCase); i++) - { - /* create name */ - memset (name, '\0', MAX_PATH + 2); - memset (name, 'a', testCases[i].dwNameLen ); - - wName = convert(name); - - wcsncpy(testCases[i].lpName, wName, - testCases[i].dwNameLen); - - free(wName); - - SetLastError(ERROR_SUCCESS); - - hEvent[i] = CreateEventW( testCases[i].lpEventAttributes, - testCases[i].bManualReset, - testCases[i].bInitialState, - testCases[i].lpName); - - if (hEvent[i] != INVALID_HANDLE_VALUE) - { - DWORD dwError = GetLastError(); - - if (dwError != testCases[i].lastError) - { - bRet = FALSE; - Trace ("PALSUITE ERROR:\nCreateEvent(%lp, %d, %d, %S)" - "\nGetLastError returned '%u', it should have returned" - "'%d' at index '%d'.\n", testCases[i].lpEventAttributes, - testCases[i].bManualReset, testCases[i].bInitialState, - testCases[i].lpName, dwError, - testCases[i].lastError, i); - } - if ( ERROR_FILENAME_EXCED_RANGE == testCases[i].lastError ) - { - result [i] = 1; - } - if ( ERROR_INVALID_HANDLE == testCases[i].lastError ) - { - result [i] = 1; - } - /* - * If we expected the testcase to FAIL and it passed, - * report an error. - */ - if (testCases[i].bResult == FAIL) - { - bRet = FALSE; - Trace ("PALSUITE ERROR:\nCreateEvent(%lp, %d, %d, %S)" - "\nShould have returned INVALID_HANDLE_VALUE but " - "didn't at index '%d'.\n", - testCases[i].lpEventAttributes, - testCases[i].bManualReset, - testCases[i].bInitialState, - testCases[i].lpName, i); - } - /* - * If result hasn't been set already set it to 0 so all the - * resources will be freed. - */ - if (!result[i]) - { - result[i] = 0; - } - } - else - { - /* - * If we get an INVALID_HANDLE_VALUE and we expected the - * test case to pass, report an error. - */ - result[i] = 1; - - if (testCases[i].bResult == PASS) - { - bRet = FALSE; - Trace ("PALSUITE ERROR:\nCreateEvent(%lp, %d, %d, %S);" - "\nReturned INVALID_HANDLE_VALUE at index '%d'.\n", - testCases[i].lpEventAttributes, - testCases[i].bManualReset, testCases[i].bInitialState, - testCases[i].lpName, i); - } - } - } - - /* cleanup */ - for (i = 0; i < sizeof(testCases)/sizeof(struct testCase); i++) - { - if (result[i]) - { - continue; - } - dwRet = WaitForSingleObject ( hEvent[i], 0 ); - - if (dwRet != WAIT_TIMEOUT) - { - bRet = FALSE; - Trace("PALSUITE ERROR: CreateEventW:\nWaitForSingleObject (%lp, " - "%d) call failed at index %d .\nGetLastError returned " - "'%u'.\n", hEvent[i], 0, i, GetLastError()); - } - - if (!CloseHandle(hEvent[i])) - { - bRet = FALSE; - Trace("PALSUITE ERROR: CreateEventW: CloseHandle(%lp) call " - "failed at index %d\nGetLastError returned '%u'.\n", - hEvent[i], i, GetLastError()); - } - } - -done: - if (hFMap != NULL && !CloseHandle(hFMap)) - { - bRet = FALSE; - Trace("PALSUITE ERROR: CreateEventW: CloseHandle(%p) call " - "failed\nGetLastError returned '%u'.\n", hFMap, - GetLastError()); - } - - if (FALSE == bRet) - { - bRet = FAIL; - } - else - { - bRet = PASS; - } - - PAL_TerminateEx(bRet); - - return(bRet); - -} - - - diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.cpp deleted file mode 100644 index f4fc377d5d6da8..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.cpp +++ /dev/null @@ -1,343 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: CreateMutexW_ReleaseMutex/test1/CreateMutexW.c -** -** Purpose: This test case tests whether a Mutex object created -** with CreateMutex really works by mutually excluding -** threads from accessing a data structure at the same -** time. Here we have a buffer that can be filled or -** emptied, we use a Mutex object to ensure that one -** operation cannot be started until the other is -** finished. If one operation detects that the other -** has not finished, it fails. There is a Producer -** thread which will try to fill the buffer 25 times, -** and a consumer thread which try to empty the buffer -** 25 times. If either the fill or empty operations -** fails because the Mutex failed to mutually exclude -** them, the corresponding thread will set an error -** flag and return. This will cause the test case to -** fail. -** -** To increase the probability of identifying problems, -** the Fill operation has been slowed dowm with a call -** to Sleep. This ensures that one operation will try -** to access the shared buffer while the other is in -** progress. -** -** NOTE: this test case also serves as a test case for -** WaitForSingleObject. -** -** -** Dependencies: CreateThread -** ReleaseMutex -** WaitForSingleObject -** WaitForMultipleObjects -** Sleep -** memset -** - -** -**=========================================================*/ - -#define UNICODE -#include - -/* Define some values that we will using many times */ -#define MAIN_BUF_SIZE 40 -#define NUM_OF_CYCLES 40 - -/* Buffer Operation return codes */ -#define OP_OK 0 -#define OP_ERR 1 -#define OP_NONE 2 - - -static HANDLE hMutex; /* handle to mutex */ - -static BOOL bProdErr; /* Producer error Flag */ -static BOOL bConErr; /* Consumer error Flag */ - -/* Test Buffer */ -static char Buffer[MAIN_BUF_SIZE]; - -/* - * EmptyBuffer implements the empty operation for test buffer. - */ -int -EmptyBuffer() -{ - int i; - - if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED) - { - Fail("ERROR: WaitForSingleObject failed.\n"); - } - - /* Check to see if the buffer is already completely empty */ - for (i=0; i - -#define szMutex "MyMutex" -#define szEmpty "" - -/* Function Prototypes */ -BOOL NegativeReleaseMutexTests_CreateMutexW_ReleaseMutex_test2(); - -struct ThreadData -{ - HANDLE hMutex; - BOOL bReturnCode; -}; -typedef struct ThreadData THREADDATA; - - -PALTEST(threading_CreateMutexW_ReleaseMutex_test2_paltest_createmutexw_releasemutex_test2, "threading/CreateMutexW_ReleaseMutex/test2/paltest_createmutexw_releasemutex_test2") -{ - BOOL bFailures = FALSE; - char *szMaxPath; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - /* - * Run some negative tests on ReleaseMutex - */ - - if (!NegativeReleaseMutexTests_CreateMutexW_ReleaseMutex_test2()) - { - bFailures = TRUE; - } - - - /* - * If there were any failures, then abort with a call to Fail - */ - - if (bFailures == TRUE) - { - Fail("ERROR: There some failures in the Mutex tests.\n"); - } - - PAL_Terminate(); - return ( PASS ); -} - -/* - * Testing Function - * - * Try some negative tests on ReleaseMutex - */ -BOOL NegativeReleaseMutexTests_CreateMutexW_ReleaseMutex_test2() -{ - HANDLE hMutex; - BOOL bRet; - BOOL bResults = TRUE; - - - /* - * Try calling ReleaseMutex on a null handle - */ - hMutex = 0; - bRet = ReleaseMutex(hMutex); - - if (bRet != 0) - { - Trace("Error: ReleaseMutex accepted null handle.\n"); - bResults = FALSE; - } - - - /* - * Try calling ReleaseMutex on an handle that we don't own - */ - hMutex = CreateMutexW (NULL, TRUE, NULL); - if (hMutex == 0) - { - Trace("Error: CreateMutex failed.\n"); - bResults = FALSE; - } - - bRet = ReleaseMutex(hMutex); - bRet = ReleaseMutex(hMutex); - - if (bRet != FALSE) - { - Trace("Error: ReleaseMutex accepted unowned handle.\n"); - bResults = FALSE; - } - - if (CloseHandle(hMutex) == FALSE) - { - Trace("Error: CloseHandle failed.\n"); - bResults = FALSE; - } - - - - /* - * Try calling ReleaseMutex on an handle that has been closed - */ - hMutex = CreateMutexW (NULL, TRUE, NULL); - if (hMutex == 0) - { - Trace("Error: CreateMutex failed.\n"); - bResults = FALSE; - } - - if (ReleaseMutex(hMutex) == FALSE) - { - Trace("Error: ReleaseMutex failed.\n"); - bResults = FALSE; - } - if (CloseHandle(hMutex) == FALSE) - { - Trace("Error: CloseHandle failed.\n"); - bResults = FALSE; - } - - bRet = ReleaseMutex(hMutex); - - if (bRet != FALSE) - { - Trace("Error: ReleaseMutex accepted invalid handle.\n"); - bResults = FALSE; - } - - return bResults; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.cpp b/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.cpp deleted file mode 100644 index aafa76918ee34f..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/childprocess.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: childprocess.c -** -** Purpose: Test to ensure DuplicateHandle works properly. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** CreateMutexW -** WaitForSingleObject -** CloseHandle -** -** -**=========================================================*/ - -#include -#include "myexitcode.h" - - -PALTEST(threading_DuplicateHandle_test11_paltest_duplicatehandle_test11_child, "threading/DuplicateHandle/test11/paltest_duplicatehandle_test11_child") -{ - HANDLE hMutex; - WCHAR wszMutexName[] = { 'T','E','S','T','1','1','\0' }; - DWORD dwRet; - int i; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* open a mutex to synchronize with the parent process */ - hMutex = CreateMutexW( NULL, FALSE, wszMutexName ); - if( hMutex == NULL ) - { - Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() ); - } - - /* acquire the mutex lock */ - dwRet = WaitForSingleObject( hMutex, 10000 ); - if( dwRet != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0", - dwRet ); - if( ! CloseHandle( hMutex ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "test failed\n" ); - } - - - /* simulate some activity */ - for( i=0; i<50000; i++ ) - ; - - /* close our mutex handle */ - if( ! CloseHandle( hMutex ) ) - { - Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - - /* terminate the PAL */ - PAL_Terminate(); - - /* return the predefined exit code */ - return TEST_EXIT_CODE; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h b/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h deleted file mode 100644 index b9446c00d1a999..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/myexitcode.h +++ /dev/null @@ -1,12 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: duplicatehandle/test11/myexitcode.h -** -** Purpose: Define an exit code constant. -** -** -**=========================================================*/ -#define TEST_EXIT_CODE 31 diff --git a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.cpp b/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.cpp deleted file mode 100644 index 94f177f3561cd9..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test11/test11.cpp +++ /dev/null @@ -1,321 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** Source: test11.c -** -** Purpose: -** -** Test to ensure proper operation of the DuplicateHandle API. -** The test launches a trivial child process, then opens -** a handle to it using OpenProcess. It then duplicates that -** handle and uses it to wait for the child process to terminate, -** and then checks the exit code of the child process in order to -** verify that it was in fact a handle to the correct -** process. The test tries to duplicate the handle again after -** the process has been closed, to verify that failure ensues. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** Fail -** ZeroMemory -** GetCurrentDirectoryW -** CreateProcessW -** WaitForSingleObject -** CreateMutexW -** ReleaseMutex -** CloseHandle -** GetLastError -** strlen -** strncpy -** -** -**===========================================================================*/ -#include -#include "myexitcode.h" - -PALTEST(threading_DuplicateHandle_test11_paltest_duplicatehandle_test11, "threading/DuplicateHandle/test11/paltest_duplicatehandle_test11") -{ - const char* rgchChildFile = "childprocess"; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - DWORD dwError; - DWORD dwExitCode; - DWORD dwFileLength; - DWORD dwDirLength; - DWORD dwSize; - DWORD dwRet; - - HANDLE hMutex; - HANDLE hChildProcess; - HANDLE hDupChildProcess; - - char rgchDirName[_MAX_DIR]; - char absPathBuf[MAX_PATH]; - char* rgchAbsPathName; - - BOOL ret = FAIL; - BOOL bChildDone = FALSE; - WCHAR wszMutexName[] = { 'T','E','S','T','1','1','\0' }; - - /* initialize the PAL */ - if( PAL_Initialize(argc, argv) != 0 ) - { - return( FAIL ); - } - - /* create a mutex to synchronize with the child process */ - hMutex = CreateMutexW( NULL, TRUE, wszMutexName ); - if( hMutex == NULL ) - { - Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() ); - } - - /* zero our process and startup info structures */ - ZeroMemory( &si, sizeof(si) ); - si.cb = sizeof( si ); - ZeroMemory( &pi, sizeof(pi) ); - - /* build the absolute path to the child process */ - rgchAbsPathName = &absPathBuf[0]; - dwFileLength = strlen( rgchChildFile ); - - strcpy(rgchDirName, ".\\"); - dwDirLength = strlen(rgchDirName); - - dwSize = mkAbsoluteFilename( rgchDirName, - dwDirLength, - rgchChildFile, - dwFileLength, - rgchAbsPathName ); - if( dwSize == 0 ) - { - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - } - if( CloseHandle( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ", - "not build absolute path name to file\n. Exiting.\n" ); - } - - LPWSTR rgchAbsPathNameW = convert(rgchAbsPathName); - /* launch the child process */ - if( !CreateProcess( NULL, /* module name to execute */ - rgchAbsPathNameW, /* command line */ - NULL, /* process handle not */ - /* inheritable */ - NULL, /* thread handle not */ - /*inheritable */ - FALSE, /* handle inheritance */ - CREATE_NEW_CONSOLE, /* dwCreationFlags */ - NULL, /* use parent's environment */ - NULL, /* use parent's starting */ - /* directory */ - &si, /* startup info struct */ - &pi ) /* process info struct */ - ) - { - dwError = GetLastError(); - free(rgchAbsPathNameW); - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - } - if( CloseHandle( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "CreateProcess call failed with error code %d\n", - dwError ); - } - - free(rgchAbsPathNameW); - - /* open another handle to the child process */ - hChildProcess = OpenProcess( PROCESS_ALL_ACCESS, /* access */ - FALSE, /* inheritable */ - pi.dwProcessId /* process id */ - ); - if( hChildProcess == NULL ) - { - dwError = GetLastError(); - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - } - Trace( "ERROR:%lu:OpenProcess call failed\n", dwError ); - goto cleanup3; - } - - /* duplicate the child process handle */ - if( ! DuplicateHandle( GetCurrentProcess(), - hChildProcess, - GetCurrentProcess(), - &hDupChildProcess, - GENERIC_READ|GENERIC_WRITE, - FALSE, - DUPLICATE_SAME_ACCESS) ) - { - Trace( "ERROR:%lu:DuplicateHandle() call failed\n", GetLastError() ); - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - } - goto cleanup2; - } - - /* release the mutex so the child can proceed */ - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - goto cleanup1; - } - - /* wait for the child process to complete, using the new handle */ - dwRet = WaitForSingleObject( hDupChildProcess, 10000 ); - if( dwRet != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject call returned %lu, " - "expected WAIT_OBJECT_0", - dwRet ); - goto cleanup1; - } - - /* remember that we waited until the child was finished */ - bChildDone = TRUE; - - /* check the exit code from the process -- this is a bit of an */ - /* extra verification that we opened the correct process handle */ - if( ! GetExitCodeProcess( hDupChildProcess, &dwExitCode ) ) - { - Trace( "ERROR:%lu:GetExitCodeProcess call failed\n", GetLastError() ); - goto cleanup1; - } - - /* verification */ - if( (dwExitCode & 0xFF) != (TEST_EXIT_CODE & 0xFF) ) - { - Trace( "GetExitCodeProcess returned an incorrect exit code %d, " - "expected value is %d\n", - (dwExitCode & 0xFF), - (TEST_EXIT_CODE & 0xFF)); - goto cleanup1; - } - - /* close the duplicate handle */ - if( ! CloseHandle( hDupChildProcess ) ) - { - Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() ); - goto cleanup2; - } - - /* close the child process handle */ - if( ! CloseHandle ( hChildProcess ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - goto cleanup3; - } - - /* try to call duplicate handle on the closed child process handle */ - if( DuplicateHandle( GetCurrentProcess(), - hChildProcess, - GetCurrentProcess(), - &hDupChildProcess, - GENERIC_READ|GENERIC_WRITE, - FALSE, - DUPLICATE_SAME_ACCESS) ) - { - Trace( "ERROR:%lu:DuplicateHandle call succeeded on " - "a closed process handle, expected ERROR_INVALID_HANDLE\n" ); - if( ! CloseHandle( hDupChildProcess ) ) - { - Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() ); - } - goto cleanup3; - } - - /* verify that the last error was ERROR_INVALID_HANDLE */ - dwRet = GetLastError(); - if( dwRet != ERROR_INVALID_HANDLE ) - { - Trace( "ERROR:DuplicateHandle returned %lu, " - "expected ERROR_INVALID_HANDLE\n", - dwRet ); - goto cleanup3; - } - - - /* success if we get here */ - ret = PASS; - - /* skip the cleanup stuff that's already done */ - goto cleanup3; - - -cleanup1: - /* close our duplicate handle */ - if( ! CloseHandle( hDupChildProcess ) ) - { - Trace( "ERROR:%lu:CloseHandle call failed\n", GetLastError() ); - ret = FAIL; - } - -cleanup2: - /* wait on the child process to complete if necessary */ - if( ! bChildDone ) - { - dwRet = WaitForSingleObject( hChildProcess, 10000 ); - if( dwRet != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject call returned %lu, " - "expected WAIT_OBJECT_0", - dwRet ); - ret = FAIL; - } - } - - /* close our child process handle */ - if( CloseHandle ( hChildProcess ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } - -cleanup3: - /* close all our other handles */ - if( CloseHandle ( pi.hProcess ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } - if( CloseHandle ( pi.hThread ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } - if( CloseHandle( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } - - if( ret == FAIL ) - { - Fail( "test failed\n" ); - } - - - - /* terminate the PAL */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.cpp b/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.cpp deleted file mode 100644 index e489f54aa5f6ea..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/DuplicateHandle/test4/test4.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test4.c (DuplicateHandle) -** -** Purpose: Tests the PAL implementation of the DuplicateHandle function. -** This test duplication of a Mutex handle. The test will comprise -** of creating a Mutex and its duplicate and create a thread that -** will get ownership. Another thread will be create that will -** attempt to get ownership of the duplicate Mutex, this will -** fail, since the Mutex is owned by another thread. The Mutex -** will be released and then the thread will attempt to get -** ownership of the duplicate Mutex, this will succeed. -** -** -**===================================================================*/ -#include - -enum wait_results -{ - WR_WAITING, - WR_GOT_MUTEX, - WR_TIMED_OUT, - WR_RELEASED -}; - - -volatile int t1_result_DuplicateHandle_test4=WR_WAITING; -volatile int t2_result_DuplicateHandle_test4=WR_WAITING; - - -DWORD PALAPI ThreadTest1_DuplicateHandle_test4(LPVOID lpParam) -{ - DWORD dwWait; - - dwWait = WaitForSingleObject((HANDLE)lpParam, 0); - if (dwWait == WAIT_OBJECT_0) - { - /* tell the main thread we got the mutex */ - t1_result_DuplicateHandle_test4=WR_GOT_MUTEX; - - /* wait for main thread to tell us to release the mutex */ - while(WR_GOT_MUTEX == t1_result_DuplicateHandle_test4) - Sleep(1); - ReleaseMutex((HANDLE)lpParam); - - /* tell the main thread we released the mutex */ - t1_result_DuplicateHandle_test4 = WR_RELEASED; - } - else - { - t1_result_DuplicateHandle_test4 = WR_TIMED_OUT; - } - return 0; -} - -DWORD PALAPI ThreadTest2_DuplicateHandle_test4(LPVOID lpParam) -{ - DWORD dwWait; - - dwWait = WaitForSingleObject((HANDLE)lpParam, 0 ); - if (dwWait == WAIT_OBJECT_0) - { - ReleaseMutex((HANDLE)lpParam); - t2_result_DuplicateHandle_test4 = WR_GOT_MUTEX; - } - else - { - t2_result_DuplicateHandle_test4 = WR_TIMED_OUT; - } - - return 0; -} - - -PALTEST(threading_DuplicateHandle_test4_paltest_duplicatehandle_test4, "threading/DuplicateHandle/test4/paltest_duplicatehandle_test4") -{ - - HANDLE hDupMutex; - HANDLE hMutex; - HANDLE hThread; - HANDLE hThread2; - BOOL bDupHandle=FALSE; - DWORD dwThreadId = 0; - - if ((PAL_Initialize(argc,argv)) != 0) - { - return(FAIL); - } - - /*Create Mutex without ownership*/ - hMutex = CreateMutexW(NULL, // no security attributes - FALSE, // initially not owned - NULL); // name of mutex - if (hMutex == NULL) - { - Fail("ERROR:%u: Unable to create mutex\n", - GetLastError()); - } - - /*Create Duplicate of the Mutex above*/ - bDupHandle = DuplicateHandle(GetCurrentProcess(), - hMutex, - GetCurrentProcess(), - &hDupMutex, - GENERIC_READ|GENERIC_WRITE, - FALSE, - DUPLICATE_SAME_ACCESS); - if (!bDupHandle) - { - Trace("ERROR:%u: Created the duplicate handle to " - "closed event handle hMutex=0x%lx\n", - GetLastError(), - hMutex); - CloseHandle(hMutex); - Fail(""); - } - - /*Create a thread to test the Mutex*/ - hThread = CreateThread(NULL, - 0, - &ThreadTest1_DuplicateHandle_test4, - hMutex, - 0, - &dwThreadId); - if (hThread == NULL) - { - Trace("ERROR:%u: unable to create thread\n", - GetLastError()); - CloseHandle(hMutex); - CloseHandle(hDupMutex); - Fail(""); - } - - /* wait until thread has taken the mutex */ - while (WR_WAITING == t1_result_DuplicateHandle_test4) - Sleep(1); - - if(WR_TIMED_OUT == t1_result_DuplicateHandle_test4) - { - Trace("ERROR: %u: thread 1 couldn't acquire the mutex\n"); - CloseHandle(hMutex); - CloseHandle(hDupMutex); - CloseHandle(hThread); - Fail(""); - } - - /*Create a second thread to use the duplicate Mutex*/ - /*This should fail since the Mutex is owned hThread*/ - hThread2 = CreateThread(NULL, - 0, - &ThreadTest2_DuplicateHandle_test4, - hDupMutex, - 0, - &dwThreadId); - - if (hThread2 == NULL) - { - Trace("ERROR:%u: unable to create thread\n", - GetLastError()); - CloseHandle(hMutex); - CloseHandle(hDupMutex); - CloseHandle(hThread); - Fail(""); - } - - /* wait until thread has tried to take the mutex */ - while (WR_WAITING == t2_result_DuplicateHandle_test4) - Sleep(1); - - if (WR_TIMED_OUT != t2_result_DuplicateHandle_test4 ) - { - Trace("ERROR:%u: Able to take mutex %#x while its duplicate %#x is " - "held\n", hDupMutex, hMutex); - CloseHandle(hMutex); - CloseHandle(hDupMutex); - CloseHandle(hThread); - CloseHandle(hThread2); - Fail(""); - } - - /* reset second thread status */ - t2_result_DuplicateHandle_test4 = WR_WAITING; - - /* tell thread 1 to release the mutex */ - t1_result_DuplicateHandle_test4 = WR_WAITING; - - /* wait for thread 1 to release the mutex */ - while (WR_WAITING == t1_result_DuplicateHandle_test4) - Sleep(1); - - CloseHandle(hThread2); - - /*Re-Create the second thread to reuse the duplicated Mutex*/ - /*This test should pass, the Mutex has since been released*/ - hThread2 = CreateThread(NULL, - 0, - &ThreadTest2_DuplicateHandle_test4, - hDupMutex, - 0, - &dwThreadId); - - if (hThread2 == NULL) - { - Trace("ERROR:%u: unable to create thread\n", - GetLastError()); - CloseHandle(hMutex); - CloseHandle(hDupMutex); - CloseHandle(hThread); - Fail(""); - } - - /* wait until thread has taken the mutex */ - while (WR_WAITING == t2_result_DuplicateHandle_test4) - Sleep(1); - - if (WR_GOT_MUTEX != t2_result_DuplicateHandle_test4 ) - { - Trace("ERROR:%u: Unable to take mutex %#x after its duplicate %#x was " - "released\n", hDupMutex, hMutex); - CloseHandle(hMutex); - CloseHandle(hDupMutex); - CloseHandle(hThread); - CloseHandle(hThread2); - Fail(""); - } - - /*Cleanup.*/ - CloseHandle(hMutex); - CloseHandle(hDupMutex); - CloseHandle(hThread); - CloseHandle(hThread2); - - PAL_Terminate(); - return (PASS); -} diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test4/test4.cpp b/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test4/test4.cpp deleted file mode 100644 index b56af149a4dbcb..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/OpenEventW/test4/test4.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================================= -** -** Source: test4.c -** -** Purpose: Positive test for OpenEventW. -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** CreateEvent -** CloseHandle -** WaitForSingleObject -** -** Purpose: -** -** Test to ensure proper operation of the OpenEventW() -** API by trying to open an event with a name that is -** already taken by a non-event object. -** -** -**===========================================================================*/ -#include - - - -PALTEST(threading_OpenEventW_test4_paltest_openeventw_test4, "threading/OpenEventW/test4/paltest_openeventw_test4") - -{ - /* local variables */ - BOOL bRet = PASS; - DWORD dwLastError = 0; - HANDLE hMutex = NULL; - HANDLE hTestEvent = NULL; - LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL; - BOOL bInitialState = TRUE; - WCHAR wcName[] = {'I','m','A','M','u','t','e','x','\0'}; - LPWSTR lpName = wcName; - - - /* PAL initialization */ - if( (PAL_Initialize(argc, argv)) != 0 ) - { - return( FAIL ); - } - - /* create a mutex object */ - hMutex = CreateMutexW( lpSecurityAttributes, - bInitialState, - lpName ); - - if( hMutex == NULL ) - { - /* ERROR */ - Fail( "ERROR:%lu:CreateMutexW() call failed\n", GetLastError() ); - } - - /* open a new handle to our event */ - hTestEvent = OpenEventW(EVENT_ALL_ACCESS, /* we want all rights */ - FALSE, /* no inherit */ - lpName ); - - if( hTestEvent != NULL ) - { - /* ERROR */ - Trace( "ERROR:OpenEventW() call succeeded against a named " - "mutex, should have returned NULL\n" ); - if( ! CloseHandle( hTestEvent ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed \n", GetLastError() ); - } - bRet = FAIL; - } - else - { - dwLastError = GetLastError(); - if( dwLastError != ERROR_INVALID_HANDLE ) - { - /* ERROR */ - Trace( "ERROR:OpenEventW() call failed against a named " - "mutex, but returned an unexpected result: %lu\n", - dwLastError ); - bRet = FAIL; - } - } - - - /* close the mutex handle */ - if( ! CloseHandle( hMutex ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed \n", GetLastError() ); - bRet = FAIL; - } - - - /* fail here if we weren't successful */ - if( bRet == FAIL ) - { - Fail( "" ); - } - - - /* PAL termination */ - PAL_Terminate(); - - /* return success or failure */ - return PASS; -} - - diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp b/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp index db78c20ebc773c..9c12e981ec3663 100644 --- a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/childProcess.cpp @@ -23,8 +23,6 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1_child, "threading/OpenProcess/test1/paltest_openprocess_test1_child") { - HANDLE hMutex; - WCHAR wszMutexName[] = { 'T','E','S','T','1','\0' }; DWORD dwRet; int i; @@ -34,38 +32,10 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1_child, "threading/ return( FAIL ); } - /* open a mutex to synchronize with the parent process */ - hMutex = CreateMutexW( NULL, FALSE, wszMutexName ); - if( hMutex == NULL ) - { - Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() ); - } - - /* acquire the mutex lock */ - dwRet = WaitForSingleObject( hMutex, 10000 ); - if( dwRet != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0", - dwRet ); - if( ! CloseHandle( hMutex ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "test failed\n" ); - } - - /* simulate some activity */ for( i=0; i<50000; i++ ) ; - /* close our mutex handle */ - if( ! CloseHandle( hMutex ) ) - { - Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - /* terminate the PAL */ PAL_Terminate(); diff --git a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp index 9bcdcb129eab98..5c5e2d3e5d19bf 100644 --- a/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/OpenProcess/test1/test1.cpp @@ -40,7 +40,6 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenPr DWORD dwSize; DWORD dwRet; - HANDLE hMutex; HANDLE hChildProcess; char rgchDirName[_MAX_DIR]; @@ -49,7 +48,6 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenPr BOOL ret = FAIL; BOOL bChildDone = FALSE; - WCHAR wszMutexName[] = { 'T','E','S','T','1','\0' }; /* initialize the PAL */ if( PAL_Initialize(argc, argv) != 0 ) @@ -57,13 +55,6 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenPr return( FAIL ); } - /* create a mutex to synchronize with the child process */ - hMutex = CreateMutexW( NULL, TRUE, wszMutexName ); - if( hMutex == NULL ) - { - Fail( "ERROR:%lu:CreateMutex() call failed\r\n", GetLastError() ); - } - /* zero our process and startup info structures */ ZeroMemory( &si, sizeof(si) ); si.cb = sizeof( si ); @@ -83,14 +74,6 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenPr rgchAbsPathName ); if( dwSize == 0 ) { - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - } - if( CloseHandle( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } Fail( "Palsuite Code: mkAbsoluteFilename() call failed. Could ", "not build absolute path name to file\n. Exiting.\n" ); } @@ -114,14 +97,6 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenPr { dwError = GetLastError(); free(rgchAbsPathNameW); - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - } - if( CloseHandle( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } Fail( "CreateProcess call failed with error code %d\n", dwError ); } @@ -136,21 +111,10 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenPr if( hChildProcess == NULL ) { dwError = GetLastError(); - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - } Trace( "ERROR:%lu:OpenProcess call failed\n", dwError ); goto cleanup2; } - /* release the mutex so the child can proceed */ - if( ReleaseMutex( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:ReleaseMutex() call failed\n", GetLastError() ); - goto cleanup; - } - /* wait for the child process to complete, using the new handle */ dwRet = WaitForSingleObject( hChildProcess, 10000 ); if( dwRet != WAIT_OBJECT_0 ) @@ -218,11 +182,6 @@ PALTEST(threading_OpenProcess_test1_paltest_openprocess_test1, "threading/OpenPr Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); ret = FAIL; } - if( CloseHandle( hMutex ) == 0 ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - ret = FAIL; - } if( ret == FAIL ) { diff --git a/src/coreclr/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.cpp b/src/coreclr/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.cpp deleted file mode 100644 index be43bce844efb1..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/ReleaseMutex/test3/ReleaseMutex.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: ReleaseMutex/test3/ReleaseMutex.c -** -** Purpose: Test failure code for ReleaseMutex. -** -** Dependencies: CreateMutex -** ReleaseMutex -** CreateThread -** - -** -**=========================================================*/ - -#include - -DWORD dwTestResult_ReleaseMutex_test3; /* global for test result */ - -DWORD dwThreadId_ReleaseMutex_test3; /* consumer thread identifier */ - -HANDLE hMutex_ReleaseMutex_test3; /* handle to mutex */ - -HANDLE hThread_ReleaseMutex_test3; /* handle to thread */ - -/* - * Thread function. - */ -DWORD -PALAPI -ThreadFunction_ReleaseMutex_test3( LPVOID lpNoArg ) -{ - - dwTestResult_ReleaseMutex_test3 = ReleaseMutex(hMutex_ReleaseMutex_test3); - - return 0; -} - -PALTEST(threading_ReleaseMutex_test3_paltest_releasemutex_test3, "threading/ReleaseMutex/test3/paltest_releasemutex_test3") -{ - - if(0 != (PAL_Initialize(argc, argv))) - { - return (FAIL); - } - - /* - * set dwTestResult so test fails even if ReleaseMutex is not called - */ - dwTestResult_ReleaseMutex_test3 = 1; - - /* - * Create mutex - */ - hMutex_ReleaseMutex_test3 = CreateMutexW ( - NULL, - TRUE, - NULL); - - if ( NULL == hMutex_ReleaseMutex_test3 ) - { - Fail ( "hMutex = CreateMutex () - returned NULL\n" - "Failing Test.\nGetLastError returned %d\n", GetLastError()); - } - - /* - * Create ThreadFunction - */ - hThread_ReleaseMutex_test3 = CreateThread( - NULL, - 0, - ThreadFunction_ReleaseMutex_test3, - NULL, - 0, - &dwThreadId_ReleaseMutex_test3); - - if ( NULL == hThread_ReleaseMutex_test3 ) - { - - Fail ( "CreateThread() returned NULL. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - /* - * Wait for ThreadFunction to complete - */ - WaitForSingleObject (hThread_ReleaseMutex_test3, INFINITE); - - if (dwTestResult_ReleaseMutex_test3) - { - Fail ("ReleaseMutex() test was expected to return 0.\n" - "It returned %d. Failing test.\n", dwTestResult_ReleaseMutex_test3 ); - } - - Trace ("ReleaseMutex() test returned 0.\nTest passed.\n"); - - PAL_Terminate(); - return ( PASS ); - -} diff --git a/src/coreclr/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp b/src/coreclr/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp index 3601abb4b904b7..7c7715adfb57ff 100644 --- a/src/coreclr/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp @@ -12,10 +12,7 @@ enum class SignalableObjectType AutoResetEvent, Semaphore, FullSemaphore, - Mutex, - UnlockedMutex, - - Last = UnlockedMutex + Last = FullSemaphore, }; enum class WaitableObjectType @@ -29,10 +26,8 @@ enum class WaitableObjectType UnsignaledAutoResetEvent, Semaphore, EmptySemaphore, - Mutex, - LockedMutex, - Last = LockedMutex + Last = EmptySemaphore }; void operator ++(SignalableObjectType &objectType) @@ -93,12 +88,6 @@ HANDLE CreateObjectToSignal(SignalableObjectType objectType) case SignalableObjectType::FullSemaphore: return CreateSemaphoreExW(nullptr, 1, 1, nullptr, 0, 0); - case SignalableObjectType::Mutex: - return CreateMutex(nullptr, true, nullptr); - - case SignalableObjectType::UnlockedMutex: - return CreateMutex(nullptr, false, nullptr); - default: TestAssert(false); } @@ -121,10 +110,6 @@ void VerifySignal(HANDLE h, SignalableObjectType objectType) TestAssert(!ReleaseSemaphore(h, 1, nullptr)); break; - case SignalableObjectType::Mutex: - TestAssert(!ReleaseMutex(h)); - break; - default: TestAssert(false); } @@ -163,12 +148,6 @@ HANDLE CreateObjectToWaitOn(WaitableObjectType objectType) case WaitableObjectType::EmptySemaphore: return CreateSemaphoreExW(nullptr, 0, 1, nullptr, 0, 0); - case WaitableObjectType::Mutex: - return CreateMutex(nullptr, false, nullptr); - - case WaitableObjectType::LockedMutex: - return CreateMutex(nullptr, true, nullptr); - default: TestAssert(false); } @@ -189,20 +168,6 @@ void VerifyWait(HANDLE h, WaitableObjectType objectType) TestAssert(WaitForSingleObject(h, 0) == WAIT_TIMEOUT); break; - case WaitableObjectType::Mutex: - TestAssert(ReleaseMutex(h)); - TestAssert(!ReleaseMutex(h)); - TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); - break; - - case WaitableObjectType::LockedMutex: - TestAssert(ReleaseMutex(h)); - TestAssert(ReleaseMutex(h)); - TestAssert(!ReleaseMutex(h)); - TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); - TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); - break; - default: TestAssert(false); } @@ -225,17 +190,6 @@ void CloseObjectToWaitOn(HANDLE h, WaitableObjectType objectType) CloseHandle(h); break; - case WaitableObjectType::Mutex: - ReleaseMutex(h); - CloseHandle(h); - break; - - case WaitableObjectType::LockedMutex: - ReleaseMutex(h); - ReleaseMutex(h); - CloseHandle(h); - break; - default: break; } @@ -257,11 +211,6 @@ bool Verify(SignalableObjectType signalableObjectType, WaitableObjectType waitab TestAssert(errorCode == ERROR_TOO_MANY_POSTS); return false; - case SignalableObjectType::UnlockedMutex: - TestAssert(waitResult == WAIT_FAILED); - TestAssert(errorCode == ERROR_NOT_OWNER); - return false; - default: break; } diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.cpp deleted file mode 100644 index 319690e5ed0f35..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test3/test3.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test3.c -** -** Purpose: Tests that waiting on an open mutex will a return -** WAIT_OBJECT_0. Does this by creating a child thread that -** acquires the mutex, releases it, and exits. -** -** -**===================================================================*/ - -#include - - -const int ChildThreadWaitTime = 1000; -const int ParentDelayTime = 2000; - -DWORD PALAPI AcquiringProc(LPVOID lpParameter); - -PALTEST(threading_WaitForMultipleObjectsEx_test3_paltest_waitformultipleobjectsex_test3, "threading/WaitForMultipleObjectsEx/test3/paltest_waitformultipleobjectsex_test3") -{ - HANDLE Mutex; - HANDLE hThread = 0; - DWORD dwThreadId = 0; - int ret; - - if (0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - Mutex = CreateMutexW(NULL, FALSE, NULL); - if (Mutex == NULL) - { - Fail("Unable to create the mutex. GetLastError returned %d\n", - GetLastError()); - } - - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)AcquiringProc, - (LPVOID) Mutex, - 0, - &dwThreadId); - - if (hThread == NULL) - { - Fail("ERROR: Was not able to create the thread to test!\n" - "GetLastError returned %d\n", GetLastError()); - } - - Sleep(ParentDelayTime); - - ret = WaitForMultipleObjectsEx(1, &Mutex, FALSE, INFINITE, FALSE); - if (ret != WAIT_OBJECT_0) - { - Fail("Expected WaitForMultipleObjectsEx to return WAIT_OBJECT_0\n" - "Got %d\n", ret); - } - - if (!CloseHandle(Mutex)) - { - Fail("CloseHandle on the mutex failed!\n"); - } - - if (!CloseHandle(hThread)) - { - Fail("CloseHandle on the thread failed!\n"); - } - - PAL_Terminate(); - return PASS; -} - -/* - * Entry Point for child thread. Acquries a mutex, releases it, and exits. - */ -DWORD PALAPI AcquiringProc(LPVOID lpParameter) -{ - HANDLE Mutex; - DWORD ret; - - Mutex = (HANDLE) lpParameter; - - Sleep(ChildThreadWaitTime); - - ret = WaitForSingleObject(Mutex, 0); - if (ret != WAIT_OBJECT_0) - { - Fail("Expected the WaitForSingleObject call on the mutex to succeed\n" - "Expected return of WAIT_OBJECT_0, got %d\n", ret); - } - - ret = ReleaseMutex(Mutex); - if (!ret) - { - Fail("Unable to release mutex! GetLastError returned %d\n", - GetLastError()); - } - - return 0; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.cpp deleted file mode 100644 index c1589702a44c83..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test4/test4.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: test4.c -** -** Purpose: Tests that waiting on an abandonded mutex will a return -** WAIT_ABANDONED_0. Does this by creating a child thread that -** acquires the mutex and exits. -** -** -**===================================================================*/ - -#include - - -const int ChildThreadWaitTime = 1000; -const int ParentDelayTime = 2000; - -DWORD PALAPI AbandoningProc(LPVOID lpParameter); - -PALTEST(threading_WaitForMultipleObjectsEx_test4_paltest_waitformultipleobjectsex_test4, "threading/WaitForMultipleObjectsEx/test4/paltest_waitformultipleobjectsex_test4") -{ - HANDLE Mutex; - HANDLE hThread = 0; - DWORD dwThreadId = 0; - int ret; - - if (0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - Mutex = CreateMutexW(NULL, FALSE, NULL); - if (Mutex == NULL) - { - Fail("Unable to create the mutex. GetLastError returned %d\n", - GetLastError()); - } - - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)AbandoningProc, - (LPVOID) Mutex, - 0, - &dwThreadId); - - if (hThread == NULL) - { - Fail("ERROR: Was not able to create the thread to test!\n" - "GetLastError returned %d\n", GetLastError()); - } - - Sleep(ParentDelayTime); - - ret = WaitForMultipleObjectsEx(1, &Mutex, FALSE, INFINITE, FALSE); - if (ret != WAIT_ABANDONED_0) - { - Fail("Expected WaitForMultipleObjectsEx to return WAIT_ABANDONED_0\n" - "Got %d\n", ret); - } - - ReleaseMutex(Mutex); - if (!CloseHandle(Mutex)) - { - Fail("CloseHandle on the mutex failed!\n"); - } - - if (!CloseHandle(hThread)) - { - Fail("CloseHandle on the thread failed!\n"); - } - - PAL_Terminate(); - return PASS; -} - -/* - * Entry Point for child thread. Acquries a mutex and exit's without - * releasing it. - */ -DWORD PALAPI AbandoningProc(LPVOID lpParameter) -{ - HANDLE Mutex; - DWORD ret; - - Mutex = (HANDLE) lpParameter; - - Sleep(ChildThreadWaitTime); - - ret = WaitForSingleObject(Mutex, 0); - if (ret != WAIT_OBJECT_0) - { - Fail("Expected the WaitForSingleObject call on the mutex to succeed\n" - "Expected return of WAIT_OBJECT_0, got %d\n", ret); - } - - return 0; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.cpp deleted file mode 100644 index 66ae05bf230f66..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.cpp +++ /dev/null @@ -1,213 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: WFSOExMutex.c -** -** Purpose: Tests a child thread in the middle of a -** WaitForSingleObjectEx call will be interrupted by QueueUserAPC -** if the alert flag was set. -** -** -**===================================================================*/ - -#include - -/*Based on SleepEx/test2 */ - -const int ChildThreadWaitTime = 4000; -const int InterruptTime = 2000; -const DWORD AcceptableDelta = 300; - -void RunTest_WFSOExMutexTest(BOOL AlertThread); -VOID PALAPI APCFunc_WFSOExMutexTest(ULONG_PTR dwParam); -DWORD PALAPI WaiterProc_WFSOExMutexTest(LPVOID lpParameter); - -DWORD ThreadWaitDelta_WFSOExMutexTest; -HANDLE hMutex_WFSOExMutexTest; -static volatile bool s_preWaitTimestampRecorded = false; - -PALTEST(threading_WaitForSingleObject_WFSOExMutexTest_paltest_waitforsingleobject_wfsoexmutextest, "threading/WaitForSingleObject/WFSOExMutexTest/paltest_waitforsingleobject_wfsoexmutextest") -{ - int ret=0; - - if (0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - /* - On some platforms (e.g. FreeBSD 4.9) the first call to some synch objects - (such as conditions) involves some pthread internal initialization that - can make the first wait slighty longer, potentially going above the - acceptable delta for this test. Let's add a dummy wait to preinitialize - internal structures - */ - Sleep(100); - - /* - The state of a mutex object is signaled when it is not owned by any thread. - The creating thread can use the bInitialOwner flag to request immediate ownership - of the mutex. Otherwise, a thread must use one of the wait functions to request - ownership. When the mutex's state is signaled, one waiting thread is granted - ownership, the mutex's state changes to nonsignaled, and the wait function returns. - Only one thread can own a mutex at any given time. The owning thread uses the - ReleaseMutex function to release its ownership. - */ - - /* Create a mutex that is not in the signalled state */ - hMutex_WFSOExMutexTest = CreateMutex(NULL, //No security attributes - TRUE, //Iniitally owned - NULL); //Name of mutex - - if (hMutex_WFSOExMutexTest == NULL) - { - Fail("Failed to create mutex! GetLastError returned %d.\n", - GetLastError()); - } - /* - * Check that Queueing an APC in the middle of a wait does interrupt - * it, if it's in an alertable state. - */ - - RunTest_WFSOExMutexTest(TRUE); - if ((ThreadWaitDelta_WFSOExMutexTest - InterruptTime) > AcceptableDelta) - { - Fail("Expected thread to wait for %d ms (and get interrupted).\n" - "Thread waited for %d ms! (Acceptable delta: %d)\n", - InterruptTime, ThreadWaitDelta_WFSOExMutexTest, AcceptableDelta); - } - - - /* - * Check that Queueing an APC in the middle of a wait does NOT interrupt - * it, if it is not in an alertable state. - */ - RunTest_WFSOExMutexTest(FALSE); - if ((ThreadWaitDelta_WFSOExMutexTest - ChildThreadWaitTime) > AcceptableDelta) - { - Fail("Expected thread to wait for %d ms (and not be interrupted).\n" - "Thread waited for %d ms! (Acceptable delta: %d)\n", - ChildThreadWaitTime, ThreadWaitDelta_WFSOExMutexTest, AcceptableDelta); - } - - - - //Release Mutex - ret = ReleaseMutex(hMutex_WFSOExMutexTest); - if (0==ret) - { - Fail("Unable to Release Mutex!\n" - "GetLastError returned %d\n", GetLastError()); - } - - //Close Mutex Handle - ret = CloseHandle(hMutex_WFSOExMutexTest); - if (!ret) - { - Fail("Unable to close handle to Mutex!\n" - "GetLastError returned %d\n", GetLastError()); - } - - PAL_Terminate(); - return PASS; -} - -void RunTest_WFSOExMutexTest(BOOL AlertThread) -{ - - HANDLE hThread = 0; - DWORD dwThreadId = 0; - - int ret=0; - - s_preWaitTimestampRecorded = false; - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)WaiterProc_WFSOExMutexTest, - (LPVOID) AlertThread, - 0, - &dwThreadId); - - if (hThread == NULL) - { - Fail("ERROR: Was not able to create the thread to test!\n" - "GetLastError returned %d\n", GetLastError()); - } - - // Wait for the pre-wait timestamp to be recorded on the other thread before sleeping, since the sleep duration here will be - // compared against the sleep/wait duration on the other thread - while (!s_preWaitTimestampRecorded) - { - Sleep(0); - } - - Sleep(InterruptTime); - - ret = QueueUserAPC(APCFunc_WFSOExMutexTest, hThread, 0); - - if (ret == 0) - { - Fail("QueueUserAPC failed! GetLastError returned %d\n", - GetLastError()); - } - - ret = WaitForSingleObject(hThread, INFINITE); - - if (ret == WAIT_FAILED) - { - Fail("Unable to wait on child thread!\nGetLastError returned %d.\n", - GetLastError()); - } - - - if (0==CloseHandle(hThread)) - { - Trace("Could not close Thread handle\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } -} - -/* Function doesn't do anything, just needed to interrupt the wait*/ -VOID PALAPI APCFunc_WFSOExMutexTest(ULONG_PTR dwParam) -{ -} - -/* Entry Point for child thread. */ -DWORD PALAPI WaiterProc_WFSOExMutexTest(LPVOID lpParameter) -{ - int64_t OldTimeStamp; - int64_t NewTimeStamp; - BOOL Alertable; - DWORD ret; - - Alertable = (BOOL)(SIZE_T) lpParameter; - - OldTimeStamp = minipal_hires_ticks(); - s_preWaitTimestampRecorded = true; - - ret = WaitForSingleObjectEx( hMutex_WFSOExMutexTest, - ChildThreadWaitTime, - Alertable); - - NewTimeStamp = minipal_hires_ticks(); - - if (Alertable && ret != WAIT_IO_COMPLETION) - { - Fail("Expected the interrupted wait to return WAIT_IO_COMPLETION.\n" - "Got %d\n", ret); - } - else if (!Alertable && ret != WAIT_TIMEOUT) - { - Fail("WaitForSingleObjectEx did not timeout.\n" - "Expected return of WAIT_TIMEOUT, got %d.\n", ret); - } - - ThreadWaitDelta_WFSOExMutexTest = (NewTimeStamp - OldTimeStamp) / (minipal_hires_tick_frequency() / 1000); - - return 0; -} - - - diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.cpp deleted file mode 100644 index 9ac6c11bc52ab6..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.cpp +++ /dev/null @@ -1,183 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: WFSOMutexTest.c -** -** Purpose: Test for WaitForSingleObjectTest. -** Create Mutex Object -** Create Two Threads, Each Threads does WFSO for the Mutex Object -** Increments Counter -** Releases Mutex -** Test Passes if the above operations are successful -** -** -** -**=========================================================*/ - - - -#include - - -#define NUMBER_OF_WORKER_THREADS 2 - -//Declaring Variables -HANDLE hMutex_WFSOMutexTest = NULL; -unsigned int globalcounter_WFSOMutexTest =0; -int testReturnCode_WFSOMutexTest = PASS; - -//Declaring Function Prototypes -DWORD PALAPI WFSOMutexTest(LPVOID params); -void incrementCounter_WFSOMutexTest(void); - - - -PALTEST(threading_WaitForSingleObject_WFSOMutexTest_paltest_waitforsingleobject_wfsomutextest, "threading/WaitForSingleObject/WFSOMutexTest/paltest_waitforsingleobject_wfsomutextest") -{ - - //Declare local variables - int i =0; - - // 2 dimensional array to hold thread handles for each worker thread - HANDLE hThread[NUMBER_OF_WORKER_THREADS]; - DWORD dwThreadId=0; - int returnCode = 0; - - //Initialize PAL - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - //Create Mutex - hMutex_WFSOMutexTest = CreateMutex(NULL, // no security attributes - FALSE, // initially not owned - NULL); // name of mutex - - //Check for Mutex Creation - - if (hMutex_WFSOMutexTest == NULL) - { - Fail("Create Mutex Failed, GetLastError: %d\n", GetLastError()); - } - - - //Spawn 2 worker threads - for (i=0;i Date: Tue, 29 Jul 2025 21:34:44 +0000 Subject: [PATCH 08/33] Remove concept of ownership and abandonment and other vistigial components of mutexes from the CoreCLR PAL. --- src/coreclr/pal/inc/pal.h | 2 - src/coreclr/pal/src/config.h.in | 2 - src/coreclr/pal/src/configure.cmake | 224 ---------- src/coreclr/pal/src/file/file.cpp | 3 +- src/coreclr/pal/src/include/pal/corunix.hpp | 67 +-- .../pal/src/include/pal/synchobjects.hpp | 9 - src/coreclr/pal/src/map/map.cpp | 3 +- .../pal/src/synchmgr/synchcontrollers.cpp | 411 +----------------- src/coreclr/pal/src/synchmgr/synchmanager.cpp | 142 +----- src/coreclr/pal/src/synchmgr/synchmanager.hpp | 89 +--- src/coreclr/pal/src/synchmgr/wait.cpp | 23 +- src/coreclr/pal/src/synchobj/event.cpp | 6 +- src/coreclr/pal/src/synchobj/semaphore.cpp | 3 +- src/coreclr/pal/src/thread/process.cpp | 3 +- src/coreclr/pal/src/thread/thread.cpp | 17 +- .../test1/CreateSemaphore.cpp | 7 - .../test2/CreateSemaphore.cpp | 7 - .../WFSOProcessTest/WFSOProcessTest.cpp | 8 - .../WFSOThreadTest/WFSOThreadTest.cpp | 99 ++--- 19 files changed, 78 insertions(+), 1047 deletions(-) diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 63ec2e9ffd4419..19d9a135cb363b 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -847,8 +847,6 @@ GetExitCodeProcess( #define MAXIMUM_WAIT_OBJECTS 64 #define WAIT_OBJECT_0 0 -#define WAIT_ABANDONED 0x00000080 -#define WAIT_ABANDONED_0 0x00000080 #define WAIT_TIMEOUT 258 #define WAIT_FAILED ((DWORD)0xFFFFFFFF) diff --git a/src/coreclr/pal/src/config.h.in b/src/coreclr/pal/src/config.h.in index 941e60e71ee62e..3338faf883ce30 100644 --- a/src/coreclr/pal/src/config.h.in +++ b/src/coreclr/pal/src/config.h.in @@ -104,8 +104,6 @@ #cmakedefine PAL_PTRACE(cmd, pid, addr, data) @PAL_PTRACE@ #cmakedefine01 SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING #cmakedefine01 ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS -#cmakedefine01 HAVE_FULLY_FEATURED_PTHREAD_MUTEXES -#cmakedefine01 HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES #cmakedefine BSD_REGS_STYLE(reg, RR, rr) @BSD_REGS_STYLE@ #cmakedefine01 HAVE_SCHED_OTHER_ASSIGNABLE #cmakedefine01 SET_SCHEDPARAM_NEEDS_PRIVS diff --git a/src/coreclr/pal/src/configure.cmake b/src/coreclr/pal/src/configure.cmake index 3d89ba2f593a13..c5814814ff446e 100644 --- a/src/coreclr/pal/src/configure.cmake +++ b/src/coreclr/pal/src/configure.cmake @@ -665,230 +665,6 @@ int main(int argc, char **argv) return 0; }" HAVE_PR_SET_PTRACER) -set(CMAKE_REQUIRED_LIBRARIES pthread) -check_cxx_source_compiles(" -#include -#include -#include - -int main() -{ - pthread_mutexattr_t mutexAttributes; - pthread_mutexattr_init(&mutexAttributes); - pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED); - pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE); - pthread_mutexattr_setrobust(&mutexAttributes, PTHREAD_MUTEX_ROBUST); - - pthread_mutex_t mutex; - pthread_mutex_init(&mutex, &mutexAttributes); - - pthread_mutexattr_destroy(&mutexAttributes); - - struct timespec timeoutTime; - timeoutTime.tv_sec = 1; // not the right way to specify absolute time, but just checking availability of timed lock - timeoutTime.tv_nsec = 0; - pthread_mutex_timedlock(&mutex, &timeoutTime); - pthread_mutex_consistent(&mutex); - - pthread_mutex_destroy(&mutex); - - int error = EOWNERDEAD; - error = ENOTRECOVERABLE; - error = ETIMEDOUT; - error = 0; - return error; -}" HAVE_FULLY_FEATURED_PTHREAD_MUTEXES) -set(CMAKE_REQUIRED_LIBRARIES) - -if(NOT CLR_CMAKE_HOST_ARCH_ARM AND NOT CLR_CMAKE_HOST_ARCH_ARM64) - set(CMAKE_REQUIRED_LIBRARIES pthread) - check_cxx_source_runs(" - // This test case verifies the pthread process-shared robust mutex's cross-process abandon detection. The parent process starts - // a child process that locks the mutex, the process process then waits to acquire the lock, and the child process abandons the - // mutex by exiting the process while holding the lock. The parent process should then be released from its wait, be assigned - // ownership of the lock, and be notified that the mutex was abandoned. - - #include - #include - - #include - #include - #include - #include - - #include - using namespace std; - - struct Shm - { - pthread_mutex_t syncMutex; - pthread_cond_t syncCondition; - pthread_mutex_t robustMutex; - int conditionValue; - - Shm() : conditionValue(0) - { - } - } *shm; - - int GetFailTimeoutTime(struct timespec *timeoutTimeRef) - { - int getTimeResult = clock_gettime(CLOCK_REALTIME, timeoutTimeRef); - if (getTimeResult != 0) - { - struct timeval tv; - getTimeResult = gettimeofday(&tv, NULL); - if (getTimeResult != 0) - return 1; - timeoutTimeRef->tv_sec = tv.tv_sec; - timeoutTimeRef->tv_nsec = tv.tv_usec * 1000; - } - timeoutTimeRef->tv_sec += 30; - return 0; - } - - int WaitForConditionValue(int desiredConditionValue) - { - struct timespec timeoutTime; - if (GetFailTimeoutTime(&timeoutTime) != 0) - return 1; - if (pthread_mutex_timedlock(&shm->syncMutex, &timeoutTime) != 0) - return 1; - - if (shm->conditionValue != desiredConditionValue) - { - if (GetFailTimeoutTime(&timeoutTime) != 0) - return 1; - if (pthread_cond_timedwait(&shm->syncCondition, &shm->syncMutex, &timeoutTime) != 0) - return 1; - if (shm->conditionValue != desiredConditionValue) - return 1; - } - - if (pthread_mutex_unlock(&shm->syncMutex) != 0) - return 1; - return 0; - } - - int SetConditionValue(int newConditionValue) - { - struct timespec timeoutTime; - if (GetFailTimeoutTime(&timeoutTime) != 0) - return 1; - if (pthread_mutex_timedlock(&shm->syncMutex, &timeoutTime) != 0) - return 1; - - shm->conditionValue = newConditionValue; - if (pthread_cond_signal(&shm->syncCondition) != 0) - return 1; - - if (pthread_mutex_unlock(&shm->syncMutex) != 0) - return 1; - return 0; - } - - void DoTest_Child(); - - int DoTest() - { - // Map some shared memory - void *shmBuffer = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); - if (shmBuffer == MAP_FAILED) - return 1; - shm = new(shmBuffer) Shm; - - // Create sync mutex - pthread_mutexattr_t syncMutexAttributes; - if (pthread_mutexattr_init(&syncMutexAttributes) != 0) - return 1; - if (pthread_mutexattr_setpshared(&syncMutexAttributes, PTHREAD_PROCESS_SHARED) != 0) - return 1; - if (pthread_mutex_init(&shm->syncMutex, &syncMutexAttributes) != 0) - return 1; - if (pthread_mutexattr_destroy(&syncMutexAttributes) != 0) - return 1; - - // Create sync condition - pthread_condattr_t syncConditionAttributes; - if (pthread_condattr_init(&syncConditionAttributes) != 0) - return 1; - if (pthread_condattr_setpshared(&syncConditionAttributes, PTHREAD_PROCESS_SHARED) != 0) - return 1; - if (pthread_cond_init(&shm->syncCondition, &syncConditionAttributes) != 0) - return 1; - if (pthread_condattr_destroy(&syncConditionAttributes) != 0) - return 1; - - // Create the robust mutex that will be tested - pthread_mutexattr_t robustMutexAttributes; - if (pthread_mutexattr_init(&robustMutexAttributes) != 0) - return 1; - if (pthread_mutexattr_setpshared(&robustMutexAttributes, PTHREAD_PROCESS_SHARED) != 0) - return 1; - if (pthread_mutexattr_setrobust(&robustMutexAttributes, PTHREAD_MUTEX_ROBUST) != 0) - return 1; - if (pthread_mutex_init(&shm->robustMutex, &robustMutexAttributes) != 0) - return 1; - if (pthread_mutexattr_destroy(&robustMutexAttributes) != 0) - return 1; - - // Start child test process - int error = fork(); - if (error == -1) - return 1; - if (error == 0) - { - DoTest_Child(); - return -1; - } - - // Wait for child to take a lock - WaitForConditionValue(1); - - // Wait to try to take a lock. Meanwhile, child abandons the robust mutex. - struct timespec timeoutTime; - if (GetFailTimeoutTime(&timeoutTime) != 0) - return 1; - error = pthread_mutex_timedlock(&shm->robustMutex, &timeoutTime); - if (error != EOWNERDEAD) // expect to be notified that the robust mutex was abandoned - return 1; - if (pthread_mutex_consistent(&shm->robustMutex) != 0) - return 1; - - if (pthread_mutex_unlock(&shm->robustMutex) != 0) - return 1; - if (pthread_mutex_destroy(&shm->robustMutex) != 0) - return 1; - return 0; - } - - void DoTest_Child() - { - // Lock the robust mutex - struct timespec timeoutTime; - if (GetFailTimeoutTime(&timeoutTime) != 0) - return; - if (pthread_mutex_timedlock(&shm->robustMutex, &timeoutTime) != 0) - return; - - // Notify parent that robust mutex is locked - if (SetConditionValue(1) != 0) - return; - - // Wait a short period to let the parent block on waiting for a lock - sleep(1); - - // Abandon the mutex by exiting the process while holding the lock. Parent's wait should be released by EOWNERDEAD. - } - - int main() - { - int result = DoTest(); - return result >= 0 ? result : 0; - }" HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES) - set(CMAKE_REQUIRED_LIBRARIES) -endif() - if(CLR_CMAKE_TARGET_APPLE) set(HAVE__NSGETENVIRON 1) set(DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 1) diff --git a/src/coreclr/pal/src/file/file.cpp b/src/coreclr/pal/src/file/file.cpp index 8d0cfa99f789d3..e6ced69cd4baef 100644 --- a/src/coreclr/pal/src/file/file.cpp +++ b/src/coreclr/pal/src/file/file.cpp @@ -75,8 +75,7 @@ CObjectType CorUnix::otFile( CFileProcessLocalDataCleanupRoutine, CObjectType::UnwaitableObject, CObjectType::SignalingNotApplicable, - CObjectType::ThreadReleaseNotApplicable, - CObjectType::OwnershipNotApplicable + CObjectType::ThreadReleaseNotApplicable ); CAllowedObjectTypes CorUnix::aotFile(otiFile); diff --git a/src/coreclr/pal/src/include/pal/corunix.hpp b/src/coreclr/pal/src/include/pal/corunix.hpp index 69f4071752757f..17d83a8ac0d189 100644 --- a/src/coreclr/pal/src/include/pal/corunix.hpp +++ b/src/coreclr/pal/src/include/pal/corunix.hpp @@ -204,10 +204,6 @@ namespace CorUnix // Must be ThreadReleaseHasNoSideEffects if eSignalingSemantics is // SingleTransitionObject // - // * eOwnershipSemantics: OwnershipTracked only for mutexes, for which the - // previous two items must also ObjectCanBeUnsignaled and - // ThreadReleaseAltersSignalCount. - // class CObjectType { @@ -232,13 +228,6 @@ namespace CorUnix ThreadReleaseNotApplicable }; - enum OwnershipSemantics - { - OwnershipTracked, - NoOwner, - OwnershipNotApplicable - }; - private: // @@ -259,7 +248,6 @@ namespace CorUnix SynchronizationSupport m_eSynchronizationSupport; SignalingSemantics m_eSignalingSemantics; ThreadReleaseSemantics m_eThreadReleaseSemantics; - OwnershipSemantics m_eOwnershipSemantics; public: @@ -273,8 +261,7 @@ namespace CorUnix OBJECT_PROCESS_LOCAL_DATA_CLEANUP_ROUTINE pProcessLocalDataCleanupRoutine, SynchronizationSupport eSynchronizationSupport, SignalingSemantics eSignalingSemantics, - ThreadReleaseSemantics eThreadReleaseSemantics, - OwnershipSemantics eOwnershipSemantics + ThreadReleaseSemantics eThreadReleaseSemantics ) : m_eTypeId(eTypeId), @@ -286,8 +273,7 @@ namespace CorUnix m_pProcessLocalDataCleanupRoutine(pProcessLocalDataCleanupRoutine), m_eSynchronizationSupport(eSynchronizationSupport), m_eSignalingSemantics(eSignalingSemantics), - m_eThreadReleaseSemantics(eThreadReleaseSemantics), - m_eOwnershipSemantics(eOwnershipSemantics) + m_eThreadReleaseSemantics(eThreadReleaseSemantics) { s_rgotIdMapping[eTypeId] = this; }; @@ -398,14 +384,6 @@ namespace CorUnix { return m_eThreadReleaseSemantics; }; - - OwnershipSemantics - GetOwnershipSemantics( - void - ) - { - return m_eOwnershipSemantics; - }; }; class CAllowedObjectTypes @@ -529,36 +507,6 @@ namespace CorUnix LONG lAmountToDecrement ) = 0; - // - // The following two routines may only be used for object types - // where eOwnershipSemantics is OwnershipTracked (i.e., mutexes). - // - - // - // SetOwner is intended to be used in the implementation of - // CreateMutex when bInitialOwner is TRUE. It must be called - // before the new object instance is registered with the - // handle manager. Any other call to this method is an error. - // - - virtual - PAL_ERROR - SetOwner( - CPalThread *pNewOwningThread - ) = 0; - - // - // DecrementOwnershipCount returns an error if the object - // is unowned, or if the thread this controller is bound to - // is not the owner of the object. - // - - virtual - PAL_ERROR - DecrementOwnershipCount( - void - ) = 0; - virtual void ReleaseController( @@ -604,8 +552,7 @@ namespace CorUnix virtual PAL_ERROR CanThreadWaitWithoutBlocking( - bool *pfCanWaitWithoutBlocking, // OUT - bool *pfAbandoned + bool *pfCanWaitWithoutBlocking // OUT ) = 0; virtual @@ -915,7 +862,6 @@ namespace CorUnix { WaitSucceeded, Alerted, - MutexAbandoned, WaitTimeout, WaitFailed }; @@ -946,13 +892,6 @@ namespace CorUnix DWORD *pdwSignaledObject // OUT ) = 0; - virtual - PAL_ERROR - AbandonObjectsOwnedByThread( - CPalThread *pCallingThread, - CPalThread *pTargetThread - ) = 0; - virtual PAL_ERROR QueueUserAPC( diff --git a/src/coreclr/pal/src/include/pal/synchobjects.hpp b/src/coreclr/pal/src/include/pal/synchobjects.hpp index e984e600ad03db..2c21da9b2d3a1b 100644 --- a/src/coreclr/pal/src/include/pal/synchobjects.hpp +++ b/src/coreclr/pal/src/include/pal/synchobjects.hpp @@ -67,7 +67,6 @@ namespace CorUnix class CSynchData; typedef struct _WaitingThreadsListNode * PWaitingThreadsListNode; - typedef struct _OwnedObjectsListNode * POwnedObjectsListNode; typedef struct _ThreadApcInfoNode * PThreadApcInfoNode; typedef struct _ThreadWaitInfo @@ -157,14 +156,6 @@ namespace CorUnix PAL_ERROR RunDeferredThreadConditionSignalings(); #endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING - // NOTE: the following methods provide non-synchronized access to - // the list of owned objects for this thread. Any thread - // accessing this list MUST own the appropriate - // synchronization lock(s). - void AddObjectToOwnedList(POwnedObjectsListNode pooln); - void RemoveObjectFromOwnedList(POwnedObjectsListNode pooln); - POwnedObjectsListNode RemoveFirstObjectFromOwnedList(void); - // The following methods provide access to the native wait lock for // those implementations that need a lock to protect the support for // native thread blocking (e.g.: pthread conditions) diff --git a/src/coreclr/pal/src/map/map.cpp b/src/coreclr/pal/src/map/map.cpp index 8900ccd1058c01..66d50bcb4bdca2 100644 --- a/src/coreclr/pal/src/map/map.cpp +++ b/src/coreclr/pal/src/map/map.cpp @@ -134,8 +134,7 @@ CObjectType CorUnix::otFileMapping( NULL, // No process local data cleanup routine CObjectType::UnwaitableObject, CObjectType::SignalingNotApplicable, - CObjectType::ThreadReleaseNotApplicable, - CObjectType::OwnershipNotApplicable + CObjectType::ThreadReleaseNotApplicable ); CAllowedObjectTypes aotFileMapping(otiFileMapping); diff --git a/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp b/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp index 52f38913d69dcd..99ca5a966f970b 100644 --- a/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp +++ b/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp @@ -133,8 +133,7 @@ namespace CorUnix signaled) --*/ PAL_ERROR CSynchWaitController::CanThreadWaitWithoutBlocking( - bool * pfCanWaitWithoutBlocking, - bool * pfAbandoned) + bool * pfCanWaitWithoutBlocking) { VALIDATEOBJECT(m_psdSynchData); @@ -142,9 +141,8 @@ namespace CorUnix _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); _ASSERTE(NULL != pfCanWaitWithoutBlocking); - _ASSERTE(NULL != pfAbandoned); - fRetVal = m_psdSynchData->CanWaiterWaitWithoutBlocking(m_pthrOwner, pfAbandoned); + fRetVal = m_psdSynchData->CanWaiterWaitWithoutBlocking(m_pthrOwner); if(!fRetVal && otiProcess == m_psdSynchData->GetObjectTypeId()) { @@ -561,117 +559,6 @@ namespace CorUnix return palErr; } - /*++ - Method: - CSynchStateController::SetOwner - - Sets the owner of the target object and initializes the ownership - count to 1 (for objects with tracked ownership). - --*/ - PAL_ERROR CSynchStateController::SetOwner(CPalThread * pNewOwningThread) - { - VALIDATEOBJECT(m_psdSynchData); - - PAL_ERROR palErr = NO_ERROR; - - _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); - _ASSERTE(NULL != pNewOwningThread); - _ASSERT_MSG(CObjectType::OwnershipTracked == - m_potObjectType->GetOwnershipSemantics(), - "SetOwner called on an object without OwnershipTracked " - "semantics\n"); - - if (0 != m_psdSynchData->GetOwnershipCount()) - { - ASSERT("Ownership count should be zero at this time\n"); - palErr = ERROR_INTERNAL_ERROR; - goto SO_exit; - } - - palErr = m_psdSynchData->AssignOwnershipToThread(m_pthrOwner, - pNewOwningThread); - - _ASSERT_MSG(0 == m_psdSynchData->GetOwnershipCount() || - 0 == m_psdSynchData->GetSignalCount(), - "Conflicting values for SignalCount [%d] and " - "OwnershipCount [%d]\n", - m_psdSynchData->GetOwnershipCount(), - m_psdSynchData->GetSignalCount()); - - SO_exit: - return palErr; - } - - /*++ - Method: - CSynchStateController::DecrementOwnershipCount - - Decrements the ownership count of the target object possibly triggering - waiting threads awakening (for objects with tracked ownership). - --*/ - PAL_ERROR CSynchStateController::DecrementOwnershipCount() - { - VALIDATEOBJECT(m_psdSynchData); - - PAL_ERROR palErr = NO_ERROR; - LONG lOwnershipCount = m_psdSynchData->GetOwnershipCount(); - - _ASSERTE(InternalGetCurrentThread() == m_pthrOwner); - _ASSERT_MSG(CObjectType::OwnershipTracked == - m_potObjectType->GetOwnershipSemantics(), - "Trying to decrement ownership count on an object with " - "ownership semantics other than OwnershipTracked\n"); - _ASSERT_MSG(0 <= lOwnershipCount, - "Operation would make ownership count negative - object " - "should be owned at this time [ownership count=%d]\n", - lOwnershipCount); - - if ( (1 > lOwnershipCount) || - (m_psdSynchData->GetOwnerProcessID() != gPID) || - (m_psdSynchData->GetOwnerThread() != m_pthrOwner) ) - { - palErr = ERROR_NOT_OWNER; - goto DOC_exit; - } - - lOwnershipCount--; - m_psdSynchData->SetOwnershipCount(lOwnershipCount); - - if (0 == lOwnershipCount) - { - CPalSynchronizationManager * pSynchManager = - CPalSynchronizationManager::GetInstance(); - OwnedObjectsListNode * pooln = - m_psdSynchData->GetOwnershipListNode(); - - _ASSERT_MSG(NULL != pooln, - "Null ownership node pointer in SynchData with ownership " - "semantics\n"); - _ASSERT_MSG(m_psdSynchData == pooln->pPalObjSynchData, - "Corrupted ownership node\n"); - - // Object has been released - // Remove it from list of owned objs for current thread - m_pthrOwner->synchronizationInfo.RemoveObjectFromOwnedList(pooln); - - // Release SynchData reference count implied by the ownership - // list node - m_psdSynchData->Release(m_pthrOwner); - - // Return node to the cache - pSynchManager->CacheAddOwnedObjsListNode(m_pthrOwner, pooln); - - // Reset ownership - m_psdSynchData->ResetOwnership(); - - // Signal it and trigger waiter thread awakening - m_psdSynchData->Signal(m_pthrOwner, 1); - } - - DOC_exit: - return palErr; - } - /*++ Method: CSynchStateController::ReleaseController @@ -786,11 +673,8 @@ namespace CorUnix CObjectType::SignalingSemantics ssSignalingSemantics = potObjectType->GetSignalingSemantics(); #endif // _DEBUG - CObjectType::OwnershipSemantics osOwnershipSemantics = - potObjectType->GetOwnershipSemantics(); CObjectType::ThreadReleaseSemantics trsThreadReleaseSemantics = potObjectType->GetThreadReleaseSemantics(); - bool fReenteringObjWithOwnership = false; _ASSERT_MSG(CObjectType::SignalingNotApplicable != ssSignalingSemantics, "Signaling not applicable"); @@ -798,59 +682,24 @@ namespace CorUnix trsThreadReleaseSemantics, "Thread releasing not applicable"); _ASSERT_MSG(CObjectType::SingleTransitionObject != ssSignalingSemantics || - (CObjectType::ThreadReleaseHasNoSideEffects == - trsThreadReleaseSemantics && - CObjectType::NoOwner == osOwnershipSemantics), + CObjectType::ThreadReleaseHasNoSideEffects == trsThreadReleaseSemantics, "Conflicting object synchronization attributes " - "[SignalingSemantics=%u OwnershipSemantics=%u " - "ThreadReleaseSemantics=%u]\n", ssSignalingSemantics, - osOwnershipSemantics, trsThreadReleaseSemantics); + "[SignalingSemantics=%u " + "ThreadReleaseSemantics=%u]\n", + ssSignalingSemantics, + trsThreadReleaseSemantics); - if (CObjectType::OwnershipTracked == osOwnershipSemantics && - 0 < GetOwnershipCount()) - { - // We are rentering an object with ownership: we need to skip - // the object unsignaling - fReenteringObjWithOwnership = true; - } - - if (!fReenteringObjWithOwnership && - CObjectType::ThreadReleaseAltersSignalCount == trsThreadReleaseSemantics) + if (CObjectType::ThreadReleaseAltersSignalCount == trsThreadReleaseSemantics) { _ASSERT_MSG(0 < GetSignalCount(), "Internal error: operation would make signal count " "negative - object should be signaled at this time " "[signal count=%d]", GetSignalCount()); - _ASSERT_MSG(CObjectType::OwnershipTracked != osOwnershipSemantics || - 1 == GetSignalCount(), - "Ownable objects cannot have signal count greater " - "than zero [current SignalCount=%d]\n", - GetSignalCount()); // Unsignal the object DecrementSignalCount(); } - if (CObjectType::OwnershipTracked == osOwnershipSemantics) - { - _ASSERT_MSG(0 == GetOwnershipCount() || 0 == GetSignalCount(), - "OwnershipCount and SignalCount with conflicting " - "values\n"); - - // Take ownership or increment ownership count. - // We do this after the object unsignaling to minimize possibilities - // of having both SignalCount and OwnershipCount greater than zero - // (see comment in AssignOwnershipToThread) - palErr = AssignOwnershipToThread(pthrCurrent, pthrTarget); - - if (NO_ERROR != palErr) - { - ERROR("AssignOwnershipToThread failed with error %u; " - "ownership data on object with SynchData {p=%p} " - "may be corrupted\n", palErr, this); - } - } - #ifdef SYNCH_STATISTICS if (NO_ERROR == palErr) { @@ -873,45 +722,11 @@ namespace CorUnix object is local, both local and shared one if the object is shared). --*/ bool CSynchData::CanWaiterWaitWithoutBlocking( - CPalThread * pWaiterThread, - bool * pfAbandoned) + CPalThread * pWaiterThread) { VALIDATEOBJECT(this); - bool fRetVal = (0 < GetSignalCount()); - bool fAbandoned = false; - bool fOwnershipTracked = (CObjectType::OwnershipTracked == - GetObjectType()->GetOwnershipSemantics()); - if (fRetVal) - { - // Object signaled: thread can wait without blocking - if (fOwnershipTracked) - { - fAbandoned = IsAbandoned(); - } - - goto CWWWB_exit; - } - - // Object not signaled: thread can wait without blocking only if the - // object is an ownable one, and it is owned by the current thread - if (fOwnershipTracked) - { - _ASSERT_MSG(0 < GetSignalCount() || 0 < GetOwnershipCount(), - "Objects with ownership must be either signaled or " - "owned by a thread\n"); - - if ((GetOwnerProcessID() == gPID) && - (GetOwnerThread() == pWaiterThread) ) - { - fRetVal = true; - goto CWWWB_exit; - } - } - - CWWWB_exit: - *pfAbandoned = fAbandoned; - return fRetVal; + return 0 < GetSignalCount(); } /*++ @@ -958,12 +773,6 @@ namespace CorUnix } } - _ASSERT_MSG(CObjectType::OwnershipTracked != - GetObjectType()->GetOwnershipSemantics() || - 0 == GetOwnershipCount() || 0 == GetSignalCount(), - "Conflicting values for SignalCount [%d] and " - "OwnershipCount [%d]\n", - GetOwnershipCount(), GetSignalCount()); return; } @@ -1038,31 +847,6 @@ namespace CorUnix dwObjIdx = pwtlnItem->dwObjIndex; ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo; - bool fAbandoned = false; - - if (CObjectType::OwnershipTracked == - GetObjectType()->GetOwnershipSemantics()) - { - // Get the abandoned status before resetting it by - // assigning ownership to target thread - fAbandoned = IsAbandoned(); - - // Assign ownership to target thread - // Note: This will cause both ownership count and - // signal count to be greater than zero at the - // same time; the signal count will be anyway - // decremented immediately by the caller - // CsynchData::Signal - palErr = AssignOwnershipToThread(pthrCurrent, - ptwiWaitInfo->pthrOwner); - if (NO_ERROR != palErr) - { - ERROR("Synch Worker: AssignOwnershipToThread " - "failed with error %u; ownership data on " - "object with SynchData %p may be " - "corrupted\n", palErr, this); - } - } if (fWaitAll) { @@ -1089,7 +873,7 @@ namespace CorUnix palErr = CPalSynchronizationManager::WakeUpLocalThread( pthrCurrent, ptwiWaitInfo->pthrOwner, - fAbandoned ? MutexAbandoned : WaitSucceeded, + WaitSucceeded, dwObjIdx); if (NO_ERROR != palErr) @@ -1172,26 +956,6 @@ namespace CorUnix dwObjIdx = pwtlnItem->dwObjIndex; ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo; - bool fAbandoned = false; - - if (CObjectType::OwnershipTracked == - GetObjectType()->GetOwnershipSemantics()) - { - // Get the abandoned status before resetting it by - // assigning ownership to target thread - fAbandoned = IsAbandoned(); - - // Assign ownership to target thread - palErr = AssignOwnershipToThread(pthrCurrent, - ptwiWaitInfo->pthrOwner); - if (NO_ERROR != palErr) - { - ERROR("Synch Worker: AssignOwnershipToThread " - "failed with error %u; ownership data on " - "object with SynchData %p may be " - "corrupted\n", palErr, this); - } - } if (fWaitAll) { @@ -1218,7 +982,7 @@ namespace CorUnix palErr = CPalSynchronizationManager::WakeUpLocalThread( pthrCurrent, ptwiWaitInfo->pthrOwner, - fAbandoned ? MutexAbandoned : WaitSucceeded, + WaitSucceeded, dwObjIdx); if (NO_ERROR != palErr) @@ -1263,7 +1027,7 @@ namespace CorUnix WaitCompletionState CSynchData::IsRestOfWaitAllSatisfied( WaitingThreadsListNode * pwtlnNode) { - int iSignaledOrOwnedObjCount = 0; + int iSignaledObjCount = 0; int iTgtCount = 0; int i; WaitCompletionState wcsWaitCompletionState = WaitIsNotSatisfied; @@ -1290,7 +1054,6 @@ namespace CorUnix { WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i]; bool fRetVal; - bool fIsAbandoned; VALIDATEOBJECT(pwtlnItem); @@ -1307,17 +1070,16 @@ namespace CorUnix // The target object (the one related to pwtlnNode) is counted as // signaled/owned without checking it (also if it is not, as // it normally happens when this method is called) - iSignaledOrOwnedObjCount++; + iSignaledObjCount++; continue; } fRetVal = psdSynchDataItem->CanWaiterWaitWithoutBlocking( - ptwiWaitInfo->pthrOwner, - &fIsAbandoned); + ptwiWaitInfo->pthrOwner); if (fRetVal) { - iSignaledOrOwnedObjCount++; + iSignaledObjCount++; } else { @@ -1325,7 +1087,7 @@ namespace CorUnix } } - if (iSignaledOrOwnedObjCount < iTgtCount) + if (iSignaledObjCount < iTgtCount) { wcsWaitCompletionState = WaitIsNotSatisfied; } @@ -1339,145 +1101,6 @@ namespace CorUnix return wcsWaitCompletionState; } - - /*++ - Method: - CSynchData::SetOwner - - Blindly sets the thread whose CPalThread is passed as argument, as the - owner of the current object. - WARNING: this method discards any previous ownership data and does not - update the list of the object owned by the owner thread. - - Note: this method must be called while holding the appropriate - synchronization locks (the local process synch lock if the target - object is local, both local and shared one if the object is shared). - --*/ - void CSynchData::SetOwner(CPalThread * pOwnerThread) - { - VALIDATEOBJECT(this); - - m_dwOwnerPid = gPID; - m_dwOwnerTid = pOwnerThread->GetThreadId(); - m_pOwnerThread = pOwnerThread; - } - - /*++ - Method: - CSynchData::ResetOwnership - - Resets current object's ownership data - - Note: this method must be called while holding the appropriate - synchronization locks (the local process synch lock if the target - object is local, both local and shared one if the object is shared). - --*/ - void CSynchData::ResetOwnership() - { - VALIDATEOBJECT(this); - - m_lOwnershipCount = 0; - m_dwOwnerPid = 0; - m_dwOwnerTid = 0; - m_pOwnerThread = NULL; - m_poolnOwnedObjectListNode = NULL; - } - - /*++ - Method: - CSynchData::AssignOwnershipToThread - - Assigns thw ownership of the current object to the target thread, performing - all the operations neede to mantain the correct status of ownership data, - also handling recursive object ownership acquisition - - Note: this method must be called while holding the appropriate - synchronization locks (the local process synch lock if the target - object is local, both local and shared one if the object is shared). - --*/ - PAL_ERROR CSynchData::AssignOwnershipToThread( - CPalThread * pthrCurrent, - CPalThread * pthrTarget) - { - // Note: when this method is called by ReleaseFirstWaiter there is - // a small time window in which both SignalCount and - // OwnershipCount can be greater than zero (which normally - // is illegal). Anyway that is fine since ReleaseFirstWaiter - // will restore the value right after, and such situation - // takes place while holding synchroniztion locks, so no - // other thread/process can access the object. - - PAL_ERROR palErr = NO_ERROR; - - _ASSERT_MSG(CObjectType::OwnershipTracked == - GetObjectType()->GetOwnershipSemantics(), - "AssignOwnershipToThread called on a non-ownable " - "CSynchData [this=%p OwnershipSemantics=%u]\n", this, - GetObjectType()->GetOwnershipSemantics()); - - - if (0 < m_lOwnershipCount) - { - // - // Object already owned, incrementing ownership count - // - _ASSERT_MSG(0 == GetSignalCount(), - "Conflicting OwnershipCount and SignalCount values\n"); - - _ASSERT_MSG(pthrTarget == m_pOwnerThread && gPID == m_dwOwnerPid, - "Attempting to assign ownership of CSynchData %p to " - "thread {pid=%#x tid=%#x} while it is currently owned " - "by thread {pid=%#x tid=%#x}\n", this, - gPID, pthrTarget->GetThreadId(), - m_dwOwnerPid, m_pOwnerThread->GetThreadId()); - - m_lOwnershipCount++; - - TRACE("Incrementing ownership count for object with " - "SynchData %p owned by thread %#x [new count=%d]\n", - this, pthrTarget->GetThreadId(), m_lOwnershipCount); - } - else - { - // - // Acquiring currently not owned object - // - CPalSynchronizationManager * pSynchManager = - CPalSynchronizationManager::GetInstance(); - OwnedObjectsListNode * pooln; - - pooln = pSynchManager->CacheGetOwnedObjsListNode(pthrCurrent); - if (NULL == pooln) - { - ERROR("Out of memory while acquiring mutex ownership"); - // In this case we bail out. It will result in no - // thread being awakend, which may cause deadlock, - // but it is anyway better than corrupting the - // ownership list - palErr = ERROR_NOT_ENOUGH_MEMORY; - goto AOTT_exit; - } - - TRACE("Assigning ownable object with SynchData %p to " - "thread %#x\n", - this, pthrTarget->GetThreadId()); - - // Set ownership data - SetOwner(pthrTarget); - SetOwnershipListNode(pooln); - SetOwnershipCount(1); - SetAbandoned(false); - - // Add object to list of owned objs for current thread - pooln->pPalObjSynchData = this; - AddRef(); - pthrTarget->synchronizationInfo.AddObjectToOwnedList(pooln); - } - - AOTT_exit: - return palErr; - } - /*++ Method: CSynchData::WaiterEnqueue diff --git a/src/coreclr/pal/src/synchmgr/synchmanager.cpp b/src/coreclr/pal/src/synchmgr/synchmanager.cpp index a2fa13c742cedd..4cf80dd3aa6a1e 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.cpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.cpp @@ -159,8 +159,7 @@ namespace CorUnix m_cacheSHRSynchData(SynchDataCacheMaxSize), m_cacheWTListNodes(WTListNodeCacheMaxSize), m_cacheSHRWTListNodes(WTListNodeCacheMaxSize), - m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize), - m_cacheOwnedObjectsListNodes(OwnedObjectsListCacheMaxSize) + m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize) { #if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT m_iKQueue = -1; @@ -396,7 +395,6 @@ namespace CorUnix break; } case WaitSucceeded: - case MutexAbandoned: *pdwSignaledObject = dwSigObjIdx; break; default: @@ -533,98 +531,6 @@ namespace CorUnix return palErr; } - /*++ - Method: - CPalSynchronizationManager::AbandonObjectsOwnedByThread - - This method is called by a thread at thread-exit time to abandon - any currently owned waitable object (mutexes). If pthrTarget is - different from pthrCurrent, AbandonObjectsOwnedByThread assumes - to be called whether by TerminateThread or at shutdown time. See - comments below for more details - --*/ - PAL_ERROR CPalSynchronizationManager::AbandonObjectsOwnedByThread( - CPalThread * pthrCurrent, - CPalThread * pthrTarget) - { - PAL_ERROR palErr = NO_ERROR; - OwnedObjectsListNode * poolnItem; - CThreadSynchronizationInfo * pSynchInfo = &pthrTarget->synchronizationInfo; - CPalSynchronizationManager * pSynchManager = GetInstance(); - - // Local lock - AcquireLocalSynchLock(pthrCurrent); - - // Abandon owned objects - while (NULL != (poolnItem = pSynchInfo->RemoveFirstObjectFromOwnedList())) - { - CSynchData * psdSynchData = poolnItem->pPalObjSynchData; - - _ASSERT_MSG(NULL != psdSynchData, - "NULL psdSynchData pointer in ownership list node\n"); - - VALIDATEOBJECT(psdSynchData); - - TRACE("Abandoning object with SynchData at %p\n", psdSynchData); - - // Reset ownership data - psdSynchData->ResetOwnership(); - - // Set abandoned status; in case there is a thread to be released: - // - if the thread is local, ReleaseFirstWaiter will reset the - // abandoned status - // - if the thread is remote, the remote worker thread will use - // the value and reset it - psdSynchData->SetAbandoned(true); - - // Signal the object and trigger thread awakening - psdSynchData->Signal(pthrCurrent, 1); - - // Release reference to SynchData - psdSynchData->Release(pthrCurrent); - - // Return node to the cache - pSynchManager->m_cacheOwnedObjectsListNodes.Add(pthrCurrent, poolnItem); - } - - if (pthrTarget != pthrCurrent) - { - // If the target thead is not the current one, we are being called - // at shutdown time, right before the target thread is suspended, - // or anyway the target thread is being terminated. - // In this case we switch its wait state to TWS_EARLYDEATH so that, - // if the thread is currently waiting/sleeping and it wakes up - // before shutdown code manage to suspend it, it will be rerouted - // to ThreadPrepareForShutdown (that will be done without holding - // any internal lock, in a way to accommodate shutdown time thread - // suspension). - // At this time we also unregister the wait, so no dummy nodes are - // left around on waiting objects. - // The TWS_EARLYDEATH wait-state will also prevent the thread from - // successfully registering for a possible new wait in the same - // time window. - LONG lTWState; - DWORD * pdwWaitState; - - pdwWaitState = SharedIDToTypePointer(DWORD, pthrTarget->synchronizationInfo.m_shridWaitAwakened); - lTWState = InterlockedExchange((LONG *)pdwWaitState, TWS_EARLYDEATH); - - if (( ((LONG)TWS_WAITING == lTWState) || ((LONG)TWS_ALERTABLE == lTWState) ) && - (0 < pSynchInfo->m_twiWaitInfo.lObjCount)) - { - // Unregister the wait - UnRegisterWait(pthrCurrent, &pSynchInfo->m_twiWaitInfo); - } - } - - // Unlock - ReleaseLocalSynchLock(pthrCurrent); - - DiscardAllPendingAPCs(pthrCurrent, pthrTarget); - - return palErr; - } - /*++ Method: CPalSynchronizationManager::GetSynchWaitControllersForObjects @@ -3159,52 +3065,6 @@ namespace CorUnix return palErr; } - - /*++ - Method: - CThreadSynchronizationInfo::AddObjectToOwnedList - - Adds an object to the list of currently owned objects. - --*/ - void CThreadSynchronizationInfo::AddObjectToOwnedList(POwnedObjectsListNode pooln) - { - InsertTailList(&m_leOwnedObjsList, &pooln->Link); - } - - /*++ - Method: - CThreadSynchronizationInfo::RemoveObjectFromOwnedList - - Removes an object from the list of currently owned objects. - --*/ - void CThreadSynchronizationInfo::RemoveObjectFromOwnedList(POwnedObjectsListNode pooln) - { - RemoveEntryList(&pooln->Link); - } - - /*++ - Method: - CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList - - Removes the first object from the list of currently owned objects. - --*/ - POwnedObjectsListNode CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList() - { - OwnedObjectsListNode * poolnItem; - - if (IsListEmpty(&m_leOwnedObjsList)) - { - poolnItem = NULL; - } - else - { - PLIST_ENTRY pLink = RemoveHeadList(&m_leOwnedObjsList); - poolnItem = CONTAINING_RECORD(pLink, OwnedObjectsListNode, Link); - } - - return poolnItem; - } - #if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING /*++ diff --git a/src/coreclr/pal/src/synchmgr/synchmanager.hpp b/src/coreclr/pal/src/synchmgr/synchmanager.hpp index 7d86ab6fb12d4d..f1bb0df602401d 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.hpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.hpp @@ -116,12 +116,6 @@ namespace CorUnix CPalThread * pthrTarget; } DeferredSignalingListNode; - typedef struct _OwnedObjectsListNode - { - LIST_ENTRY Link; - CSynchData * pPalObjSynchData; - } OwnedObjectsListNode; - typedef struct _ThreadApcInfoNode { struct _ThreadApcInfoNode * pNext; @@ -147,15 +141,6 @@ namespace CorUnix LONG m_lRefCount; LONG m_lSignalCount; - // Ownership data - LONG m_lOwnershipCount; - DWORD m_dwOwnerPid; - DWORD m_dwOwnerTid; // used only by remote processes - // (thread ids may be recycled) - CPalThread * m_pOwnerThread; // valid only on the target process - OwnedObjectsListNode * m_poolnOwnedObjectListNode; - bool m_fAbandoned; - #ifdef SYNCH_STATISTICS ULONG m_lStatWaitCount; ULONG m_lStatContentionCount; @@ -164,9 +149,7 @@ namespace CorUnix public: CSynchData() : m_ulcWaitingThreads(0), m_lRefCount(1), - m_lSignalCount(0), m_lOwnershipCount(0), m_dwOwnerPid(0), - m_dwOwnerTid(0), m_pOwnerThread(NULL), - m_poolnOwnedObjectListNode(NULL), m_fAbandoned(false) + m_lSignalCount(0) { // m_ptrWTLHead, m_ptrWTLTail // and m_otiObjectTypeId are initialized by @@ -190,8 +173,7 @@ namespace CorUnix LONG Release(CPalThread * pthrCurrent); bool CanWaiterWaitWithoutBlocking( - CPalThread * pWaiterThread, - bool * pfAbandoned); + CPalThread * pWaiterThread); PAL_ERROR ReleaseWaiterWithoutBlocking( CPalThread * pthrCurrent, @@ -248,48 +230,6 @@ namespace CorUnix return --m_lSignalCount; } - // Object ownership accessor methods - void SetOwner(CPalThread * pOwnerThread); - void ResetOwnership(void); - PAL_ERROR AssignOwnershipToThread( - CPalThread * pthrCurrent, - CPalThread * pthrTarget); - DWORD GetOwnerProcessID(void) - { - return m_dwOwnerPid; - } - DWORD GetOwnerThreadID(void) - { - return m_dwOwnerTid; - } - CPalThread * GetOwnerThread(void) - { - return m_pOwnerThread; - } - OwnedObjectsListNode * GetOwnershipListNode(void) - { - return m_poolnOwnedObjectListNode; - } - void SetOwnershipListNode(OwnedObjectsListNode * pooln) - { - m_poolnOwnedObjectListNode = pooln; - } - - // Object ownership count accessor methods - LONG GetOwnershipCount(void) - { - return m_lOwnershipCount; - } - void SetOwnershipCount(LONG lOwnershipCount) - { - m_lOwnershipCount = lOwnershipCount; - } - - // Object abandoned flag accessor methods - void SetAbandoned(bool fAbandoned) - { m_fAbandoned = fAbandoned; } - bool IsAbandoned(void) { return m_fAbandoned; } - void IncrementWaitingThreadCount(void) { m_ulcWaitingThreads += 1; @@ -419,8 +359,7 @@ namespace CorUnix // ISynchWaitController methods // virtual PAL_ERROR CanThreadWaitWithoutBlocking( - bool * pfCanWaitWithoutBlocking, - bool * pfAbandoned); + bool * pfCanWaitWithoutBlocking); virtual PAL_ERROR ReleaseWaitingThreadWithoutBlocking(void); @@ -452,8 +391,6 @@ namespace CorUnix virtual PAL_ERROR SetSignalCount(LONG lNewCount); virtual PAL_ERROR IncrementSignalCount(LONG lAmountToIncrement); virtual PAL_ERROR DecrementSignalCount(LONG lAmountToDecrement); - virtual PAL_ERROR SetOwner(CPalThread *pNewOwningThread); - virtual PAL_ERROR DecrementOwnershipCount(void); virtual void ReleaseController(void); }; @@ -470,7 +407,6 @@ namespace CorUnix typedef CSynchCache CWaitingThreadsListNodeCache; typedef CSHRSynchCache CSHRWaitingThreadsListNodeCache; typedef CSynchCache CThreadApcInfoNodeCache; - typedef CSynchCache COwnedObjectsListNodeCache; private: // types @@ -510,7 +446,6 @@ namespace CorUnix static const int SynchDataCacheMaxSize = 256; static const int WTListNodeCacheMaxSize = 256; static const int ApcInfoNodeCacheMaxSize = 32; - static const int OwnedObjectsListCacheMaxSize = 16; static const int MaxWorkerConsecutiveEintrs = 128; static const int MaxConsecutiveEagains = 128; static const int WorkerThreadProcMonitoringTimeout = 250; // ms @@ -548,7 +483,6 @@ namespace CorUnix CWaitingThreadsListNodeCache m_cacheWTListNodes; CSHRWaitingThreadsListNodeCache m_cacheSHRWTListNodes; CThreadApcInfoNodeCache m_cacheThreadApcInfoNodes; - COwnedObjectsListNodeCache m_cacheOwnedObjectsListNodes; // static methods static PAL_ERROR Initialize(); @@ -715,19 +649,6 @@ namespace CorUnix m_cacheThreadApcInfoNodes.Add(pthrCurrent, pNode); } - OwnedObjectsListNode * CacheGetOwnedObjsListNode( - CPalThread * pthrCurrent) - { - return m_cacheOwnedObjectsListNodes.Get(pthrCurrent); - } - void CacheAddOwnedObjsListNode( - CPalThread * pthrCurrent, - OwnedObjectsListNode * pNode) - { - m_cacheOwnedObjectsListNodes.Add(pthrCurrent, pNode); - } - - // // IPalSynchronizationManager methods // @@ -739,10 +660,6 @@ namespace CorUnix ThreadWakeupReason *ptwrWakeupReason, DWORD *pdwSignaledObject); - virtual PAL_ERROR AbandonObjectsOwnedByThread( - CPalThread *pthrCurrent, - CPalThread *pthrTarget); - virtual PAL_ERROR GetSynchWaitControllersForObjects( CPalThread *pthrCurrent, IPalObject *rgObjects[], diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index 781cf1bce3a34e..51a9da87483798 100644 --- a/src/coreclr/pal/src/synchmgr/wait.cpp +++ b/src/coreclr/pal/src/synchmgr/wait.cpp @@ -328,7 +328,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( PAL_ERROR palErr = NO_ERROR; int i, iSignaledObjCount, iSignaledObjIndex = -1; bool fWAll = (bool)bWaitAll, fNeedToBlock = false; - bool fAbandoned = false; WaitType wtWaitType; IPalObject * pIPalObjStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL }; @@ -444,8 +443,8 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( iSignaledObjIndex = -1; for (i=0;i<(int)nCount;i++) { - bool fValue, fWaitObjectAbandoned = false; - palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue, &fWaitObjectAbandoned); + bool fValue; + palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue); if (NO_ERROR != palErr) { ERROR("ISynchWaitController::CanThreadWaitWithoutBlocking() failed for " @@ -453,10 +452,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( pThread->SetLastError(ERROR_INTERNAL_ERROR); goto WFMOExIntReleaseControllers; } - if (fWaitObjectAbandoned) - { - fAbandoned = true; - } if (fValue) { iSignaledObjCount++; @@ -505,7 +500,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( } } - dwRet = (fAbandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0); + dwRet = WAIT_OBJECT_0; } else if (0 == dwMilliseconds) { @@ -571,9 +566,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( case WaitSucceeded: dwRet = WAIT_OBJECT_0; // offset added later break; - case MutexAbandoned: - dwRet = WAIT_ABANDONED_0; // offset added later - break; case WaitTimeout: dwRet = WAIT_TIMEOUT; break; @@ -595,14 +587,14 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( } } - if (!fWAll && ((WAIT_OBJECT_0 == dwRet) || (WAIT_ABANDONED_0 == dwRet))) + if (!fWAll && (WAIT_OBJECT_0 == dwRet)) { _ASSERT_MSG(0 <= iSignaledObjIndex, - "Failed to identify signaled/abandoned object\n"); + "Failed to identify signaled object\n"); _ASSERT_MSG(iSignaledObjIndex >= 0 && nCount > static_cast(iSignaledObjIndex), "SignaledObjIndex object out of range " "[index=%d obj_count=%u\n", - iSignaledObjCount, nCount); + iSignaledObjIndex, nCount); if (iSignaledObjIndex < 0) { @@ -784,9 +776,6 @@ DWORD CorUnix::InternalSleepEx ( palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); _ASSERT_MSG(NO_ERROR == palErr, "Awakened for APC, but no APC is pending\n"); - break; - case MutexAbandoned: - ASSERT("Thread %p awakened with reason=MutexAbandoned from a SleepEx\n", pThread); break; case WaitFailed: default: diff --git a/src/coreclr/pal/src/synchobj/event.cpp b/src/coreclr/pal/src/synchobj/event.cpp index 619a55f28a4595..c59ebf52a05946 100644 --- a/src/coreclr/pal/src/synchobj/event.cpp +++ b/src/coreclr/pal/src/synchobj/event.cpp @@ -39,8 +39,7 @@ CObjectType CorUnix::otManualResetEvent( NULL, // No process local data cleanup routine CObjectType::WaitableObject, CObjectType::ObjectCanBeUnsignaled, - CObjectType::ThreadReleaseHasNoSideEffects, - CObjectType::NoOwner + CObjectType::ThreadReleaseHasNoSideEffects ); CObjectType CorUnix::otAutoResetEvent( @@ -53,8 +52,7 @@ CObjectType CorUnix::otAutoResetEvent( NULL, // No process local data cleanup routine CObjectType::WaitableObject, CObjectType::ObjectCanBeUnsignaled, - CObjectType::ThreadReleaseAltersSignalCount, - CObjectType::NoOwner + CObjectType::ThreadReleaseAltersSignalCount ); PalObjectTypeId rgEventIds[] = {otiManualResetEvent, otiAutoResetEvent}; diff --git a/src/coreclr/pal/src/synchobj/semaphore.cpp b/src/coreclr/pal/src/synchobj/semaphore.cpp index 3ea6fd487691ba..c16cb9a4677d6b 100644 --- a/src/coreclr/pal/src/synchobj/semaphore.cpp +++ b/src/coreclr/pal/src/synchobj/semaphore.cpp @@ -39,8 +39,7 @@ CObjectType CorUnix::otSemaphore( NULL, // No process local data cleanup routine CObjectType::WaitableObject, CObjectType::ObjectCanBeUnsignaled, - CObjectType::ThreadReleaseAltersSignalCount, - CObjectType::NoOwner + CObjectType::ThreadReleaseAltersSignalCount ); CAllowedObjectTypes aotSempahore(otiSemaphore); diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index e0ea3f4b0c6c81..fbf05fca824b79 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -131,8 +131,7 @@ CObjectType CorUnix::otProcess( NULL, // No process local data cleanup routine CObjectType::WaitableObject, CObjectType::SingleTransitionObject, - CObjectType::ThreadReleaseHasNoSideEffects, - CObjectType::NoOwner + CObjectType::ThreadReleaseHasNoSideEffects ); CAllowedObjectTypes aotProcess(otiProcess); diff --git a/src/coreclr/pal/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp index fbe9d8501d03f1..7acf3439735b6e 100644 --- a/src/coreclr/pal/src/thread/thread.cpp +++ b/src/coreclr/pal/src/thread/thread.cpp @@ -103,8 +103,7 @@ CObjectType CorUnix::otThread( NULL, // No process local data cleanup routine CObjectType::WaitableObject, CObjectType::SingleTransitionObject, - CObjectType::ThreadReleaseHasNoSideEffects, - CObjectType::NoOwner + CObjectType::ThreadReleaseHasNoSideEffects ); CAllowedObjectTypes aotThread(otiThread); @@ -796,20 +795,6 @@ CorUnix::InternalEndCurrentThread( PAL_ERROR palError = NO_ERROR; ISynchStateController *pSynchStateController = NULL; - // - // Abandon any objects owned by this thread - // - - palError = g_pSynchronizationManager->AbandonObjectsOwnedByThread( - pThread, - pThread - ); - - if (NO_ERROR != palError) - { - ERROR("Failure abandoning owned objects"); - } - // // Need to synchronize setting the thread state to TS_DONE since // this is checked for in InternalSuspendThreadFromData. diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.cpp index 8650b2140a2ada..ba98229594ed0a 100644 --- a/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test1/CreateSemaphore.cpp @@ -95,13 +95,6 @@ down(HANDLE hSemaphore) * semaphore. */ break; - case WAIT_ABANDONED: /* - * Object was mutex object whose owning - * thread has terminated. Shouldn't occur. - */ - Fail("WaitForSingleObject call returned 'WAIT_ABANDONED'.\n" - "Failing Test.\n"); - break; case WAIT_FAILED: /* WaitForSingleObject function failed */ Fail("WaitForSingleObject call returned 'WAIT_FAILED'.\n" "GetLastError returned %d\nFailing Test.\n",GetLastError()); diff --git a/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.cpp b/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.cpp index 85c10482ccc6c0..f5601c4f782686 100644 --- a/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.cpp @@ -95,13 +95,6 @@ down_CreateSemaphoreW_test2(HANDLE hSemaphore) * semaphore. */ break; - case WAIT_ABANDONED: /* - * Object was mutex object whose owning - * thread has terminated. Shouldn't occur. - */ - Fail("WaitForSingleObject call returned 'WAIT_ABANDONED'.\n" - "Failing Test.\n"); - break; case WAIT_FAILED: /* WaitForSingleObject function failed */ Fail("WaitForSingleObject call returned 'WAIT_FAILED'.\n" "GetLastError returned %d\nFailing Test.\n",GetLastError()); diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp index dae1fab6f3458a..f850973a4cdea2 100644 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp @@ -82,14 +82,6 @@ switch (dwWaitResult) return FALSE; } - // Got ownership of the abandoned process object. - case WAIT_ABANDONED: - { - Fail ( "Got ownership of the abandoned Process object. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - return FALSE; - } - //Error condition case WAIT_FAILED: { diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.cpp index 589e27a72223bb..6cdd34cb790735 100644 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.cpp +++ b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.cpp @@ -3,14 +3,14 @@ /*============================================================ ** -** Source: WFSOThreadTest.c +** Source: WFSOThreadTest.c ** -** Purpose: Test for WaitForSingleObjectTest. +** Purpose: Test for WaitForSingleObjectTest. ** Create One Thread and do some work -** Use WFSO For the Thread to finish -** +** Use WFSO For the Thread to finish +** ** Test Passes if the above operations are successful -** +** ** ** **=========================================================*/ @@ -33,10 +33,10 @@ PALTEST(threading_WaitForSingleObject_WFSOThreadTest_paltest_waitforsingleobject { //Declare local variables - DWORD dwThreadId=0; - DWORD dwWaitResult=0; + DWORD dwThreadId=0; + DWORD dwWaitResult=0; - //Initialize PAL + //Initialize PAL if(0 != (PAL_Initialize(argc, argv))) { return ( FAIL ); @@ -51,101 +51,84 @@ PALTEST(threading_WaitForSingleObject_WFSOThreadTest_paltest_waitforsingleobject "GetLastError returned %d\n", GetLastError()); } - + //Create Thread hThread_WFSOThreadTest = CreateThread( - NULL, - 0, - incrementCounter, - NULL, - 0, + NULL, + 0, + incrementCounter, + NULL, + 0, &dwThreadId); - if ( NULL == hThread_WFSOThreadTest ) + if ( NULL == hThread_WFSOThreadTest ) { Fail ( "CreateThread() returned NULL. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); + "GetLastError returned %d\n", GetLastError()); } - //Wait For Thread to signal start + //Wait For Thread to signal start dwWaitResult = WaitForSingleObject(hEvent_WFSOThreadTest,INFINITE); - - switch (dwWaitResult) + + switch (dwWaitResult) { // The thread wait was successful - case WAIT_OBJECT_0: + case WAIT_OBJECT_0: { Trace ("Wait for Single Object (hEvent) was successful.\n"); - break; - } + break; + } // Time-out. - case WAIT_TIMEOUT: + case WAIT_TIMEOUT: { Fail ( "Time -out. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); + "GetLastError returned %d\n", GetLastError()); return FALSE; } - - // Got ownership of the abandoned event object. - case WAIT_ABANDONED: - { - Fail ( "Got ownership of the abandoned event object. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - return FALSE; - } - } - - //Wait for Thread to finish - dwWaitResult = WaitForSingleObject( + + //Wait for Thread to finish + dwWaitResult = WaitForSingleObject( hThread_WFSOThreadTest, //handle to thread 5000L); //Wait Indefinitely - - switch (dwWaitResult) + + switch (dwWaitResult) { // The thread wait was successful - case WAIT_OBJECT_0: + case WAIT_OBJECT_0: { Trace("Wait for thread was successful\n"); - - break; - } + + break; + } // Time-out. - case WAIT_TIMEOUT: + case WAIT_TIMEOUT: { Fail ( "Time -out. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); + "GetLastError returned %d\n", GetLastError()); return FALSE; } - // Got ownership of the abandoned thread object. - case WAIT_ABANDONED: - { - Fail ( "Got ownership of the abandoned thread object. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - return FALSE; - } - } //Close Handles if (0==CloseHandle(hEvent_WFSOThreadTest)) { - Trace("Could not Close event handle\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); + Trace("Could not Close event handle\n"); + Fail ( "GetLastError returned %d\n", GetLastError()); } if (0==CloseHandle(hThread_WFSOThreadTest)) { - Trace("Could not Close thread handle\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); + Trace("Could not Close thread handle\n"); + Fail ( "GetLastError returned %d\n", GetLastError()); } PAL_Terminate(); @@ -160,13 +143,13 @@ DWORD PALAPI incrementCounter(LPVOID params) if (0==SetEvent(hEvent_WFSOThreadTest)) { Fail ( "SetEvent returned Zero. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); + "GetLastError returned %d\n", GetLastError()); } for (globalcounter_WFSOThreadTest=0;globalcounter_WFSOThreadTest<100000;globalcounter_WFSOThreadTest++); //Sleep(5000); - + Trace("Global Counter Value: %d \n", globalcounter_WFSOThreadTest); return 0; } From 2358bb40c656ab337eba7ad5fb3b18813f38557b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 29 Jul 2025 21:47:18 +0000 Subject: [PATCH 09/33] Change spinlock scoping --- src/coreclr/pal/src/thread/threadsusp.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/coreclr/pal/src/thread/threadsusp.cpp b/src/coreclr/pal/src/thread/threadsusp.cpp index 74462ce4fc8d9f..ec0dc9c5b92594 100644 --- a/src/coreclr/pal/src/thread/threadsusp.cpp +++ b/src/coreclr/pal/src/thread/threadsusp.cpp @@ -50,14 +50,18 @@ suspension mutex or spinlock. The downside is that it restricts us to only performing one suspension or resumption in the PAL at a time. */ #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION +namespace +{ + LONG g_ssSuspensionLock = 0; +} +#endif + #define SYNCSPINLOCK_F_ASYMMETRIC 1 #define SPINLOCKInit(lock) (*(lock) = 0) namespace { - LONG g_ssSuspensionLock = 0; - /* Basic spinlock implementation */ void SPINLOCKAcquire (LONG * lock, unsigned int flags) { @@ -94,9 +98,7 @@ namespace return InterlockedCompareExchange(lock, 1, 0); // only returns 0 or 1. } - } -#endif /*++ Function: From 1892e04f7524ec4ac57086f2fbd96fa183476944 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 29 Jul 2025 22:26:49 +0000 Subject: [PATCH 10/33] Remove prioritized waits from the PAL and remove WAIT_ABANDONED usage --- src/coreclr/pal/src/include/pal/corunix.hpp | 3 +- .../pal/src/include/pal/synchobjects.hpp | 3 +- .../pal/src/synchmgr/synchcontrollers.cpp | 65 +++++-------------- src/coreclr/pal/src/synchmgr/synchmanager.hpp | 5 +- src/coreclr/pal/src/synchmgr/wait.cpp | 6 +- src/coreclr/vm/finalizerthread.cpp | 4 -- src/coreclr/vm/threads.cpp | 10 ++- 7 files changed, 33 insertions(+), 63 deletions(-) diff --git a/src/coreclr/pal/src/include/pal/corunix.hpp b/src/coreclr/pal/src/include/pal/corunix.hpp index 17d83a8ac0d189..8e57767be3a07a 100644 --- a/src/coreclr/pal/src/include/pal/corunix.hpp +++ b/src/coreclr/pal/src/include/pal/corunix.hpp @@ -571,8 +571,7 @@ namespace CorUnix RegisterWaitingThread( WaitType eWaitType, DWORD dwIndex, - bool fAltertable, - bool fPrioritize + bool fAltertable ) = 0; // diff --git a/src/coreclr/pal/src/include/pal/synchobjects.hpp b/src/coreclr/pal/src/include/pal/synchobjects.hpp index 2c21da9b2d3a1b..407df2b52b510c 100644 --- a/src/coreclr/pal/src/include/pal/synchobjects.hpp +++ b/src/coreclr/pal/src/include/pal/synchobjects.hpp @@ -37,8 +37,7 @@ namespace CorUnix CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds, - BOOL bAlertable, - BOOL bPrioritize = FALSE); + BOOL bAlertable); DWORD InternalSignalObjectAndWait( CPalThread *thread, diff --git a/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp b/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp index 99ca5a966f970b..945268b31f49a6 100644 --- a/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp +++ b/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp @@ -247,8 +247,7 @@ namespace CorUnix PAL_ERROR CSynchWaitController::RegisterWaitingThread( WaitType wtWaitType, DWORD dwIndex, - bool fAlertable, - bool fPrioritize) + bool fAlertable) { VALIDATEOBJECT(m_psdSynchData); @@ -366,7 +365,7 @@ namespace CorUnix } // Add new node to queue - m_psdSynchData->WaiterEnqueue(pwtlnNewNode, fPrioritize); + m_psdSynchData->WaiterEnqueue(pwtlnNewNode); // Succeeded: update object count ptwiWaitInfo->lObjCount++; @@ -1113,60 +1112,32 @@ namespace CorUnix Note: this method must be called while holding the local process synchronization lock. --*/ - void CSynchData::WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode, bool fPrioritize) + void CSynchData::WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode) { VALIDATEOBJECT(this); VALIDATEOBJECT(pwtlnNewNode); - if (!fPrioritize) - { - // Enqueue normally to the end of the queue - WaitingThreadsListNode * pwtlnCurrLast = m_ptrWTLTail.ptr; + // Enqueue normally to the end of the queue + WaitingThreadsListNode * pwtlnCurrLast = m_ptrWTLTail.ptr; - pwtlnNewNode->ptrNext.ptr = NULL; - if (NULL == pwtlnCurrLast) - { - _ASSERT_MSG(NULL == m_ptrWTLHead.ptr, - "Corrupted waiting list on local CSynchData @ %p\n", - this); - - pwtlnNewNode->ptrPrev.ptr = NULL; - m_ptrWTLHead.ptr = pwtlnNewNode; - m_ptrWTLTail.ptr = pwtlnNewNode; - } - else - { - VALIDATEOBJECT(pwtlnCurrLast); + pwtlnNewNode->ptrNext.ptr = NULL; + if (NULL == pwtlnCurrLast) + { + _ASSERT_MSG(NULL == m_ptrWTLHead.ptr, + "Corrupted waiting list on local CSynchData @ %p\n", + this); - pwtlnNewNode->ptrPrev.ptr = pwtlnCurrLast; - pwtlnCurrLast->ptrNext.ptr = pwtlnNewNode; - m_ptrWTLTail.ptr = pwtlnNewNode; - } + pwtlnNewNode->ptrPrev.ptr = NULL; + m_ptrWTLHead.ptr = pwtlnNewNode; + m_ptrWTLTail.ptr = pwtlnNewNode; } else { - // The wait is prioritized, enqueue to the beginning of the queue - WaitingThreadsListNode * pwtlnCurrFirst = m_ptrWTLHead.ptr; + VALIDATEOBJECT(pwtlnCurrLast); - pwtlnNewNode->ptrPrev.ptr = NULL; - if (NULL == pwtlnCurrFirst) - { - _ASSERT_MSG(NULL == m_ptrWTLTail.ptr, - "Corrupted waiting list on local CSynchData @ %p\n", - this); - - pwtlnNewNode->ptrNext.ptr = NULL; - m_ptrWTLHead.ptr = pwtlnNewNode; - m_ptrWTLTail.ptr = pwtlnNewNode; - } - else - { - VALIDATEOBJECT(pwtlnCurrFirst); - - pwtlnNewNode->ptrNext.ptr = pwtlnCurrFirst; - pwtlnCurrFirst->ptrPrev.ptr = pwtlnNewNode; - m_ptrWTLHead.ptr = pwtlnNewNode; - } + pwtlnNewNode->ptrPrev.ptr = pwtlnCurrLast; + pwtlnCurrLast->ptrNext.ptr = pwtlnNewNode; + m_ptrWTLTail.ptr = pwtlnNewNode; } m_ulcWaitingThreads += 1; diff --git a/src/coreclr/pal/src/synchmgr/synchmanager.hpp b/src/coreclr/pal/src/synchmgr/synchmanager.hpp index f1bb0df602401d..1b884ab478f722 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.hpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.hpp @@ -179,7 +179,7 @@ namespace CorUnix CPalThread * pthrCurrent, CPalThread * pthrTarget); - void WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode, bool fPrioritize); + void WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode); // Object Type accessor methods CObjectType * GetObjectType(void) @@ -366,8 +366,7 @@ namespace CorUnix virtual PAL_ERROR RegisterWaitingThread( WaitType wtWaitType, DWORD dwIndex, - bool fAlertable, - bool fPrioritize); + bool fAlertable); virtual void ReleaseController(void); diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index 51a9da87483798..d0907ee334629f 100644 --- a/src/coreclr/pal/src/synchmgr/wait.cpp +++ b/src/coreclr/pal/src/synchmgr/wait.cpp @@ -321,8 +321,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds, - BOOL bAlertable, - BOOL bPrioritize) + BOOL bAlertable) { DWORD dwRet = WAIT_FAILED; PAL_ERROR palErr = NO_ERROR; @@ -516,8 +515,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( palErr = ppISyncWaitCtrlrs[i]->RegisterWaitingThread( wtWaitType, i, - (TRUE == bAlertable), - bPrioritize != FALSE); + (TRUE == bAlertable)); if (NO_ERROR != palErr) { ERROR("RegisterWaitingThread() failed for %d-th object " diff --git a/src/coreclr/vm/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp index ef5949ec055404..52e60e6ba95a53 100644 --- a/src/coreclr/vm/finalizerthread.cpp +++ b/src/coreclr/vm/finalizerthread.cpp @@ -269,8 +269,6 @@ void FinalizerThread::WaitForFinalizerEvent (CLREvent *event) { case (WAIT_OBJECT_0): return; - case (WAIT_ABANDONED): - return; case (WAIT_TIMEOUT): break; } @@ -332,8 +330,6 @@ void FinalizerThread::WaitForFinalizerEvent (CLREvent *event) { case (WAIT_OBJECT_0): return; - case (WAIT_ABANDONED): - return; case (WAIT_TIMEOUT): break; } diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 4f82f34f45eb0f..c1a89e6603360d 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -3358,7 +3358,6 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa goto retry; } _ASSERTE((ret >= WAIT_OBJECT_0 && ret < (WAIT_OBJECT_0 + (DWORD)countHandles)) || - (ret >= WAIT_ABANDONED && ret < (WAIT_ABANDONED + (DWORD)countHandles)) || (ret == WAIT_TIMEOUT) || (ret == WAIT_FAILED)); // countHandles is used as an unsigned -- it should never be negative. _ASSERTE(countHandles >= 0); @@ -3456,11 +3455,13 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa DWORD subRet = WaitForSingleObject (handles[i], 0); if ((subRet == WAIT_OBJECT_0) || (subRet == WAIT_FAILED)) break; +#ifdef HOST_WINDOWS if (subRet == WAIT_ABANDONED) { ret = (ret - WAIT_OBJECT_0) + WAIT_ABANDONED; break; } +#endif // HOST_WINDOWS // If we get alerted it just masks the real state of the current // handle, so retry the wait. if (subRet == WAIT_IO_COMPLETION) @@ -3616,11 +3617,18 @@ DWORD Thread::DoSignalAndWaitWorker(HANDLE* pHandles, DWORD millis,BOOL alertabl WaitCompleted: //Check that the return state is valid +#ifdef HOST_WINDOWS _ASSERTE(WAIT_OBJECT_0 == ret || WAIT_ABANDONED == ret || WAIT_TIMEOUT == ret || WAIT_FAILED == ret || ERROR_TOO_MANY_POSTS == ret); +#else + _ASSERTE(WAIT_OBJECT_0 == ret || + WAIT_TIMEOUT == ret || + WAIT_FAILED == ret || + ERROR_TOO_MANY_POSTS == ret); +#endif // HOST_WINDOWS //Wrong to time out if the wait was infinite _ASSERTE((WAIT_TIMEOUT != ret) || (INFINITE != millis)); From b53dad0179d0538921246a3b501164bbe9ef0a79 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 14 Aug 2025 18:53:34 +0000 Subject: [PATCH 11/33] Run "OnThreadExiting" during thread finalization. Testing with Mono has already hardened the cases that this makes weird, so we can reliably do it with CoreCLR --- .../src/System/Threading/Thread.CoreCLR.cs | 13 ++++++++++++- src/coreclr/vm/corelib.h | 1 - src/coreclr/vm/threads.cpp | 9 --------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 4aecd3ab6ec2c0..7f0ee7f767581f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -176,7 +176,18 @@ private void Initialize() private static partial void Initialize(ObjectHandleOnStack thread); /// Clean up the thread when it goes away. - ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. + ~Thread() + { + // During the actual thread shutdown, + // it may not be practical for us to run enough managed code to clean up + // any managed code that needs to know when a thread exits. + // Instead, run that clean up here when the thread object is finalized + // (which is definitely after the thread has exited) + OnThreadExiting(); + + // Do any additional finalization that's required on the unmanaged side. + InternalFinalize(); + } [MethodImpl(MethodImplOptions.InternalCall)] private extern void InternalFinalize(); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 8befc7bbf119c1..0b979353253f72 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -969,7 +969,6 @@ DEFINE_CLASS(DIRECTONTHREADLOCALDATA, Threading, Thread+DirectOnThreadLocalData) DEFINE_CLASS(THREAD, Threading, Thread) DEFINE_METHOD(THREAD, START_CALLBACK, StartCallback, IM_RetVoid) DEFINE_METHOD(THREAD, POLLGC, PollGC, NoSig) -DEFINE_METHOD(THREAD, ON_THREAD_EXITING, OnThreadExiting, IM_RetVoid) #ifdef FEATURE_OBJCMARSHAL DEFINE_CLASS(AUTORELEASEPOOL, Threading, AutoreleasePool) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index c1a89e6603360d..4246ef4ecefe9d 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2725,15 +2725,6 @@ void Thread::CooperativeCleanup() GCX_COOP(); - if (!IsGCSpecial()) - { - // Allow managed subsystems to clean up on thread exit. - PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__ON_THREAD_EXITING); - DECLARE_ARGHOLDER_ARRAY(args, 1); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(GetExposedObject()); - CALL_MANAGED_METHOD_NORET(args); - } - // Clear any outstanding stale EH state that maybe still active on the thread. #ifdef FEATURE_EH_FUNCLETS ExInfo::PopTrackers((void*)-1); From c3549599ea2ec895c5dfb1c727232d0968f8814b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 14 Aug 2025 23:11:05 +0000 Subject: [PATCH 12/33] Cleanup on the finalizer thread when a thread is detached, not when the managed thread object is finalized. --- .../src/System/Threading/Thread.CoreCLR.cs | 13 +------------ src/coreclr/vm/corelib.h | 1 + src/coreclr/vm/threads.cpp | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 7f0ee7f767581f..4aecd3ab6ec2c0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -176,18 +176,7 @@ private void Initialize() private static partial void Initialize(ObjectHandleOnStack thread); /// Clean up the thread when it goes away. - ~Thread() - { - // During the actual thread shutdown, - // it may not be practical for us to run enough managed code to clean up - // any managed code that needs to know when a thread exits. - // Instead, run that clean up here when the thread object is finalized - // (which is definitely after the thread has exited) - OnThreadExiting(); - - // Do any additional finalization that's required on the unmanaged side. - InternalFinalize(); - } + ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. [MethodImpl(MethodImplOptions.InternalCall)] private extern void InternalFinalize(); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 0b979353253f72..861f6626814447 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -969,6 +969,7 @@ DEFINE_CLASS(DIRECTONTHREADLOCALDATA, Threading, Thread+DirectOnThreadLocalData) DEFINE_CLASS(THREAD, Threading, Thread) DEFINE_METHOD(THREAD, START_CALLBACK, StartCallback, IM_RetVoid) DEFINE_METHOD(THREAD, POLLGC, PollGC, NoSig) +DEFINE_METHOD(THREAD, ON_THREAD_EXITING, OnThreadExiting, IM_RetVoid) #ifdef FEATURE_OBJCMARSHAL DEFINE_CLASS(AUTORELEASEPOOL, Threading, AutoreleasePool) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 4246ef4ecefe9d..56e8fe9624a1f6 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2573,7 +2573,8 @@ void Thread::CoUninitialize() void Thread::CleanupDetachedThreads() { CONTRACTL { - NOTHROW; + THROWS; + MODE_COOPERATIVE; GC_TRIGGERS; } CONTRACTL_END; @@ -2594,6 +2595,20 @@ void Thread::CleanupDetachedThreads() { STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - detaching thread 0x%p\n", thread); + if (!thread->IsGCSpecial()) + { + GCX_COOP(); + // During the actual thread shutdown, + // it may not be practical for us to run enough managed code to clean up + // any managed code that needs to know when a thread exits. + // Instead, run that clean up here when the thread object is finalized + // (which is definitely after the thread has exited) + PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__ON_THREAD_EXITING); + DECLARE_ARGHOLDER_ARRAY(args, 1); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(thread->GetExposedObject()); + CALL_MANAGED_METHOD_NORET(args); + } + // Unmark that the thread is detached while we have the // thread store lock. This will ensure that no other // thread will race in here and try to delete it, too. From 2daf216d900daee79a0a49472d08711c90037ad6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 14 Aug 2025 23:54:39 +0000 Subject: [PATCH 13/33] Call OnThreadExiting outside of the thread store lock --- src/coreclr/vm/threads.cpp | 152 ++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 69 deletions(-) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 56e8fe9624a1f6..fa301c21900db5 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2581,100 +2581,114 @@ void Thread::CleanupDetachedThreads() _ASSERTE(!ThreadStore::HoldingThreadStore()); - ThreadStoreLockHolder threadStoreLockHolder; + ArrayList threadsWithManagedCleanup; - Thread *thread = ThreadStore::GetAllThreadList(NULL, 0, 0); + { + ThreadStoreLockHolder threadStoreLockHolder; - STRESS_LOG0(LF_SYNC, LL_INFO1000, "T::CDT called\n"); + Thread *thread = ThreadStore::GetAllThreadList(NULL, 0, 0); - while (thread != NULL) - { - Thread *next = ThreadStore::GetAllThreadList(thread, 0, 0); + STRESS_LOG0(LF_SYNC, LL_INFO1000, "T::CDT called\n"); - if (thread->IsDetached()) + while (thread != NULL) { - STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - detaching thread 0x%p\n", thread); + Thread *next = ThreadStore::GetAllThreadList(thread, 0, 0); - if (!thread->IsGCSpecial()) + if (thread->IsDetached()) { - GCX_COOP(); - // During the actual thread shutdown, - // it may not be practical for us to run enough managed code to clean up - // any managed code that needs to know when a thread exits. - // Instead, run that clean up here when the thread object is finalized - // (which is definitely after the thread has exited) - PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__ON_THREAD_EXITING); - DECLARE_ARGHOLDER_ARRAY(args, 1); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(thread->GetExposedObject()); - CALL_MANAGED_METHOD_NORET(args); - } + STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - detaching thread 0x%p\n", thread); + + if (!thread->IsGCSpecial()) + { + // Record threads that we'll need to do some managed cleanup of + // after we exit the thread store lock. + threadsWithManagedCleanup.Append(thread); + } - // Unmark that the thread is detached while we have the - // thread store lock. This will ensure that no other - // thread will race in here and try to delete it, too. - thread->ResetThreadState(TS_Detached); - InterlockedDecrement(&m_DetachCount); - if (!thread->IsBackground()) - InterlockedDecrement(&m_ActiveDetachCount); - - // If the debugger is attached, then we need to unlock the - // thread store before calling OnThreadTerminate. That - // way, we won't be holding the thread store lock if we - // need to block sending a detach thread event. - BOOL debuggerAttached = + // Unmark that the thread is detached while we have the + // thread store lock. This will ensure that no other + // thread will race in here and try to delete it, too. + thread->ResetThreadState(TS_Detached); + InterlockedDecrement(&m_DetachCount); + if (!thread->IsBackground()) + InterlockedDecrement(&m_ActiveDetachCount); + + // If the debugger is attached, then we need to unlock the + // thread store before calling OnThreadTerminate. That + // way, we won't be holding the thread store lock if we + // need to block sending a detach thread event. + BOOL debuggerAttached = #ifdef DEBUGGING_SUPPORTED - CORDebuggerAttached(); + CORDebuggerAttached(); #else // !DEBUGGING_SUPPORTED - FALSE; + FALSE; #endif // !DEBUGGING_SUPPORTED - if (debuggerAttached) - ThreadStore::UnlockThreadStore(); + if (debuggerAttached) + ThreadStore::UnlockThreadStore(); - thread->OnThreadTerminate(debuggerAttached ? FALSE : TRUE); + thread->OnThreadTerminate(debuggerAttached ? FALSE : TRUE); #ifdef DEBUGGING_SUPPORTED - if (debuggerAttached) + if (debuggerAttached) + { + ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_OTHER); + + // We remember the next Thread in the thread store + // list before deleting the current one. But we can't + // use that Thread pointer now that we release the + // thread store lock in the middle of the loop. We + // have to start from the beginning of the list every + // time. If two threads T1 and T2 race into + // CleanupDetachedThreads, then T1 will grab the first + // Thread on the list marked for deletion and release + // the lock. T2 will grab the second one on the + // list. T2 may complete destruction of its Thread, + // then T1 might re-acquire the thread store lock and + // try to use the next Thread in the thread store. But + // T2 just deleted that next Thread. + thread = ThreadStore::GetAllThreadList(NULL, 0, 0); + } + else +#endif // DEBUGGING_SUPPORTED + { + thread = next; + } + } + else if (thread->HasThreadState(TS_Finalized)) { - ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_OTHER); - - // We remember the next Thread in the thread store - // list before deleting the current one. But we can't - // use that Thread pointer now that we release the - // thread store lock in the middle of the loop. We - // have to start from the beginning of the list every - // time. If two threads T1 and T2 race into - // CleanupDetachedThreads, then T1 will grab the first - // Thread on the list marked for deletion and release - // the lock. T2 will grab the second one on the - // list. T2 may complete destruction of its Thread, - // then T1 might re-acquire the thread store lock and - // try to use the next Thread in the thread store. But - // T2 just deleted that next Thread. - thread = ThreadStore::GetAllThreadList(NULL, 0, 0); + STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - finalized thread 0x%p\n", thread); + + thread->ResetThreadState(TS_Finalized); + // We have finalized the managed Thread object. Now it is time to clean up the unmanaged part + thread->DecExternalCount(TRUE); + thread = next; } else -#endif // DEBUGGING_SUPPORTED { thread = next; } } - else if (thread->HasThreadState(TS_Finalized)) - { - STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - finalized thread 0x%p\n", thread); - thread->ResetThreadState(TS_Finalized); - // We have finalized the managed Thread object. Now it is time to clean up the unmanaged part - thread->DecExternalCount(TRUE); - thread = next; - } - else - { - thread = next; - } + s_fCleanFinalizedThread = FALSE; } - s_fCleanFinalizedThread = FALSE; + ArrayList::Iterator iter = threadsWithManagedCleanup.Iterate(); + while (iter.Next()) + { + // During the actual thread shutdown, + // it may not be practical for us to run enough managed code to clean up + // any managed code that needs to know when a thread exits. + // Instead, run that clean up here when the thread object is finalized + // (which is definitely after the thread has exited) + // + // We know its safe to do this work here as we can't have finalized the managed thread object yet + // as we're running on the finalizer thread right now. + PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__ON_THREAD_EXITING); + DECLARE_ARGHOLDER_ARRAY(args, 1); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(((Thread*)iter.GetElement())->GetExposedObject()); + CALL_MANAGED_METHOD_NORET(args); + } } #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT From e5b8b92af197ad5334f484f5e32e23247574c540 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 15 Aug 2025 14:34:14 -0700 Subject: [PATCH 14/33] pragma out the error that only occurs on platforms that don't use the wait subsystem --- .../src/System/Threading/Thread.CoreCLR.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 4aecd3ab6ec2c0..81bf220bf83a25 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -557,7 +557,9 @@ WaitSubsystem.ThreadWaitInfo AllocateWaitInfo() } #endif +#pragma warning disable CA1822 // Member 'OnThreadExiting' does not access instance data and can be marked as static private void OnThreadExiting() +#pragma warning restore CA1822 // Member 'OnThreadExiting' does not access instance data and can be marked as static { #if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI // Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by From 0e504c5ff68df97b2f9f11a781ca7d71ecee711f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Aug 2025 13:35:09 -0700 Subject: [PATCH 15/33] Use shared wait handling for Windows instead of calling into the VM on Windows --- .../System.Private.CoreLib.csproj | 1 - .../src/System/Threading/Thread.CoreCLR.cs | 29 ++- .../Threading/WaitHandle.CoreCLR.Windows.cs | 35 ---- .../src/System/Runtime/RuntimeImports.cs | 13 +- .../src/System/Threading/Thread.NativeAot.cs | 6 + src/coreclr/vm/CMakeLists.txt | 2 - src/coreclr/vm/comsynchronizable.cpp | 29 +++ src/coreclr/vm/comsynchronizable.h | 2 + src/coreclr/vm/comwaithandle.cpp | 84 -------- src/coreclr/vm/comwaithandle.h | 21 -- src/coreclr/vm/corelib.cpp | 1 - src/coreclr/vm/qcallentrypoints.cpp | 6 +- src/coreclr/vm/threads.cpp | 184 ++---------------- src/coreclr/vm/threads.h | 8 +- .../System.Private.CoreLib.Shared.projitems | 6 +- .../System/Threading/WaitHandle.Windows.cs | 6 +- 16 files changed, 88 insertions(+), 345 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs delete mode 100644 src/coreclr/vm/comwaithandle.cpp delete mode 100644 src/coreclr/vm/comwaithandle.h diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 88992bc63e56ae..b089e0b436671b 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -298,7 +298,6 @@ Common\Interop\Windows\OleAut32\Interop.VariantChangeTypeEx.cs - diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 81bf220bf83a25..2640adff4c16a9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -302,7 +302,6 @@ public ThreadState ThreadState [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")] private static partial int GetThreadState(ThreadHandle t); -#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI internal void SetWaitSleepJoinState() { // This method is called when the thread is about to enter a wait, sleep, or join state. @@ -324,7 +323,6 @@ internal void ClearWaitSleepJoinState() [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ClearWaitSleepJoinState")] private static partial void ClearWaitSleepJoinState(ThreadHandle t); -#endif /// /// An unstarted thread can be marked to indicate that it will host a @@ -375,6 +373,9 @@ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError) return true; } + internal static bool ReentrantWaitsEnabled => + CurrentThread.GetApartmentState() == ApartmentState.STA; + #else // FEATURE_COMINTEROP_APARTMENT_SUPPORT public ApartmentState GetApartmentState() => ApartmentState.Unknown; @@ -392,6 +393,8 @@ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwO return true; } + + internal const bool ReentrantWaitsEnabled = false; #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT #if FEATURE_COMINTEROP @@ -568,6 +571,18 @@ private void OnThreadExiting() #endif } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ReentrantWaitAny")] + internal static unsafe partial int ReentrantWaitAny([MarshalAs(UnmanagedType.Bool)] bool alertable, int timeout, int count, IntPtr* handles); + + internal static void CheckForPendingInterrupt() + { + CheckForPendingInterrupt(CurrentThread.GetNativeHandle()); + GC.KeepAlive(CurrentThread); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_CheckForPendingInterrupt")] + private static partial void CheckForPendingInterrupt(ThreadHandle t); + [StructLayout(LayoutKind.Sequential)] private struct NativeThreadClass { @@ -576,12 +591,12 @@ private struct NativeThreadClass private enum NativeThreadState { - None = 0, - TS_AbortRequested = 0x00000001, // Abort the thread - TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads? - TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only) + None = 0, + TS_AbortRequested = 0x00000001, // Abort the thread + TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads? + TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only) - // We require (and assert) that the following bits are less than 0x100. + // We require (and assert) that the following bits are less than 0x100. TS_CatchAtSafePoint = (TS_AbortRequested | TS_DebugSuspendPending | TS_GCOnTransitions), }; } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs deleted file mode 100644 index a63096dc84941f..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.Windows.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - public abstract partial class WaitHandle - { - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_WaitOneCore")] - private static partial int WaitOneCore(IntPtr waitHandle, int millisecondsTimeout, [MarshalAs(UnmanagedType.Bool)] bool useTrivialWaits); - - private static int WaitMultipleIgnoringSyncContextCore(ReadOnlySpan waitHandles, bool waitAll, int millisecondsTimeout) - => WaitMultipleIgnoringSyncContext(waitHandles, waitHandles.Length, waitAll, millisecondsTimeout); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_WaitMultipleIgnoringSyncContext")] - private static partial int WaitMultipleIgnoringSyncContext(ReadOnlySpan waitHandles, int numHandles, [MarshalAs(UnmanagedType.Bool)] bool waitAll, int millisecondsTimeout); - - private static int SignalAndWaitCore(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout) - { - int ret = SignalAndWait(waitHandleToSignal, waitHandleToWaitOn, millisecondsTimeout); - - if (ret == Interop.Errors.ERROR_TOO_MANY_POSTS) - { - throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); - } - - return ret; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "WaitHandle_SignalAndWait")] - private static partial int SignalAndWait(IntPtr waitHandleToSignal, IntPtr waitHandleToWaitOn, int millisecondsTimeout); - } -} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index b920bac2cd52f8..1c3c039043dc6a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -452,19 +452,8 @@ internal static IntPtr RhHandleAllocCrossReference(object value, IntPtr context) #if !TARGET_UNIX // Wait for any object to be signalled, in a way that's compatible with the CLR's behavior in an STA. - // ExactSpelling = 'true' to force MCG to resolve it to default [LibraryImport(RuntimeLibrary)] - private static unsafe partial int RhCompatibleReentrantWaitAny(int alertable, int timeout, int count, IntPtr* handles); - - // Temporary workaround to unblock shareable assembly bring-up - without shared interop, - // we must prevent RhCompatibleReentrantWaitAny from using marshaling because it would - // rewrite System.Private.CoreLib to reference the non-shareable interop assembly. With shared interop, - // we will be able to remove this helper method and change the DllImport above - // to directly accept a boolean parameter and use the SetLastError = true modifier. - internal static unsafe int RhCompatibleReentrantWaitAny(bool alertable, int timeout, int count, IntPtr* handles) - { - return RhCompatibleReentrantWaitAny(alertable ? 1 : 0, timeout, count, handles); - } + internal static unsafe partial int RhCompatibleReentrantWaitAny([MarshalAs(UnmanagedType.Bool)] bool alertable, int timeout, int count, IntPtr* handles); #endif // diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index 34db94ad986451..863af737890b36 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -520,5 +520,11 @@ internal static void WaitForForegroundThreads() } s_allDone.WaitOne(); } + + internal static unsafe int ReentrantWaitAny([MarshalAs(UnmanagedType.Bool)] bool alertable, int timeout, int count, IntPtr* handles) + { + Debug.Assert(ReentrantWaitsEnabled); + return RuntimeImports.RhCompatibleReentrantWaitAny(alertable, timeout, count, handles); + } } } diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 37f1a76d4f73a3..840a95c11579fb 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -308,7 +308,6 @@ set(VM_SOURCES_WKS commodule.cpp comsynchronizable.cpp comutilnative.cpp - comwaithandle.cpp coreassemblyspec.cpp corelib.cpp # true customattribute.cpp @@ -413,7 +412,6 @@ set(VM_HEADERS_WKS commodule.h comsynchronizable.h comutilnative.h - comwaithandle.h customattribute.h custommarshalerinfo.h autotrace.h diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index a1095dc564eecf..47153440f372c2 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -933,3 +933,32 @@ FCIMPL0(FC_BOOL_RET, ThreadNative::CurrentThreadIsFinalizerThread) FC_RETURN_BOOL(IsFinalizerThread()); } FCIMPLEND + + +extern "C" INT32 QCALLTYPE ThreadNative_ReentrantWaitAny(BOOL alertable, INT32 timeout, INT32 count, HANDLE *handles) +{ + QCALL_CONTRACT; + + INT32 retVal = 0; + + BEGIN_QCALL; + + Thread *pThread = GetThread(); + WaitMode mode = (WaitMode)((alertable ? WaitMode_Alertable : WaitMode_None) | WaitMode_IgnoreSyncCtx); + retVal = pThread->DoAppropriateWait(count, handles, FALSE, timeout, mode); + + END_QCALL; + + return retVal; +} + +extern "C" void QCALLTYPE ThreadNative_CheckForPendingInterrupt(QCall::ThreadHandle thread) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + thread->HandleThreadInterrupt(); + + END_QCALL; +} diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index 1931eca7a79afd..a16b99b509a4c6 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -58,6 +58,8 @@ extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t); extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_SetWaitSleepJoinState(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHandle thread); +extern "C" INT32 QCALLTYPE ThreadNative_ReentrantWaitAny(BOOL alertable, INT32 timeout, INT32 count, HANDLE *handles); +extern "C" void QCALLTYPE ThreadNative_CheckForPendingInterrupt(QCall::ThreadHandle thread); #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT extern "C" INT32 QCALLTYPE ThreadNative_GetApartmentState(QCall::ObjectHandleOnStack t); diff --git a/src/coreclr/vm/comwaithandle.cpp b/src/coreclr/vm/comwaithandle.cpp deleted file mode 100644 index 322cf20704e831..00000000000000 --- a/src/coreclr/vm/comwaithandle.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - -/*============================================================ -** -** COMWaitHandle.cpp -** -** Purpose: Native methods on System.WaitHandle -** -** -===========================================================*/ -#include "common.h" -#include "comwaithandle.h" - -extern "C" INT32 QCALLTYPE WaitHandle_WaitOneCore(HANDLE handle, INT32 timeout, BOOL useTrivialWaits) -{ - QCALL_CONTRACT; - - INT32 retVal = 0; - - BEGIN_QCALL; - - _ASSERTE(handle != 0); - _ASSERTE(handle != INVALID_HANDLE_VALUE); - - Thread* pThread = GET_THREAD(); - WaitMode waitMode = (WaitMode)((!useTrivialWaits ? WaitMode_Alertable : WaitMode_None) | WaitMode_IgnoreSyncCtx); - retVal = pThread->DoAppropriateWait(1, &handle, TRUE, timeout, waitMode); - - END_QCALL; - return retVal; -} - -extern "C" INT32 QCALLTYPE WaitHandle_WaitMultipleIgnoringSyncContext(HANDLE *handleArray, INT32 numHandles, BOOL waitForAll, INT32 timeout) -{ - QCALL_CONTRACT; - - INT32 ret = 0; - BEGIN_QCALL; - - Thread * pThread = GET_THREAD(); - -#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT - // There are some issues with wait-all from an STA thread - // - https://github.com/dotnet/runtime/issues/10243#issuecomment-385117537 - if (waitForAll && numHandles > 1 && pThread->GetApartment() == Thread::AS_InSTA) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_WaitAllSTAThread")); - } -#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - - ret = pThread->DoAppropriateWait(numHandles, handleArray, waitForAll, timeout, (WaitMode)(WaitMode_Alertable | WaitMode_IgnoreSyncCtx)); - - END_QCALL; - return ret; -} - -extern "C" INT32 QCALLTYPE WaitHandle_SignalAndWait(HANDLE waitHandleSignal, HANDLE waitHandleWait, INT32 timeout) -{ - QCALL_CONTRACT; - - INT32 retVal = (DWORD)-1; - - BEGIN_QCALL; - - _ASSERTE(waitHandleSignal != 0); - _ASSERTE(waitHandleWait != 0); - - Thread* pThread = GET_THREAD(); - -#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT - if (pThread->GetApartment() == Thread::AS_InSTA) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_SignalAndWaitSTAThread")); - } -#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - - HANDLE handles[] = { waitHandleSignal, waitHandleWait }; - retVal = pThread->DoSignalAndWait(handles, timeout, TRUE /*alertable*/); - - END_QCALL; - return retVal; -} diff --git a/src/coreclr/vm/comwaithandle.h b/src/coreclr/vm/comwaithandle.h deleted file mode 100644 index 7fb890e7554ce2..00000000000000 --- a/src/coreclr/vm/comwaithandle.h +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - -/*============================================================ -** -** Header: COMWaitHandle.h -** -** Purpose: Native methods on System.WaitHandle -** -** -===========================================================*/ - -#ifndef _COM_WAITABLE_HANDLE_H -#define _COM_WAITABLE_HANDLE_H - -extern "C" INT32 QCALLTYPE WaitHandle_WaitOneCore(HANDLE handle, INT32 timeout, BOOL useTrivialWaits); -extern "C" INT32 QCALLTYPE WaitHandle_WaitMultipleIgnoringSyncContext(HANDLE *handleArray, INT32 numHandles, BOOL waitForAll, INT32 timeout); -extern "C" INT32 QCALLTYPE WaitHandle_SignalAndWait(HANDLE waitHandleSignal, HANDLE waitHandleWait, INT32 timeout); - -#endif // _COM_WAITABLE_HANDLE_H diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp index f335fb26172eb9..679f10d5f67b6e 100644 --- a/src/coreclr/vm/corelib.cpp +++ b/src/coreclr/vm/corelib.cpp @@ -32,7 +32,6 @@ #include "comdatetime.h" #include "debugdebugger.h" #include "assemblynative.hpp" -#include "comwaithandle.h" #include "proftoeeinterfaceimpl.h" diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 103a03a0c668b1..4040d546d57c84 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -26,7 +26,6 @@ #include "comdatetime.h" #include "debugdebugger.h" #include "assemblynative.hpp" -#include "comwaithandle.h" #include "proftoeeinterfaceimpl.h" @@ -291,6 +290,8 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_GetThreadState) DllImportEntry(ThreadNative_SetWaitSleepJoinState) DllImportEntry(ThreadNative_ClearWaitSleepJoinState) + DllImportEntry(ThreadNative_ReentrantWaitAny) + DllImportEntry(ThreadNative_CheckForPendingInterrupt) #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT DllImportEntry(ThreadNative_GetApartmentState) DllImportEntry(ThreadNative_SetApartmentState) @@ -305,9 +306,6 @@ static const Entry s_QCall[] = #ifdef FEATURE_COMINTEROP DllImportEntry(ThreadNative_DisableComObjectEagerCleanup) #endif // FEATURE_COMINTEROP - DllImportEntry(WaitHandle_WaitOneCore) - DllImportEntry(WaitHandle_WaitMultipleIgnoringSyncContext) - DllImportEntry(WaitHandle_SignalAndWait) DllImportEntry(ClrConfig_GetConfigBoolValue) DllImportEntry(Buffer_Clear) DllImportEntry(Buffer_MemMove) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index fa301c21900db5..e24c7f9c6954e4 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -3149,6 +3149,24 @@ DWORD MsgWaitHelper(int numWaiters, HANDLE* phEvent, BOOL bWaitAll, DWORD millis #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT + +DWORD Thread::DoReentrantWaitAny(int numWaiters, HANDLE* pHandles, DWORD timeout, WaitMode mode) +{ + CONTRACTL { + THROWS; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + DWORD ret = 0; + + DoAppropriateAptStateWait(numWaiters, pHandles, FALSE, timeout, mode); + + return ret; +} + + //-------------------------------------------------------------------- // Do appropriate wait based on apartment state (STA or MTA) DWORD Thread::DoAppropriateAptStateWait(int numWaiters, HANDLE* pHandles, BOOL bWaitAll, @@ -3204,18 +3222,6 @@ void Thread::DoAppropriateWaitWorkerAlertableHelper(WaitMode mode) } } -void MarkOSAlertableWait() -{ - LIMITED_METHOD_CONTRACT; - GetThread()->SetThreadStateNC (Thread::TSNC_OSAlertableWait); -} - -void UnMarkOSAlertableWait() -{ - LIMITED_METHOD_CONTRACT; - GetThread()->ResetThreadStateNC (Thread::TSNC_OSAlertableWait); -} - //-------------------------------------------------------------------- // Based on whether this thread has a message pump, do the appropriate // style of Wait. @@ -3290,8 +3296,6 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa DoAppropriateWaitWorkerAlertableHelper(mode); } - StateHolder OSAlertableWait(alertable); - ThreadStateHolder tsh(alertable, TS_Interruptible | TS_Interrupted); bool sendWaitEvents = @@ -3504,158 +3508,6 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa return ret; } - -//-------------------------------------------------------------------- -// Only one style of wait for DoSignalAndWait since we don't support this on STA Threads -//-------------------------------------------------------------------- -DWORD Thread::DoSignalAndWait(HANDLE *handles, DWORD millis, BOOL alertable, PendingSync *syncState) -{ - STATIC_CONTRACT_THROWS; - STATIC_CONTRACT_GC_TRIGGERS; - - _ASSERTE(alertable || syncState == 0); - - struct Param - { - Thread *pThis; - HANDLE *handles; - DWORD millis; - BOOL alertable; - DWORD dwRet; - } param; - param.pThis = this; - param.handles = handles; - param.millis = millis; - param.alertable = alertable; - param.dwRet = (DWORD) -1; - - EE_TRY_FOR_FINALLY(Param *, pParam, ¶m) { - pParam->dwRet = pParam->pThis->DoSignalAndWaitWorker(pParam->handles, pParam->millis, pParam->alertable); - } - EE_FINALLY { - if (syncState) { - if (!GOT_EXCEPTION() && WAIT_OBJECT_0 == param.dwRet) { - // This thread has been removed from syncblk waiting list by the signalling thread - syncState->Restore(FALSE); - } - else - syncState->Restore(TRUE); - } - - _ASSERTE (WAIT_IO_COMPLETION != param.dwRet); - } - EE_END_FINALLY; - - return(param.dwRet); -} - - -DWORD Thread::DoSignalAndWaitWorker(HANDLE* pHandles, DWORD millis,BOOL alertable) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END; - - DWORD ret = 0; - - GCX_PREEMP(); - - if(alertable) - { - DoAppropriateWaitWorkerAlertableHelper(WaitMode_None); - } - - StateHolder OSAlertableWait(alertable); - - ThreadStateHolder tsh(alertable, TS_Interruptible | TS_Interrupted); - - ULONGLONG dwStart = 0, dwEnd; - - if (INFINITE != millis) - { - dwStart = minipal_lowres_ticks(); - } - - ret = SignalObjectAndWait(pHandles[0], pHandles[1], millis, alertable); - -retry: - - if (WAIT_IO_COMPLETION == ret) - { - _ASSERTE (alertable); - // We could be woken by some spurious APC or an EE APC queued to - // interrupt us. In the latter case the TS_Interrupted bit will be set - // in the thread state bits. Otherwise we just go back to sleep again. - if ((m_State & TS_Interrupted)) - { - HandleThreadInterrupt(); - } - if (INFINITE != millis) - { - dwEnd = minipal_lowres_ticks(); - if (dwStart + millis <= dwEnd) - { - ret = WAIT_TIMEOUT; - goto WaitCompleted; - } - - millis -= (DWORD)(dwEnd - dwStart); - dwStart = dwEnd; - } - //Retry case we don't want to signal again so only do the wait... - ret = WaitForSingleObjectEx(pHandles[1],millis,TRUE); - goto retry; - } - - if (WAIT_FAILED == ret) - { - DWORD errorCode = ::GetLastError(); - //If the handle to signal is a mutex and - // the calling thread is not the owner, errorCode is ERROR_NOT_OWNER - - switch(errorCode) - { - case ERROR_INVALID_HANDLE: - case ERROR_NOT_OWNER: - case ERROR_ACCESS_DENIED: - COMPlusThrowWin32(); - break; - - case ERROR_TOO_MANY_POSTS: - ret = ERROR_TOO_MANY_POSTS; - break; - - default: - CONSISTENCY_CHECK_MSGF(0, ("This errorCode is not understood '(%d)''\n", errorCode)); - COMPlusThrowWin32(); - break; - } - } - -WaitCompleted: - - //Check that the return state is valid -#ifdef HOST_WINDOWS - _ASSERTE(WAIT_OBJECT_0 == ret || - WAIT_ABANDONED == ret || - WAIT_TIMEOUT == ret || - WAIT_FAILED == ret || - ERROR_TOO_MANY_POSTS == ret); -#else - _ASSERTE(WAIT_OBJECT_0 == ret || - WAIT_TIMEOUT == ret || - WAIT_FAILED == ret || - ERROR_TOO_MANY_POSTS == ret); -#endif // HOST_WINDOWS - - //Wrong to time out if the wait was infinite - _ASSERTE((WAIT_TIMEOUT != ret) || (INFINITE != millis)); - - return ret; -} - DWORD Thread::DoSyncContextWait(OBJECTREF *pSyncCtxObj, int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis) { CONTRACTL diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 5b79157aa208e0..6b51c13885c6a0 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -617,7 +617,7 @@ class Thread // unused = 0x00000200, TSNC_OwnsSpinLock = 0x00000400, // The thread owns a spinlock. TSNC_PreparingAbort = 0x00000800, // Preparing abort. This avoids recursive HandleThreadAbort call. - TSNC_OSAlertableWait = 0x00001000, // Preparing abort. This avoids recursive HandleThreadAbort call. + // unused = 0x00001000, // Preparing abort. This avoids recursive HandleThreadAbort call. // unused = 0x00002000, TSNC_CreatingTypeInitException = 0x00004000, // Thread is trying to create a TypeInitException // unused = 0x00008000, @@ -2166,13 +2166,11 @@ class Thread DWORD millis, WaitMode mode, PendingSync *syncInfo = 0); - DWORD DoSignalAndWait(HANDLE *handles, DWORD millis, BOOL alertable, - PendingSync *syncState = 0); + DWORD DoReentrantWaitAny(int numWaiters, HANDLE* pHandles, DWORD timeout, WaitMode mode); private: void DoAppropriateWaitWorkerAlertableHelper(WaitMode mode); DWORD DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis, WaitMode mode, void *associatedObjectForMonitorWait); - DWORD DoSignalAndWaitWorker(HANDLE* pHandles, DWORD millis,BOOL alertable); DWORD DoAppropriateAptStateWait(int numWaiters, HANDLE* pHandles, BOOL bWaitAll, DWORD timeout, WaitMode mode); DWORD DoSyncContextWait(OBJECTREF *pSyncCtxObj, int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis); public: @@ -2657,9 +2655,9 @@ class Thread InterlockedExchange(&m_UserInterrupt, 0); } +public: void HandleThreadInterrupt(); -public: static void WINAPI UserInterruptAPC(ULONG_PTR ignore); #if defined(_DEBUG) && defined(TRACK_SYNC) diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index e5981a2f0f1e3c..2621e6e8153287 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -2292,6 +2292,7 @@ + @@ -2865,9 +2866,6 @@ Common\Interop\Unix\System.Native\Interop.LowLevelCrossProcessMutex.cs - - - @@ -2911,4 +2909,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs index 755ea77ba1cbec..18373595f96c3c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -26,7 +26,7 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan if (numHandles == 1) waitAll = false; -#if NATIVEAOT // TODO: reentrant wait support in Mono https://github.com/dotnet/runtime/issues/49518 +#if !MONO // TODO: reentrant wait support in Mono https://github.com/dotnet/runtime/issues/49518 // Trivial waits don't allow reentrance bool reentrantWait = !useTrivialWaits && Thread.ReentrantWaitsEnabled; @@ -61,11 +61,11 @@ private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHan while (true) { -#if NATIVEAOT +#if !MONO if (reentrantWait) { Debug.Assert(!waitAll); - result = RuntimeImports.RhCompatibleReentrantWaitAny(true, millisecondsTimeout, numHandles, pHandles); + result = Thread.ReentrantWaitAny(true, millisecondsTimeout, numHandles, pHandles); } else { From f82ab8f08e940e591830c9ee579e77bec8242a40 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Aug 2025 13:35:56 -0700 Subject: [PATCH 16/33] Host deadlock detection doesn't exist any more --- src/coreclr/vm/comsynchronizable.cpp | 2 +- src/coreclr/vm/synch.h | 1 - src/coreclr/vm/threads.cpp | 4 ---- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 47153440f372c2..9e6d58262dc3f7 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -627,7 +627,7 @@ static BOOL DoJoin(THREADBASEREF dyingThread, INT32 timeout) ? INFINITE : (DWORD) timeout); - DWORD rv = DyingInternal->JoinEx(dwTimeOut32, (WaitMode)(WaitMode_Alertable/*alertable*/|WaitMode_InDeadlock)); + DWORD rv = DyingInternal->JoinEx(dwTimeOut32, (WaitMode)(WaitMode_Alertable/*alertable*/)); switch(rv) { case WAIT_OBJECT_0: diff --git a/src/coreclr/vm/synch.h b/src/coreclr/vm/synch.h index 72e19f1c33b602..51e11f60dd06bb 100644 --- a/src/coreclr/vm/synch.h +++ b/src/coreclr/vm/synch.h @@ -13,7 +13,6 @@ enum WaitMode WaitMode_None =0x0, WaitMode_Alertable = 0x1, // Can be waken by APC. May pumping message. WaitMode_IgnoreSyncCtx = 0x2, // Dispatch to synchronization context if existed. - WaitMode_InDeadlock = 0x4, // The wait can be terminated by host's deadlock detection }; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index e24c7f9c6954e4..7b91b770564083 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -415,10 +415,6 @@ DWORD Thread::JoinEx(DWORD timeout, WaitMode mode) _ASSERTE(pCurThread || dbgOnly_IsSpecialEEThread()); { - // We're not hosted, so WaitMode_InDeadlock is irrelevant. Clear it, so that this wait can be - // forwarded to a SynchronizationContext if needed. - mode = (WaitMode)(mode & ~WaitMode_InDeadlock); - HANDLE handle = GetThreadHandle(); if (handle == INVALID_HANDLE_VALUE) { return WAIT_FAILED; From 757bc4f88aa76a14af940dd58ce229441da54c2e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Aug 2025 13:44:05 -0700 Subject: [PATCH 17/33] Don't call back into the sync context from the native "do appropriate wait" worker as the only handles it gets on Unix are PAL handles, not wait subsystem handles (and for Windows, we don't need to call back for handles that come through here) --- src/coreclr/vm/comsynchronizable.cpp | 2 +- src/coreclr/vm/corelib.h | 5 -- src/coreclr/vm/metasig.h | 3 -- src/coreclr/vm/object.h | 28 ----------- src/coreclr/vm/synch.h | 1 - src/coreclr/vm/threads.cpp | 69 ---------------------------- src/coreclr/vm/threads.h | 20 -------- 7 files changed, 1 insertion(+), 127 deletions(-) diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 9e6d58262dc3f7..7b688863c88b72 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -944,7 +944,7 @@ extern "C" INT32 QCALLTYPE ThreadNative_ReentrantWaitAny(BOOL alertable, INT32 t BEGIN_QCALL; Thread *pThread = GetThread(); - WaitMode mode = (WaitMode)((alertable ? WaitMode_Alertable : WaitMode_None) | WaitMode_IgnoreSyncCtx); + WaitMode mode = alertable ? WaitMode_Alertable : WaitMode_None; retVal = pThread->DoAppropriateWait(count, handles, FALSE, timeout, mode); END_QCALL; diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 861f6626814447..707cdd08fe3afa 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -947,11 +947,6 @@ DEFINE_METHOD(STRING_BUILDER, INTERNAL_COPY, InternalCopy, DEFINE_METHOD(STRING_BUILDER, REPLACE_BUFFER_INTERNAL,ReplaceBufferInternal, IM_PtrChar_Int_RetVoid) DEFINE_METHOD(STRING_BUILDER, REPLACE_BUFFER_ANSI_INTERNAL,ReplaceBufferAnsiInternal, IM_PtrSByt_Int_RetVoid) -DEFINE_CLASS_U(Threading, SynchronizationContext, SynchronizationContextObject) -DEFINE_FIELD_U(_requireWaitNotification, SynchronizationContextObject, _requireWaitNotification) -DEFINE_CLASS(SYNCHRONIZATION_CONTEXT, Threading, SynchronizationContext) -DEFINE_METHOD(SYNCHRONIZATION_CONTEXT, INVOKE_WAIT_METHOD_HELPER, InvokeWaitMethodHelper, SM_SyncCtx_ArrIntPtr_Bool_Int_RetInt) - #ifdef DEBUG DEFINE_CLASS(STACKCRAWMARK, Threading, StackCrawlMark) #endif diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 4d6e8666749c2b..f69fc14b0d5681 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -526,9 +526,6 @@ DEFINE_METASIG_T(SM(Assembly_RetVoid, C(ASSEMBLY), v)) DEFINE_METASIG_T(SM(Assembly_Str_RetArrAssembly, C(ASSEMBLY) s, a(C(ASSEMBLY)))) DEFINE_METASIG(SM(Str_RetArrStr, s, a(s))) -// Execution Context -DEFINE_METASIG_T(SM(SyncCtx_ArrIntPtr_Bool_Int_RetInt, C(SYNCHRONIZATION_CONTEXT) a(I) F i, i)) - // Exception DEFINE_METASIG(IM(RefUInt_RetStr, r(K), s)) diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 7ba028c03e19d5..6a36bfeddb8b8e 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1219,37 +1219,15 @@ class ReflectModuleBaseObject : public Object }; class ThreadBaseObject; -class SynchronizationContextObject: public Object -{ - friend class CoreLibBinder; -private: - // These field are also defined in the managed representation. (SecurityContext.cs)If you - // add or change these field you must also change the managed code so that - // it matches these. This is necessary so that the object is the proper - // size. - CLR_BOOL _requireWaitNotification; -public: - BOOL IsWaitNotificationRequired() const - { - LIMITED_METHOD_CONTRACT; - return _requireWaitNotification; - } -}; - - - - typedef DPTR(class CultureInfoBaseObject) PTR_CultureInfoBaseObject; #ifdef USE_CHECKED_OBJECTREFS -typedef REF SYNCHRONIZATIONCONTEXTREF; typedef REF EXECUTIONCONTEXTREF; typedef REF CULTUREINFOBASEREF; typedef REF ARRAYBASEREF; #else -typedef SynchronizationContextObject* SYNCHRONIZATIONCONTEXTREF; typedef CultureInfoBaseObject* CULTUREINFOBASEREF; typedef PTR_ArrayBase ARRAYBASEREF; #endif @@ -1358,12 +1336,6 @@ class ThreadBaseObject : public Object return m_Name; } - OBJECTREF GetSynchronizationContext() - { - LIMITED_METHOD_CONTRACT; - return m_SynchronizationContext; - } - void InitExisting(); void ResetStartHelper() diff --git a/src/coreclr/vm/synch.h b/src/coreclr/vm/synch.h index 51e11f60dd06bb..58dbf2b2ca6866 100644 --- a/src/coreclr/vm/synch.h +++ b/src/coreclr/vm/synch.h @@ -12,7 +12,6 @@ enum WaitMode { WaitMode_None =0x0, WaitMode_Alertable = 0x1, // Can be waken by APC. May pumping message. - WaitMode_IgnoreSyncCtx = 0x2, // Dispatch to synchronization context if existed. }; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 7b91b770564083..66f324e67ac894 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -3234,48 +3234,6 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa DWORD ret = 0; BOOL alertable = (mode & WaitMode_Alertable) != 0; - // Waits from SynchronizationContext.WaitHelper are always just WaitMode_IgnoreSyncCtx. - // So if we defer to a sync ctx, we will lose any extra bits. We must therefore not - // defer to a sync ctx if doing any non-default wait. - // If you're doing a default wait, but want to ignore sync ctx, specify WaitMode_IgnoreSyncCtx - // which will make mode != WaitMode_Alertable. - BOOL ignoreSyncCtx = (mode != WaitMode_Alertable); - - if (AppDomain::GetCurrentDomain()->MustForceTrivialWaitOperations()) - ignoreSyncCtx = TRUE; - - // Unless the ignoreSyncCtx flag is set, first check to see if there is a synchronization - // context on the current thread and if there is, dispatch to it to do the wait. - // If the wait is non alertable we cannot forward the call to the sync context - // since fundamental parts of the system (such as the GC) rely on non alertable - // waits not running any managed code. Also if we are past the point in shutdown were we - // are allowed to run managed code then we can't forward the call to the sync context. - if (!ignoreSyncCtx - && alertable - && !HasThreadStateNC(Thread::TSNC_BlockedForShutdown)) - { - GCX_COOP(); - - BOOL fSyncCtxPresent = FALSE; - OBJECTREF SyncCtxObj = NULL; - GCPROTECT_BEGIN(SyncCtxObj) - { - GetSynchronizationContext(&SyncCtxObj); - if (SyncCtxObj != NULL) - { - SYNCHRONIZATIONCONTEXTREF syncRef = (SYNCHRONIZATIONCONTEXTREF)SyncCtxObj; - if (syncRef->IsWaitNotificationRequired()) - { - fSyncCtxPresent = TRUE; - ret = DoSyncContextWait(&SyncCtxObj, countHandles, handles, waitAll, millis); - } - } - } - GCPROTECT_END(); - - if (fSyncCtxPresent) - return ret; - } // Before going to pre-emptive mode the thread needs to be flagged as waiting for // the debugger. This used to be accomplished by the TS_Interruptible flag but that @@ -3504,33 +3462,6 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa return ret; } -DWORD Thread::DoSyncContextWait(OBJECTREF *pSyncCtxObj, int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(handles)); - PRECONDITION(IsProtectedByGCFrame (pSyncCtxObj)); - } - CONTRACTL_END; - MethodDescCallSite invokeWaitMethodHelper(METHOD__SYNCHRONIZATION_CONTEXT__INVOKE_WAIT_METHOD_HELPER); - - BASEARRAYREF handleArrayObj = (BASEARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I, countHandles); - memcpyNoGCRefs(handleArrayObj->GetDataPtr(), handles, countHandles * sizeof(HANDLE)); - - ARG_SLOT args[6] = - { - ObjToArgSlot(*pSyncCtxObj), - ObjToArgSlot(handleArrayObj), - BoolToArgSlot(waitAll), - (ARG_SLOT)millis, - }; - - return invokeWaitMethodHelper.Call_RetI4(args); -} - // Called out of SyncBlock::Wait() to block this thread until the Notify occurs. BOOL Thread::Block(INT32 timeOut, PendingSync *syncState) { diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 6b51c13885c6a0..1590d8e03c3aa1 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -1643,25 +1643,6 @@ class Thread return (ObjectFromHandle(m_ExposedObject) != NULL) ; } - void GetSynchronizationContext(OBJECTREF *pSyncContextObj) - { - CONTRACTL - { - MODE_COOPERATIVE; - GC_NOTRIGGER; - NOTHROW; - PRECONDITION(CheckPointer(pSyncContextObj)); - } - CONTRACTL_END; - - *pSyncContextObj = NULL; - - THREADBASEREF ExposedThreadObj = (THREADBASEREF)GetExposedObjectRaw(); - if (ExposedThreadObj != NULL) - *pSyncContextObj = ExposedThreadObj->GetSynchronizationContext(); - } - - // When we create a managed thread, the thread is suspended. We call StartThread to get // the thread start. DWORD StartThread(); @@ -2172,7 +2153,6 @@ class Thread DWORD DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis, WaitMode mode, void *associatedObjectForMonitorWait); DWORD DoAppropriateAptStateWait(int numWaiters, HANDLE* pHandles, BOOL bWaitAll, DWORD timeout, WaitMode mode); - DWORD DoSyncContextWait(OBJECTREF *pSyncCtxObj, int countHandles, HANDLE *handles, BOOL waitAll, DWORD millis); public: //************************************************************************ From 39654192c086878b0eeed70dab952aea422296bc Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Aug 2025 13:46:13 -0700 Subject: [PATCH 18/33] named sync primitives don't exist on non-Windows in CoreCLR any more --- src/coreclr/vm/threads.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 66f324e67ac894..8560b06ee37926 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -3368,14 +3368,6 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa { ThrowOutOfMemory(); } -#ifdef TARGET_UNIX - else if (errorCode == ERROR_NOT_SUPPORTED) - { - // "Wait for any" and "wait for all" operations on multiple wait handles are not supported when a cross-process sync - // object is included in the array - COMPlusThrow(kPlatformNotSupportedException, W("PlatformNotSupported_NamedSyncObjectWaitAnyWaitAll")); - } -#endif else if (errorCode != ERROR_INVALID_HANDLE) { ThrowWin32(errorCode); From b18871c57124c4b77aa0057054ba163b97420af6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 28 Aug 2025 13:52:49 -0700 Subject: [PATCH 19/33] Share the sleep impl between all runtimes on Windows and unix --- .../src/System/Threading/Thread.CoreCLR.cs | 8 -- .../Threading/Thread.NativeAot.Windows.cs | 53 ------------- src/coreclr/vm/comsynchronizable.cpp | 11 --- src/coreclr/vm/comsynchronizable.h | 1 - src/coreclr/vm/qcallentrypoints.cpp | 1 - src/coreclr/vm/threads.cpp | 76 ------------------- src/coreclr/vm/threads.h | 2 - .../src/System/Threading/Thread.Windows.cs | 55 ++++++++++++-- 8 files changed, 50 insertions(+), 157 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 2640adff4c16a9..0a0eed3748dbef 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -112,14 +112,6 @@ private void StartCallback() startHelper.Run(); } - /// - /// Suspends the current thread for timeout milliseconds. If timeout == 0, - /// forces the thread to give up the remainder of its timeslice. If timeout - /// == Timeout.Infinite, no timeout will occur. - /// - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Sleep")] - private static partial void SleepInternal(int millisecondsTimeout); - // Max iterations to be done in SpinWait without switching GC modes. private const int SpinWaitCoopThreshold = 1024; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs index e5e691fe6094c2..8c596b2ef9d90f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs @@ -26,59 +26,6 @@ public sealed partial class Thread partial void PlatformSpecificInitialize(); - internal static void SleepInternal(int millisecondsTimeout) - { - Debug.Assert(millisecondsTimeout >= Timeout.Infinite); - - CheckForPendingInterrupt(); - - Thread currentThread = CurrentThread; - if (millisecondsTimeout == Timeout.Infinite) - { - // Infinite wait - use alertable wait - currentThread.SetWaitSleepJoinState(); - uint result; - while (true) - { - result = Interop.Kernel32.SleepEx(Timeout.UnsignedInfinite, true); - if (result != Interop.Kernel32.WAIT_IO_COMPLETION) - { - break; - } - CheckForPendingInterrupt(); - } - - currentThread.ClearWaitSleepJoinState(); - } - else - { - // Timed wait - use alertable wait - currentThread.SetWaitSleepJoinState(); - long startTime = Environment.TickCount64; - while (true) - { - uint result = Interop.Kernel32.SleepEx((uint)millisecondsTimeout, true); - if (result != Interop.Kernel32.WAIT_IO_COMPLETION) - { - break; - } - // Check if this was our interrupt APC - CheckForPendingInterrupt(); - // Handle APC completion by adjusting timeout and retrying - long currentTime = Environment.TickCount64; - long elapsed = currentTime - startTime; - if (elapsed >= millisecondsTimeout) - { - break; - } - millisecondsTimeout -= (int)elapsed; - startTime = currentTime; - } - - currentThread.ClearWaitSleepJoinState(); - } - } - // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start private void PlatformSpecificInitializeExistingThread() { diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 7b688863c88b72..167c90fb4740fa 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -857,17 +857,6 @@ extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread) END_QCALL; } -extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - - GetThread()->UserSleep(iTime); - - END_QCALL; -} - #ifdef FEATURE_COMINTEROP extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::ThreadHandle thread) { diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index a16b99b509a4c6..2582209d894d65 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -71,7 +71,6 @@ extern "C" void QCALLTYPE ThreadNative_Abort(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_ResetAbort(); extern "C" void QCALLTYPE ThreadNative_SpinWait(INT32 iterations); extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread); -extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime); #ifdef FEATURE_COMINTEROP extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::ThreadHandle thread); #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 4040d546d57c84..d570ae7f992324 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -301,7 +301,6 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_ResetAbort) DllImportEntry(ThreadNative_SpinWait) DllImportEntry(ThreadNative_Interrupt) - DllImportEntry(ThreadNative_Sleep) DllImportEntry(ThreadNative_PollGC) #ifdef FEATURE_COMINTEROP DllImportEntry(ThreadNative_DisableComObjectEagerCleanup) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 8560b06ee37926..8788df0dad9937 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -3703,82 +3703,6 @@ void Thread::UserInterrupt(ThreadInterruptMode mode) } } -// Implementation of Thread.Sleep(). -void Thread::UserSleep(INT32 time) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END; - - INCONTRACT(_ASSERTE(!GetThread()->GCNoTrigger())); - - DWORD res; - - // Before going to pre-emptive mode the thread needs to be flagged as waiting for - // the debugger. This used to be accomplished by the TS_Interruptible flag but that - // doesn't work reliably, see DevDiv Bugs 699245. - ThreadStateNCStackHolder tsNC(TRUE, TSNC_DebuggerSleepWaitJoin); - GCX_PREEMP(); - - // A word about ordering for Interrupt. If someone tries to interrupt a thread - // that's in the interruptible state, we queue an APC. But if they try to interrupt - // a thread that's not in the interruptible state, we just record that fact. So - // we have to set TS_Interruptible before we test to see whether someone wants to - // interrupt us or else we have a race condition that causes us to skip the APC. - SetThreadState(TS_Interruptible); - - // If someone has interrupted us, we should not enter the wait. - if (IsUserInterrupted()) - { - HandleThreadInterrupt(); - } - - ThreadStateHolder tsh(TRUE, TS_Interruptible | TS_Interrupted); - - ResetThreadState(TS_Interrupted); - - DWORD dwTime = (DWORD)time; -retry: - - ULONGLONG start = minipal_lowres_ticks(); - - res = ClrSleepEx (dwTime, TRUE); - - if (res == WAIT_IO_COMPLETION) - { - // We could be woken by some spurious APC or an EE APC queued to - // interrupt us. In the latter case the TS_Interrupted bit will be set - // in the thread state bits. Otherwise we just go back to sleep again. - if ((m_State & TS_Interrupted)) - { - HandleThreadInterrupt(); - } - - if (dwTime == INFINITE) - { - goto retry; - } - else - { - ULONGLONG actDuration = minipal_lowres_ticks() - start; - - if (dwTime > actDuration) - { - dwTime -= (DWORD)actDuration; - goto retry; - } - else - { - res = WAIT_TIMEOUT; - } - } - } - _ASSERTE(res == WAIT_TIMEOUT || res == WAIT_OBJECT_0); -} - - // Correspondence between an EE Thread and an exposed System.Thread: OBJECTREF Thread::GetExposedObject() { diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 1590d8e03c3aa1..76e52567c9e10f 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -1803,8 +1803,6 @@ class Thread static bool SysSweepThreadsForDebug(bool forceSync); static void SysResumeFromDebug(AppDomain *pAppDomain); - void UserSleep(INT32 time); - private: // Specifies type of thread abort. diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs index 3d16397d7301fb..dcce3e074eb4e2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs @@ -18,13 +18,58 @@ public sealed partial class Thread { internal static void UninterruptibleSleep0() => Interop.Kernel32.Sleep(0); -#if MONO - private static void SleepInternal(int millisecondsTimeout) + internal static void SleepInternal(int millisecondsTimeout) { - Debug.Assert(millisecondsTimeout >= -1); - Interop.Kernel32.Sleep((uint)millisecondsTimeout); + Debug.Assert(millisecondsTimeout >= Timeout.Infinite); + + CheckForPendingInterrupt(); + + Thread currentThread = CurrentThread; + if (millisecondsTimeout == Timeout.Infinite) + { + // Infinite wait - use alertable wait + currentThread.SetWaitSleepJoinState(); + uint result; + while (true) + { + result = Interop.Kernel32.SleepEx(Timeout.UnsignedInfinite, true); + if (result != Interop.Kernel32.WAIT_IO_COMPLETION) + { + break; + } + CheckForPendingInterrupt(); + } + + currentThread.ClearWaitSleepJoinState(); + } + else + { + // Timed wait - use alertable wait + currentThread.SetWaitSleepJoinState(); + long startTime = Environment.TickCount64; + while (true) + { + uint result = Interop.Kernel32.SleepEx((uint)millisecondsTimeout, true); + if (result != Interop.Kernel32.WAIT_IO_COMPLETION) + { + break; + } + // Check if this was our interrupt APC + CheckForPendingInterrupt(); + // Handle APC completion by adjusting timeout and retrying + long currentTime = Environment.TickCount64; + long elapsed = currentTime - startTime; + if (elapsed >= millisecondsTimeout) + { + break; + } + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; + } + + currentThread.ClearWaitSleepJoinState(); + } } -#endif internal static int GetCurrentProcessorNumber() { From 8c69df470e6c84c6b166190913e19da9bf523d69 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 10 Sep 2025 14:12:01 -0700 Subject: [PATCH 20/33] Use MemberNotNull and adjust SleepInternal def --- .../src/System/Threading/LowLevelLifoSemaphore.Unix.cs | 9 +++++---- .../src/System/Threading/Thread.Unix.cs | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs index 41319b669c6e79..162d8e5a89c9c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Threading { @@ -13,8 +13,9 @@ internal sealed partial class LowLevelLifoSemaphore : IDisposable { // Declared nullable even though it is initialized in Create // as Roslyn doesn't see that it's set in Create and Create is called from all constructors. - private WaitSubsystem.WaitableObject? _semaphore; + private WaitSubsystem.WaitableObject _semaphore; + [MemberNotNull(nameof(_semaphore))] private void Create(int maximumSignalCount) { _semaphore = WaitSubsystem.WaitableObject.NewSemaphore(0, maximumSignalCount); @@ -26,12 +27,12 @@ public void Dispose() private bool WaitCore(int timeoutMs) { - return WaitSubsystem.Wait(_semaphore!, timeoutMs, false, true) == WaitHandle.WaitSuccess; + return WaitSubsystem.Wait(_semaphore, timeoutMs, false, true) == WaitHandle.WaitSuccess; } private void ReleaseCore(int count) { - WaitSubsystem.ReleaseSemaphore(_semaphore!, count); + WaitSubsystem.ReleaseSemaphore(_semaphore, count); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs index 285b85485decbf..d6b463cec0b0de 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs @@ -38,9 +38,7 @@ internal static void PollWasiEventLoopUntilResolvedVoid(Task mainTask) // the closest analog to Sleep(0) on Unix is sched_yield internal static void UninterruptibleSleep0() => Thread.Yield(); -#if !CORECLR private static void SleepInternal(int millisecondsTimeout) => WaitSubsystem.Sleep(millisecondsTimeout); -#endif // sched_getcpu doesn't exist on all platforms. On those it doesn't exist on, the shim returns -1 internal static int GetCurrentProcessorNumber() => Interop.Sys.SchedGetCpu(); From efa6e4673640271297d5b1d2dd211f24b1999e9e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 10 Sep 2025 14:16:25 -0700 Subject: [PATCH 21/33] Call DoReentrantWaitAny from the qcall --- src/coreclr/vm/comsynchronizable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 167c90fb4740fa..206408a986ada1 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -934,7 +934,7 @@ extern "C" INT32 QCALLTYPE ThreadNative_ReentrantWaitAny(BOOL alertable, INT32 t Thread *pThread = GetThread(); WaitMode mode = alertable ? WaitMode_Alertable : WaitMode_None; - retVal = pThread->DoAppropriateWait(count, handles, FALSE, timeout, mode); + retVal = pThread->DoReentrantWaitAny(count, handles, timeout, mode); END_QCALL; From 50ea4cc172ca13f0be0a9e131d0423ec70255276 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 10 Sep 2025 14:19:26 -0700 Subject: [PATCH 22/33] Fix native runtime build --- src/coreclr/debug/runtimeinfo/datadescriptor.cpp | 4 +--- src/coreclr/pal/src/include/pal/context.h | 1 - src/coreclr/pal/src/misc/dbgmsg.cpp | 1 - src/coreclr/pal/src/thread/process.cpp | 1 - 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp index 0efae1ad25089f..7400c2582c83ad 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp @@ -6,8 +6,6 @@ #include #include -#include "static_assert.h" - #include #include "cdacplatformmetadata.hpp" #include "methodtable.h" @@ -257,7 +255,7 @@ struct MagicAndBlob { }; // we only support 32-bit and 64-bit right now -static_assert_no_msg(sizeof(void*) == 4 || sizeof(void*) == 8); +static_assert(sizeof(void*) == 4 || sizeof(void*) == 8); // C-style designated initializers are a C++20 feature. Have to use plain old aggregate initialization instead. diff --git a/src/coreclr/pal/src/include/pal/context.h b/src/coreclr/pal/src/include/pal/context.h index d22d7af500ba89..1b7a45aa6bbab8 100644 --- a/src/coreclr/pal/src/include/pal/context.h +++ b/src/coreclr/pal/src/include/pal/context.h @@ -29,7 +29,6 @@ extern "C" #include #include -#include /* A type to wrap the native context type, which is ucontext_t on some * platforms and another type elsewhere. */ diff --git a/src/coreclr/pal/src/misc/dbgmsg.cpp b/src/coreclr/pal/src/misc/dbgmsg.cpp index 161cdecd2145e5..c9451c99ffbc37 100644 --- a/src/coreclr/pal/src/misc/dbgmsg.cpp +++ b/src/coreclr/pal/src/misc/dbgmsg.cpp @@ -23,7 +23,6 @@ Module Name: #include "pal/cruntime.h" #include "pal/file.h" #include "pal/environ.h" -#include /* standard headers */ diff --git a/src/coreclr/pal/src/thread/process.cpp b/src/coreclr/pal/src/thread/process.cpp index 22eaaa3edc4809..da1378347c1803 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -34,7 +34,6 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #include #include -#include #include #if HAVE_POLL From 1d37a4650c8faf292d0e383a6db3055a3de51d05 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 11 Sep 2025 11:47:58 -0700 Subject: [PATCH 23/33] Increase the refcount to ensure that the exposed thread object can't be cleaned up before we run our managed cleanup in CleanupDetachedThreads --- src/coreclr/vm/threads.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 5da72440848d72..353aeafa4d49a4 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2600,6 +2600,9 @@ void Thread::CleanupDetachedThreads() { // Record threads that we'll need to do some managed cleanup of // after we exit the thread store lock. + // Increase the external ref count to ensure the exposed object stays alive + // until we do our cleanup outside of the thread store lock. + thread->IncExternalCount(); threadsWithManagedCleanup.Append(thread); } @@ -2677,15 +2680,15 @@ void Thread::CleanupDetachedThreads() // During the actual thread shutdown, // it may not be practical for us to run enough managed code to clean up // any managed code that needs to know when a thread exits. - // Instead, run that clean up here when the thread object is finalized - // (which is definitely after the thread has exited) - // - // We know its safe to do this work here as we can't have finalized the managed thread object yet - // as we're running on the finalizer thread right now. + // Instead, run that clean up here when the Thread is detached, + // which is definitely after the thread has exited. + PTR_Thread pThread = (PTR_Thread)iter.GetElement(); PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__ON_THREAD_EXITING); DECLARE_ARGHOLDER_ARRAY(args, 1); - args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(((Thread*)iter.GetElement())->GetExposedObject()); + args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(pThread->GetExposedObject()); CALL_MANAGED_METHOD_NORET(args); + + pThread->DecExternalCount(FALSE); } } From 8ab90ffaaa652590b851fb846130ba6036b3369d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 12 Sep 2025 22:37:57 +0000 Subject: [PATCH 24/33] Fix unix build --- .../src/System/Runtime/RuntimeImports.cs | 12 +----------- .../src/System/Threading/Thread.NativeAot.Windows.cs | 6 ++++++ .../src/System/Threading/Thread.NativeAot.cs | 6 ------ 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 0d32c5b5f32276..a3edf2aa63724d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -74,17 +74,7 @@ internal static void RhReRegisterForFinalize(object obj) // Wait for all pending finalizers. This must be a p/invoke to avoid starving the GC. [LibraryImport(RuntimeLibrary)] - private static partial void RhWaitForPendingFinalizers(int allowReentrantWait); - - // Temporary workaround to unblock shareable assembly bring-up - without shared interop, - // we must prevent RhWaitForPendingFinalizers from using marshaling because it would - // rewrite System.Private.CoreLib to reference the non-shareable interop assembly. With shared interop, - // we will be able to remove this helper method and change the DllImport above - // to directly accept a boolean parameter. - internal static void RhWaitForPendingFinalizers(bool allowReentrantWait) - { - RhWaitForPendingFinalizers(allowReentrantWait ? 1 : 0); - } + internal static partial void RhWaitForPendingFinalizers([MarshalAs(UnmanagedType.Bool)] bool allowReentrantWait); // Get maximum GC generation number. [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs index 8c596b2ef9d90f..30d38b241fb536 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs @@ -473,6 +473,12 @@ internal static void CheckForPendingInterrupt() } } + internal static unsafe int ReentrantWaitAny(bool alertable, int timeout, int count, IntPtr* handles) + { + Debug.Assert(ReentrantWaitsEnabled); + return RuntimeImports.RhCompatibleReentrantWaitAny(alertable, timeout, count, handles); + } + internal static bool ReentrantWaitsEnabled => GetCurrentApartmentType() == ApartmentType.STA; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index 863af737890b36..34db94ad986451 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -520,11 +520,5 @@ internal static void WaitForForegroundThreads() } s_allDone.WaitOne(); } - - internal static unsafe int ReentrantWaitAny([MarshalAs(UnmanagedType.Bool)] bool alertable, int timeout, int count, IntPtr* handles) - { - Debug.Assert(ReentrantWaitsEnabled); - return RuntimeImports.RhCompatibleReentrantWaitAny(alertable, timeout, count, handles); - } } } From c24ae0bac59c8534435827fef84bf14f8d4f0605 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 09:45:58 -0700 Subject: [PATCH 25/33] Remove OSEvent concept (unused) --- src/coreclr/vm/synch.cpp | 106 --------------------------------------- src/coreclr/vm/synch.h | 16 ------ 2 files changed, 122 deletions(-) diff --git a/src/coreclr/vm/synch.cpp b/src/coreclr/vm/synch.cpp index 7acc4254efe396..8c754edb5bdca1 100644 --- a/src/coreclr/vm/synch.cpp +++ b/src/coreclr/vm/synch.cpp @@ -19,7 +19,6 @@ void CLREventBase::CreateAutoEvent (BOOL bInitialState // If TRUE, initial stat // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -45,7 +44,6 @@ BOOL CLREventBase::CreateAutoEventNoThrow (BOOL bInitialState // If TRUE, initi // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -71,7 +69,6 @@ void CLREventBase::CreateManualEvent (BOOL bInitialState // If TRUE, initial st // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -94,7 +91,6 @@ BOOL CLREventBase::CreateManualEventNoThrow (BOOL bInitialState // If TRUE, ini // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -110,103 +106,6 @@ BOOL CLREventBase::CreateManualEventNoThrow (BOOL bInitialState // If TRUE, ini return IsValid(); } -void CLREventBase::CreateOSAutoEvent (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - // Can not assert here. ASP.NET uses our Threadpool before EE is started. - //_ASSERTE (g_fEEStarted); - - SetOSEvent(); - SetAutoEvent(); - - HANDLE h = CreateEvent(NULL,FALSE,bInitialState,NULL); - if (h == NULL) { - ThrowOutOfMemory(); - } - m_handle = h; -} - -BOOL CLREventBase::CreateOSAutoEventNoThrow (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - EX_TRY - { - CreateOSAutoEvent(bInitialState); - } - EX_CATCH - { - } - EX_END_CATCH - - return IsValid(); -} - -void CLREventBase::CreateOSManualEvent (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - // Can not assert here. ASP.NET uses our Threadpool before EE is started. - //_ASSERTE (g_fEEStarted); - - SetOSEvent(); - - HANDLE h = CreateEvent(NULL,TRUE,bInitialState,NULL); - if (h == NULL) { - ThrowOutOfMemory(); - } - m_handle = h; -} - -BOOL CLREventBase::CreateOSManualEventNoThrow (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - EX_TRY - { - CreateOSManualEvent(bInitialState); - } - EX_CATCH - { - } - EX_END_CATCH - - return IsValid(); -} - void CLREventBase::CloseEvent() { CONTRACTL @@ -347,11 +246,6 @@ DWORD CLREventBase::WaitEx(DWORD dwMilliseconds, WaitMode mode) Thread * pThread = GetThreadNULLOk(); -#ifdef _DEBUG - // If a CLREvent is OS event only, we can not wait for the event on a managed thread - if (IsOSEvent()) - _ASSERTE (pThread == NULL); -#endif _ASSERTE((pThread != NULL) || !g_fEEStarted || dbgOnly_IsSpecialEEThread()); { diff --git a/src/coreclr/vm/synch.h b/src/coreclr/vm/synch.h index e66404c677a866..9d10c1d9ffc717 100644 --- a/src/coreclr/vm/synch.h +++ b/src/coreclr/vm/synch.h @@ -32,14 +32,6 @@ class CLREventBase BOOL CreateAutoEventNoThrow(BOOL bInitialState); BOOL CreateManualEventNoThrow(BOOL bInitialState); - // Create an Event that is not host aware - void CreateOSAutoEvent (BOOL bInitialState); - void CreateOSManualEvent (BOOL bInitialState); - - // Non-throwing variants of the functions above - BOOL CreateOSAutoEventNoThrow (BOOL bInitialState); - BOOL CreateOSManualEventNoThrow (BOOL bInitialState); - void CloseEvent(); BOOL IsValid() const @@ -67,7 +59,6 @@ class CLREventBase enum { CLREVENT_FLAGS_AUTO_EVENT = 0x0001, - CLREVENT_FLAGS_OS_EVENT = 0x0002, CLREVENT_FLAGS_STATIC = 0x0020, @@ -83,13 +74,6 @@ class CLREventBase // cannot use `|=' operator on `Volatile' m_dwFlags = m_dwFlags | CLREVENT_FLAGS_AUTO_EVENT; } - BOOL IsOSEvent() { LIMITED_METHOD_CONTRACT; return m_dwFlags & CLREVENT_FLAGS_OS_EVENT; } - void SetOSEvent () - { - LIMITED_METHOD_CONTRACT; - // cannot use `|=' operator on `Volatile' - m_dwFlags = m_dwFlags | CLREVENT_FLAGS_OS_EVENT; - } }; From f877b08e7244a34f1b431558dde7c83e2d40a318 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 09:50:04 -0700 Subject: [PATCH 26/33] Remove Thread::Wait as it's unused now --- src/coreclr/vm/threads.cpp | 47 -------------------------------------- src/coreclr/vm/threads.h | 4 ---- 2 files changed, 51 deletions(-) diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 2212b3e7ecb389..793e759a3022c8 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -3262,53 +3262,6 @@ DWORD Thread::DoAppropriateWait(int countHandles, HANDLE *handles, BOOL waitAll, return ret; } -// Return whether or not a timeout occurred. TRUE=>we waited successfully -DWORD Thread::Wait(HANDLE *objs, int cntObjs, INT32 timeOut) -{ - WRAPPER_NO_CONTRACT; - - DWORD dwResult; - DWORD dwTimeOut32; - - _ASSERTE(timeOut >= 0 || timeOut == INFINITE_TIMEOUT); - - dwTimeOut32 = (timeOut == INFINITE_TIMEOUT - ? INFINITE - : (DWORD) timeOut); - - dwResult = DoAppropriateWait(cntObjs, objs, FALSE /*=waitAll*/, dwTimeOut32, - WaitMode_Alertable /*alertable*/); - - // Either we succeeded in the wait, or we timed out - _ASSERTE((dwResult >= WAIT_OBJECT_0 && dwResult < (DWORD)(WAIT_OBJECT_0 + cntObjs)) || - (dwResult == WAIT_TIMEOUT)); - - return dwResult; -} - -// Return whether or not a timeout occurred. TRUE=>we waited successfully -DWORD Thread::Wait(CLREvent *pEvent, INT32 timeOut) -{ - WRAPPER_NO_CONTRACT; - - DWORD dwResult; - DWORD dwTimeOut32; - - _ASSERTE(timeOut >= 0 || timeOut == INFINITE_TIMEOUT); - - dwTimeOut32 = (timeOut == INFINITE_TIMEOUT - ? INFINITE - : (DWORD) timeOut); - - dwResult = pEvent->Wait(dwTimeOut32, TRUE /*alertable*/); - - // Either we succeeded in the wait, or we timed out - _ASSERTE((dwResult == WAIT_OBJECT_0) || - (dwResult == WAIT_TIMEOUT)); - - return dwResult; -} - #define WAIT_INTERRUPT_THREADABORT 0x1 #define WAIT_INTERRUPT_INTERRUPT 0x2 #define WAIT_INTERRUPT_OTHEREXCEPTION 0x4 diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index aedb9e663ee1d5..0159721a613e78 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2559,10 +2559,6 @@ class Thread #endif // FEATURE_HIJACK - // Support for Wait/Notify - DWORD Wait(HANDLE *objs, int cntObjs, INT32 timeOut); - DWORD Wait(CLREvent* pEvent, INT32 timeOut); - // support for Thread.Interrupt() which breaks out of Waits, Sleeps, Joins LONG m_UserInterrupt; DWORD IsUserInterrupted() From 60bcece091ffb3c4a73e42e6e8e4f078e59bcc8d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 10:12:18 -0700 Subject: [PATCH 27/33] Fix return from DoReentrantWaitAny --- src/coreclr/vm/comsynchronizable.cpp | 2 +- src/coreclr/vm/threads.cpp | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 5314fdd8b1ace4..b6275a7d074126 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -935,7 +935,7 @@ extern "C" INT32 QCALLTYPE ThreadNative_ReentrantWaitAny(BOOL alertable, INT32 t Thread *pThread = GetThread(); WaitMode mode = alertable ? WaitMode_Alertable : WaitMode_None; - retVal = pThread->DoReentrantWaitAny(count, handles, timeout, mode); + retVal = (INT32)pThread->DoReentrantWaitAny(count, handles, timeout, mode); END_QCALL; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 793e759a3022c8..32c934a4fb73e3 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2973,11 +2973,7 @@ DWORD Thread::DoReentrantWaitAny(int numWaiters, HANDLE* pHandles, DWORD timeout } CONTRACTL_END; - DWORD ret = 0; - - DoAppropriateAptStateWait(numWaiters, pHandles, FALSE, timeout, mode); - - return ret; + return DoAppropriateAptStateWait(numWaiters, pHandles, FALSE, timeout, mode); } From a82112e43871a3732c9b7196b18786c688b83fa0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 10:25:12 -0700 Subject: [PATCH 28/33] Remove special-cases in WaitHandle.cs for CoreCLR --- .../src/System/Threading/WaitHandle.cs | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs index 1b426640cab3a6..f5c5320e6409ed 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @@ -146,9 +146,6 @@ internal bool WaitOneNoCheck( bool sendWaitEvents = millisecondsTimeout != 0 && !useTrivialWaits && -#if !CORECLR // CoreCLR sends the wait events from the native side when there's no associated object - associatedObject is not null && -#endif NativeRuntimeEventSource.Log.IsEnabled( EventLevel.Verbose, NativeRuntimeEventSource.Keywords.WaitHandleKeyword); @@ -160,19 +157,11 @@ associatedObject is not null && waitSource != NativeRuntimeEventSource.WaitHandleWaitSourceMap.MonitorWait; if (tryNonblockingWaitFirst) { - // Split into separate calls instead of conditionally adding an argument. -#if CORECLR - waitResult = WaitOneCore( - waitHandle.DangerousGetHandle(), - millisecondsTimeout: 0, - useTrivialWaits, - associatedObject is not null); -#else waitResult = WaitOneCore( waitHandle.DangerousGetHandle(), millisecondsTimeout: 0, useTrivialWaits); -#endif + if (waitResult == WaitTimeout) { // Do a full wait and send the wait events @@ -193,19 +182,10 @@ associatedObject is not null && // When tryNonblockingWaitFirst is true, we have a final wait result from the nonblocking wait above if (!tryNonblockingWaitFirst) { - // Split into separate calls instead of conditionally adding an argument. -#if CORECLR - waitResult = WaitOneCore( - waitHandle.DangerousGetHandle(), - millisecondsTimeout, - useTrivialWaits, - associatedObject is not null); -#else waitResult = WaitOneCore( waitHandle.DangerousGetHandle(), millisecondsTimeout, useTrivialWaits); -#endif } if (sendWaitEvents) From 4fe3f0641d0a972bd4f5c2ae0020c30f401ed728 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 11:45:45 -0700 Subject: [PATCH 29/33] Fix messed up merge commit --- eng/SignCheckExclusionsFile.txt | 15 - src/coreclr/debug/runtimeinfo/.editorconfig | 2 - src/coreclr/debug/runtimeinfo/configure.h.in | 6 - .../runtimeinfo/contract-descriptor.c.in | 34 - .../runtimeinfo/contractdescriptorstub.c | 39 - .../debug/runtimeinfo/contractpointerdata.cpp | 24 - src/coreclr/debug/runtimeinfo/contracts.jsonc | 27 - .../debug/runtimeinfo/datadescriptor.cpp | 341 ------ .../debug/runtimeinfo/datadescriptor.inc | 1027 ----------------- .../src/System/Collections/Generic/Empty.cs | 65 -- .../Collections/Generic/LowLevelList.cs | 196 ---- .../src/System/RuntimeType.cs | 864 -------------- .../System.Data.OleDb/src/OleDbComWrappers.cs | 124 -- .../src/System/Linq/Skip.SizeOpt.cs | 20 - .../src/System/Linq/Take.SizeOpt.cs | 47 - .../Threading/LowLevelLifoSemaphore.Unix.cs | 3 +- .../Metadata/TypeNameParserOptions.cs | 38 - .../Legacy/TheoryDataExtensions.cs | 49 - .../IArrayOfStatelessElements.cs | 87 -- .../SharedTypes/ComInterfaces/IBool.cs | 26 - .../ICustomStringMarshallingUtf16.cs | 20 - .../SharedTypes/ComInterfaces/IDerived.cs | 29 - .../SharedTypes/ComInterfaces/IEmpty.cs | 16 - .../SharedTypes/ComInterfaces/IEnumUnknown.cs | 19 - .../SharedTypes/ComInterfaces/IFloat.cs | 25 - .../ComInterfaces/IGetAndSetInt.cs | 27 - .../SharedTypes/ComInterfaces/IGetIntArray.cs | 19 - .../SharedTypes/ComInterfaces/IHide.cs | 56 - .../SharedTypes/ComInterfaces/IInt.cs | 47 - .../SharedTypes/ComInterfaces/IIntArray.cs | 72 -- .../SharedTypes/ComInterfaces/IInterface.cs | 51 - .../ComInterfaces/IJaggedIntArray.cs | 60 - .../ComInterfaces/IPointProvider.cs | 28 - .../SharedTypes/ComInterfaces/IRefStrings.cs | 18 - .../SharedTypes/ComInterfaces/ISafeHandles.cs | 19 - .../ComInterfaces/IStatefulAllShapes.cs | 46 - .../IStatefulCallerAllocatedBuffer.cs | 58 - .../IStatefulCollectionAllShapes.cs | 72 -- .../IStatefulCollectionBlittableElement.cs | 30 - .../IStatefulCollectionPinnableReference.cs | 86 -- .../IStatefulCollectionStatelessElement.cs | 57 - .../IStatefulFinallyMarshalling.cs | 135 --- .../ComInterfaces/IStatefulMarshalling.cs | 155 --- .../IStatefulPinnedMarshalling.cs | 288 ----- .../ComInterfaces/IStatelessAllShapes.cs | 63 - ...tatelessCallerAllocateBufferMarshalling.cs | 165 --- .../IStatelessCollectionAllShapes.cs | 72 -- .../IStatelessCollectionBlittableElement.cs | 30 - ...tatelessCollectionCallerAllocatedBuffer.cs | 140 --- .../IStatelessCollectionPinnableReference.cs | 85 -- .../IStatelessCollectionStatelessElement.cs | 134 --- .../IStatelessFinallyMarshalling.cs | 55 - .../ComInterfaces/IStatelessMarshalling.cs | 77 -- ...lessPinnableCollectionBlittableElements.cs | 156 --- .../IStatelessPinnedMarshalling.cs | 62 - .../IStringMarshallingOverride.cs | 23 - .../IStringMarshallingOverrideDerived.cs | 23 - .../SharedTypes/ComInterfaces/ISystem.cs | 19 - .../ComInterfaces/IUTF16Marshalling.cs | 20 - .../ComInterfaces/IUTF8Marshalling.cs | 20 - .../ManagedComMethodFailureException.cs | 11 - .../ICollectionMarshallingFails.cs | 56 - .../IJaggedIntArrayMarshallingFails.cs | 89 -- .../IStringArrayMarshallingFails.cs | 79 -- .../MarshallingFails/ISupportErrorInfo.cs | 17 - .../MarshallingFailureException.cs | 13 - .../ThrowOn4thElementMarshalled.cs | 37 - ....Serialization.Xml.Canonicalization.csproj | 72 -- ...m.Security.Cryptography.Pkcs.netcoreapp.cs | 222 ---- .../ChannelClosedException.netcoreapp.cs | 27 - ...tem.Threading.Tasks.Dataflow.netcoreapp.cs | 19 - .../Base/DataflowBlock.IAsyncEnumerable.cs | 35 - .../TransformManyBlock.IAsyncEnumerable.cs | 267 ----- ...PortableThreadPool.Browser.Threads.Mono.cs | 19 - .../ThreadPool.Browser.Threads.Mono.cs | 13 - .../System/Threading/ThreadPool.Wasi.Mono.cs | 138 --- ...eadPoolBoundHandle.Browser.Threads.Mono.cs | 15 - .../System/Threading/TimerQueue.Wasi.Mono.cs | 157 --- 78 files changed, 1 insertion(+), 6896 deletions(-) delete mode 100644 eng/SignCheckExclusionsFile.txt delete mode 100644 src/coreclr/debug/runtimeinfo/.editorconfig delete mode 100644 src/coreclr/debug/runtimeinfo/configure.h.in delete mode 100644 src/coreclr/debug/runtimeinfo/contract-descriptor.c.in delete mode 100644 src/coreclr/debug/runtimeinfo/contractdescriptorstub.c delete mode 100644 src/coreclr/debug/runtimeinfo/contractpointerdata.cpp delete mode 100644 src/coreclr/debug/runtimeinfo/contracts.jsonc delete mode 100644 src/coreclr/debug/runtimeinfo/datadescriptor.cpp delete mode 100644 src/coreclr/debug/runtimeinfo/datadescriptor.inc delete mode 100644 src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs delete mode 100644 src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs delete mode 100644 src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs delete mode 100644 src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs delete mode 100644 src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs delete mode 100644 src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs delete mode 100644 src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs delete mode 100644 src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs delete mode 100644 src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj delete mode 100644 src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs delete mode 100644 src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs delete mode 100644 src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs delete mode 100644 src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs delete mode 100644 src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs diff --git a/eng/SignCheckExclusionsFile.txt b/eng/SignCheckExclusionsFile.txt deleted file mode 100644 index f41753338155eb..00000000000000 --- a/eng/SignCheckExclusionsFile.txt +++ /dev/null @@ -1,15 +0,0 @@ -;; Exclusions for SignCheck. Corresponds to info in Signing.props. -;; Format: https://github.com/dotnet/arcade/blob/397316e195639450b6c76bfeb9823b40bee72d6d/src/SignCheck/Microsoft.SignCheck/Verification/Exclusion.cs#L23-L35 -;; -;; This issue tracks a way to implement exclusions via Signing.props and avoid this extra file: https://github.com/dotnet/arcade/issues/2888 - -;; The apphost and comhost are template files, modified by the SDK to produce the executable for FDE -;; and SCD apps. If they are signed, the file that the SDK produces has an invalid signature and -;; can't be signed again. More info at https://github.com/dotnet/core-setup/pull/7549. -*apphost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 -*singlefilehost.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 -*comhost.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 -*apphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 -*comhosttemplatecomhostdll.dll;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 -*staticapphosttemplateapphostexe.exe;;Template, DO-NOT-SIGN, https://github.com/dotnet/core-setup/pull/7549 -*dotnet.js;;Workaround, https://github.com/dotnet/core-eng/issues/9933 \ No newline at end of file diff --git a/src/coreclr/debug/runtimeinfo/.editorconfig b/src/coreclr/debug/runtimeinfo/.editorconfig deleted file mode 100644 index ce2ce7b8d1ec54..00000000000000 --- a/src/coreclr/debug/runtimeinfo/.editorconfig +++ /dev/null @@ -1,2 +0,0 @@ -[contracts.jsonc] -indent_size = 2 diff --git a/src/coreclr/debug/runtimeinfo/configure.h.in b/src/coreclr/debug/runtimeinfo/configure.h.in deleted file mode 100644 index efe0ede365c644..00000000000000 --- a/src/coreclr/debug/runtimeinfo/configure.h.in +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef RUNTIME_INFO_CONFIGURE_H_INCLUDED -#define RUNTIME_INFO_CONFIGURE_H_INCLUDED - -#define RID_STRING @CLR_DOTNET_RID@ - -#endif // RUNTIME_INFO_CONFIGURE_H_INCLUDED diff --git a/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in b/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in deleted file mode 100644 index c1f0edd7a66f9c..00000000000000 --- a/src/coreclr/debug/runtimeinfo/contract-descriptor.c.in +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -#ifdef _MSC_VER -#define DLLEXPORT __declspec(dllexport) -#else -#define DLLEXPORT __attribute__((visibility("default"))) -#endif - -struct DotNetRuntimeContractDescriptor -{ - uint64_t magic; - uint32_t flags; - const uint32_t descriptor_size; - const char *descriptor; - const uint32_t pointer_data_count; - uint32_t pad0; - const uintptr_t *pointer_data; -}; - -extern const uintptr_t contractDescriptorPointerData[]; - -DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor; - -DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = { - .magic = 0x0043414443434e44ull, // "DNCCDAC\0" - .flags = %%platformFlags%%, - .descriptor_size = %%jsonDescriptorSize%%, - .descriptor = "%%jsonDescriptor%%", - .pointer_data_count = %%pointerDataCount%%, - .pointer_data = &contractDescriptorPointerData[0], -}; diff --git a/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c b/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c deleted file mode 100644 index 59421a6692d2a7..00000000000000 --- a/src/coreclr/debug/runtimeinfo/contractdescriptorstub.c +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include - -#ifdef _MSC_VER -#define DLLEXPORT __declspec(dllexport) -#else -#define DLLEXPORT __attribute__((visibility("default"))) -#endif - -struct DotNetRuntimeContractDescriptor -{ - uint64_t magic; - uint32_t flags; - const uint32_t descriptor_size; - const char *descriptor; - const uint32_t pointer_data_count; - uint32_t pad0; - const uintptr_t *pointer_data; -}; - -extern const uintptr_t contractDescriptorPointerData[]; - -// just the placeholder pointer -const uintptr_t contractDescriptorPointerData[] = { (uintptr_t)0 }; - -DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor; - -#define STUB_DESCRIPTOR "{\"version\":0,\"baseline\":\"empty\",\"contracts\":{},\"types\":{},\"globals\":{}}" - -DLLEXPORT struct DotNetRuntimeContractDescriptor DotNetRuntimeContractDescriptor = { - .magic = 0x0043414443434e44ull, // "DNCCDAC\0" - .flags = 0x1u & (sizeof(void*) == 4 ? 0x02u : 0x00u), - .descriptor_size = sizeof(STUB_DESCRIPTOR), - .descriptor = STUB_DESCRIPTOR, - .pointer_data_count = 1, - .pointer_data = &contractDescriptorPointerData[0], -}; diff --git a/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp b/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp deleted file mode 100644 index 1848b1fb69d898..00000000000000 --- a/src/coreclr/debug/runtimeinfo/contractpointerdata.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" - -#include -#include - -#include "cdacplatformmetadata.hpp" -#include "threads.h" -#include "vars.hpp" - -extern "C" -{ -// without an extern declaration, clang does not emit this global into the object file -extern const uintptr_t contractDescriptorPointerData[]; - -const uintptr_t contractDescriptorPointerData[] = { - (uintptr_t)0, // placeholder -#define CDAC_GLOBAL_POINTER(name,value) (uintptr_t)(value), -#include "datadescriptor.inc" -}; - -} diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc deleted file mode 100644 index 81b28cd2e1fe92..00000000000000 --- a/src/coreclr/debug/runtimeinfo/contracts.jsonc +++ /dev/null @@ -1,27 +0,0 @@ -//algorithmic contracts for coreclr -// The format of this file is: JSON with comments -// { -// "CONTRACT NAME": VERSION, -// ... -// } -// CONTRACT NAME is an arbitrary string, VERSION is an integer -// -// cdac-build-tool can take multiple "-c contract_file" arguments -// so to conditionally include contracts, put additional contracts in a separate file -{ - "CodeVersions": 1, - "DacStreams": 1, - "EcmaMetadata" : 1, - "Exception": 1, - "ExecutionManager": 2, - "Loader": 1, - "Object": 1, - "PlatformMetadata": 1, - "PrecodeStubs": 3, - "ReJIT": 1, - "RuntimeInfo": 1, - "RuntimeTypeSystem": 1, - "StackWalk": 1, - "StressLog": 2, - "Thread": 1 -} diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp deleted file mode 100644 index 7400c2582c83ad..00000000000000 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" - -#include -#include - -#include -#include "cdacplatformmetadata.hpp" -#include "methodtable.h" -#include "threads.h" -#include "exinfo.h" - -#include "configure.h" - -#include "../debug/ee/debugger.h" - -#ifdef HAVE_GCCOVER -#include "gccover.h" -#endif // HAVE_GCCOVER - -// begin blob definition - -extern "C" -{ - -struct TypeSpec -{ - uint32_t Name; - uint32_t Fields; - uint16_t Size; // note: C++ fragile no designated initializers - Size must come after Name and Fields -}; - -struct FieldSpec -{ - uint32_t Name; - uint32_t TypeName; - uint16_t FieldOffset; -}; - -struct GlobalLiteralSpec -{ - uint32_t Name; - uint32_t TypeName; - uint64_t Value; -}; - -struct GlobalPointerSpec -{ - uint32_t Name; - uint32_t PointerDataIndex; -}; - -struct GlobalStringSpec -{ - uint32_t Name; - uint32_t StringValue; -}; - -#define CONCAT(token1,token2) token1 ## token2 -#define CONCAT4(token1, token2, token3, token4) token1 ## token2 ## token3 ## token4 - -#define MAKE_TYPELEN_NAME(tyname) CONCAT(cdac_string_pool_typename__, tyname) -#define MAKE_FIELDLEN_NAME(tyname,membername) CONCAT4(cdac_string_pool_membername__, tyname, __, membername) -#define MAKE_FIELDTYPELEN_NAME(tyname,membername) CONCAT4(cdac_string_pool_membertypename__, tyname, __, membername) -#define MAKE_GLOBALLEN_NAME(globalname) CONCAT(cdac_string_pool_globalname__, globalname) -#define MAKE_GLOBALTYPELEN_NAME(globalname) CONCAT(cdac_string_pool_globaltypename__, globalname) -#define MAKE_GLOBALVALUELEN_NAME(globalname) CONCAT(cdac_string_pool_globalvalue__, globalname) - -// used to stringify the result of a macros expansion -#define STRINGIFY(x) #x - -// define a struct where the size of each field is the length of some string. we will use offsetof to get -// the offset of each struct element, which will be equal to the offset of the beginning of that string in the -// string pool. -struct CDacStringPoolSizes -{ - char cdac_string_pool_nil; // make the first real string start at offset 1 -#define DECL_LEN(membername,len) char membername[(len)]; -#define CDAC_BASELINE(name) DECL_LEN(cdac_string_pool_baseline_, (sizeof(name))) -#define CDAC_TYPE_BEGIN(name) DECL_LEN(MAKE_TYPELEN_NAME(name), sizeof(#name)) -#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) DECL_LEN(MAKE_FIELDLEN_NAME(tyname,membername), sizeof(#membername)) \ - DECL_LEN(MAKE_FIELDTYPELEN_NAME(tyname,membername), sizeof(#membertyname)) -#define CDAC_GLOBAL_STRING(name, stringval) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \ - DECL_LEN(MAKE_GLOBALVALUELEN_NAME(name), sizeof(STRINGIFY(stringval))) -#define CDAC_GLOBAL_POINTER(name,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) -#define CDAC_GLOBAL(name,tyname,value) DECL_LEN(MAKE_GLOBALLEN_NAME(name), sizeof(#name)) \ - DECL_LEN(MAKE_GLOBALTYPELEN_NAME(name), sizeof(#tyname)) -#include "datadescriptor.inc" - char cdac_string_pool_trailing_nil; -#undef DECL_LEN -}; - -#define GET_TYPE_NAME(name) offsetof(struct CDacStringPoolSizes, MAKE_TYPELEN_NAME(name)) -#define GET_FIELD_NAME(tyname,membername) offsetof(struct CDacStringPoolSizes, MAKE_FIELDLEN_NAME(tyname,membername)) -#define GET_FIELDTYPE_NAME(tyname,membername) offsetof(struct CDacStringPoolSizes, MAKE_FIELDTYPELEN_NAME(tyname,membername)) -#define GET_GLOBAL_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALLEN_NAME(globalname)) -#define GET_GLOBALTYPE_NAME(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALTYPELEN_NAME(globalname)) -#define GET_GLOBALSTRING_VALUE(globalname) offsetof(struct CDacStringPoolSizes, MAKE_GLOBALVALUELEN_NAME(globalname)) - -// count the types -enum -{ - CDacBlobTypesCount = -#define CDAC_TYPES_BEGIN() 0 -#define CDAC_TYPE_BEGIN(name) + 1 -#include "datadescriptor.inc" -}; - -// count the field pool size. -// there's 1 placeholder element at the start, and 1 endmarker after each type -enum -{ - CDacBlobFieldsPoolCount = -#define CDAC_TYPES_BEGIN() 1 -#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) + 1 -#define CDAC_TYPE_END(name) + 1 -#include "datadescriptor.inc" -}; - -// count the literal globals -enum -{ - CDacBlobGlobalLiteralsCount = -#define CDAC_GLOBALS_BEGIN() 0 -#define CDAC_GLOBAL(name,tyname,value) + 1 -#include "datadescriptor.inc" -}; - -// count the aux vector globals -enum -{ - CDacBlobGlobalPointersCount = -#define CDAC_GLOBALS_BEGIN() 0 -#define CDAC_GLOBAL_POINTER(name,value) + 1 -#include "datadescriptor.inc" -}; - -// count the global strings -enum -{ - CDacBlobGlobalStringsCount = -#define CDAC_GLOBALS_BEGIN() 0 -#define CDAC_GLOBAL_STRING(name,value) + 1 -#include "datadescriptor.inc" -}; - - -#define MAKE_TYPEFIELDS_TYNAME(tyname) CONCAT(CDacFieldsPoolTypeStart__, tyname) - -// index of each run of fields. -// we make a struct containing one 1-byte field for each field in the run, and then take the offset of the -// struct to get the index of the run of fields. -// this looks like -// -// struct CDacFieldsPoolSizes { -// char cdac_fields_pool_start_placeholder__; -// struct CDacFieldsPoolTypeStart__MethodTable { -// char cdac_fields_pool_member__MethodTable__GCHandle; -// char cdac_fields_pool_member__MethodTable_endmarker; -// } CDacFieldsPoolTypeStart__MethodTable; -// ... -// }; -// -// so that offsetof(struct CDacFieldsPoolSizes, CDacFieldsPoolTypeStart__MethodTable) will give the offset of the -// method table field descriptors in the run of fields -struct CDacFieldsPoolSizes -{ -#define DECL_LEN(membername) char membername; -#define CDAC_TYPES_BEGIN() DECL_LEN(cdac_fields_pool_start_placeholder__) -#define CDAC_TYPE_BEGIN(name) struct MAKE_TYPEFIELDS_TYNAME(name) { -#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) DECL_LEN(CONCAT4(cdac_fields_pool_member__, tyname, __, membername)) -#define CDAC_TYPE_END(name) DECL_LEN(CONCAT4(cdac_fields_pool_member__, tyname, _, endmarker)) \ - } MAKE_TYPEFIELDS_TYNAME(name); -#include "datadescriptor.inc" -#undef DECL_LEN -}; - -#define GET_TYPE_FIELDS(tyname) offsetof(struct CDacFieldsPoolSizes, MAKE_TYPEFIELDS_TYNAME(tyname)) - -// index of each global pointer -// -// struct CDacGlobalPointerIndex -// { -// char placeholder; -// char firstGlobalPointerName; -// char secondGlobalPointerName; -// ... -//} -// -// offsetof (CDACGlobalPointerIndex, NAME) returns the index of the global -struct CDacGlobalPointerIndex -{ -#define DECL_LEN(membername) char membername; -#define CDAC_GLOBALS_BEGIN() DECL_LEN(cdac_global_pointer_index_start_placeholder__) -#define CDAC_GLOBAL_POINTER(name,value) DECL_LEN(CONCAT(cdac_global_pointer_index__, name)) -#include "datadescriptor.inc" -#undef DECL_LEN -}; - -#define GET_GLOBAL_POINTER_INDEX(name) offsetof(struct CDacGlobalPointerIndex, CONCAT(cdac_global_pointer_index__, name)) - -struct BinaryBlobDataDescriptor -{ - // The blob begins with a directory that gives the relative offsets of the `Baseline`, `Types`, - // `FieldsPool`, `GlobalLiteralValues`, `GlobalPointerValues` and `Names` fields of the blob. - // The number of elements of each of the arrays is next. This is followed by the sizes of the - // spec structs. Since `BinaryBlobDataDescriptor` is created via macros, we want to embed the - // `offsetof` and `sizeof` of the components of the blob into the blob itself without having to - // account for any padding that the C/C++ compiler may introduce to enforce alignment. - // Additionally the `Directory` tries to follow a common C/C++ alignment rule (we don't want - // padding introduced in the directory itself): N-byte members are aligned to start on N-byte - // boundaries. - struct Directory { - uint32_t FlagsAndBaselineStart; - uint32_t TypesStart; - - uint32_t FieldsPoolStart; - uint32_t GlobalLiteralValuesStart; - - uint32_t GlobalPointersStart; - uint32_t GlobalStringValuesStart; - uint32_t NamesPoolStart; - - uint32_t TypeCount; - uint32_t FieldsPoolCount; - - uint32_t GlobalLiteralValuesCount; - uint32_t GlobalPointerValuesCount; - uint32_t GlobalStringValuesCount; - - uint32_t NamesPoolCount; - - uint8_t TypeSpecSize; - uint8_t FieldSpecSize; - uint8_t GlobalLiteralSpecSize; - uint8_t GlobalPointerSpecSize; - uint8_t GlobalStringSpecSize; - } Directory; - uint32_t PlatformFlags; - uint32_t BaselineName; - struct TypeSpec Types[CDacBlobTypesCount]; - struct FieldSpec FieldsPool[CDacBlobFieldsPoolCount]; - struct GlobalLiteralSpec GlobalLiteralValues[CDacBlobGlobalLiteralsCount]; - struct GlobalPointerSpec GlobalPointerValues[CDacBlobGlobalPointersCount]; - struct GlobalStringSpec GlobalStringValues[CDacBlobGlobalStringsCount]; - uint8_t NamesPool[sizeof(struct CDacStringPoolSizes)]; - uint8_t EndMagic[4]; -}; - -struct MagicAndBlob { - uint64_t magic; - struct BinaryBlobDataDescriptor Blob; -}; - -// we only support 32-bit and 64-bit right now -static_assert(sizeof(void*) == 4 || sizeof(void*) == 8); - -// C-style designated initializers are a C++20 feature. Have to use plain old aggregate initialization instead. - -DLLEXPORT -struct MagicAndBlob BlobDataDescriptor = { - /*.magic = */ 0x00424F4C42434144ull,// "DACBLOB", - /*.Blob =*/ { - /*.Directory =*/ { - /* .FlagsAndBaselineStart = */ offsetof(struct BinaryBlobDataDescriptor, PlatformFlags), - /* .TypesStart = */ offsetof(struct BinaryBlobDataDescriptor, Types), - /* .FieldsPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, FieldsPool), - /* .GlobalLiteralValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalLiteralValues), - /* .GlobalPointersStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalPointerValues), - /* .GlobalStringValuesStart = */ offsetof(struct BinaryBlobDataDescriptor, GlobalStringValues), - /* .NamesPoolStart = */ offsetof(struct BinaryBlobDataDescriptor, NamesPool), - /* .TypeCount = */ CDacBlobTypesCount, - /* .FieldsPoolCount = */ CDacBlobFieldsPoolCount, - /* .GlobalLiteralValuesCount = */ CDacBlobGlobalLiteralsCount, - /* .GlobalPointerValuesCount = */ CDacBlobGlobalPointersCount, - /* .GlobalStringValuesCount = */ CDacBlobGlobalStringsCount, - /* .NamesPoolCount = */ sizeof(struct CDacStringPoolSizes), - /* .TypeSpecSize = */ sizeof(struct TypeSpec), - /* .FieldSpecSize = */ sizeof(struct FieldSpec), - /* .GlobalLiteralSpecSize = */ sizeof(struct GlobalLiteralSpec), - /* .GlobalPointerSpecSize = */ sizeof(struct GlobalPointerSpec), - /* .GlobalStringSpecSize = */ sizeof(struct GlobalStringSpec) - }, - /* .PlatformFlags = */ (sizeof(void*) == 4 ? 0x02 : 0) | 0x01, - /* .BaselineName = */ offsetof(struct CDacStringPoolSizes, cdac_string_pool_baseline_), - - /* .Types = */ { -#define CDAC_TYPE_BEGIN(name) { \ - /* .Name = */ GET_TYPE_NAME(name), \ - /* .Fields = */ GET_TYPE_FIELDS(name), -#define CDAC_TYPE_INDETERMINATE(name) /*.Size = */ 0, -#define CDAC_TYPE_SIZE(size) /* .Size = */ size, -#define CDAC_TYPE_END(name) }, -#include "datadescriptor.inc" - }, - - /* .FieldsPool = */ { -#define CDAC_TYPES_BEGIN() {0,}, -#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) { \ - /* .Name = */ GET_FIELD_NAME(tyname,membername), \ - /* .TypeName = */ GET_FIELDTYPE_NAME(tyname,membername), \ - /* .FieldOffset = */ offset, \ -}, -#define CDAC_TYPE_END(name) { 0, }, -#include "datadescriptor.inc" - }, - - /* .GlobalLiteralValues = */ { -#define CDAC_GLOBAL(name,tyname,value) { /*.Name = */ GET_GLOBAL_NAME(name), /* .TypeName = */ GET_GLOBALTYPE_NAME(name), /* .Value = */ value }, -#include "datadescriptor.inc" - }, - - /* .GlobalPointerValues = */ { -#define CDAC_GLOBAL_POINTER(name,value) { /* .Name = */ GET_GLOBAL_NAME(name), /* .PointerDataIndex = */ GET_GLOBAL_POINTER_INDEX(name) }, -#include "datadescriptor.inc" - }, - - /* .GlobalStringValues = */ { -#define CDAC_GLOBAL_STRING(name,value) { /* .Name = */ GET_GLOBAL_NAME(name), /* .Value = */ GET_GLOBALSTRING_VALUE(name) }, -#include "datadescriptor.inc" - }, - - /* .NamesPool = */ ("\0" // starts with a nul -#define CDAC_BASELINE(name) name "\0" -#define CDAC_TYPE_BEGIN(name) #name "\0" -#define CDAC_TYPE_FIELD(tyname,membertyname,membername,offset) #membername "\0" #membertyname "\0" -#define CDAC_GLOBAL_STRING(name,value) #name "\0" STRINGIFY(value) "\0" -#define CDAC_GLOBAL_POINTER(name,value) #name "\0" -#define CDAC_GLOBAL(name,tyname,value) #name "\0" #tyname "\0" -#include "datadescriptor.inc" - ), - - /* .EndMagic = */ { 0x01, 0x02, 0x03, 0x04 }, - } -}; - -// end blob definition - -} // extern "C" diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.inc b/src/coreclr/debug/runtimeinfo/datadescriptor.inc deleted file mode 100644 index f28496e5c9b7d3..00000000000000 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.inc +++ /dev/null @@ -1,1027 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// No include guards. This file is included multiple times. - -// The format is: -// CDAC_BASELINE("string") baseline data contract that the runtime should follow. "empty" is reasonable -// CDAC_TYPES_BEGIN() -// ... ... -// CDAC_TYPES_END() -// CDAC_GLOBALS_BEGIN() -// ... ... -// CDAC_GLOBALS_END() -// -// In the format is: -// CDAC_TYPE_BEGIN(cdacTypeIdentifier) // defined a new data descriptor named cdacIdentifier -// -// CDAC_TYPE_SIZE(k) -or- CDAC_TYPE_INDETERMINATE(cdacTypeIdentifier) specifies that the type has -// size k (bytes - usually sizeof(SomeNativeType)) or specify that the type's size is not provided -// It is important that CDAC_TYPE_SIZE or CDAC_TYPE_INDETERMINATE immediately follows -// CDAC_TYPE_BEGIN -// -// CDAC_TYPE_FIELD(cdacTypeIdentifier, cdacFieldTypeIdentifier, cdacFieldName, k) specifies the -// field of "cdacTypeIdentifier" that has name cdacFieldName and has the type -// "cdacFieldtypeIdentifier" located at offset k in the type layout. k is usually -// offsetof(SomeClass, m_FieldName) if the field is public -// -// if the field is private, the convention is that SomeClass declares a friend struct -// cdac_data and provides a specialization of cdac_data with a public constexpr -// size_t member that holds the offset: -// -// class MyClass { -// private: -// void* m_myField; -// friend template cdac_data; -// }; -// template<> struct cdac_data { -// static constexpr size_t MyField = offsetof(MyClass, m_myField); -// }; -// -// then the field layout can be specified as -// CDAC_TYPE_FIELD(MyClassLayout, pointer, MyField, cdac_data::MyField) -// There can be zero or more CDAC_TYPE_FIELD entries per type layout -// For types mapping to managed objects, use exact managed type field names in the descriptor, as -// field names often can't change due to binary serialization or implicit diagnostic contracts -// -// CDAC_TYPE_END(cdacTypeIdentifier) specifies the end of the type layout for cdacTypeIdentifier -// -// In the format is: -// -// CDAC_GLOBAL(cdacGlobalName, cdacTypeIdentifier, value) -// or -// CDAC_GLOBAL_POINTER(cdacGlobalName, cdacTypeIdentifier, address) -// -// Zero or more globals can be defined -// -// if a global is given with CDAC_GLOBAL(), `value` should be a constexpr uint64_t (or convertible -// to uint64_t) for example, it can be a literal constant or a preprocessor definition -// -// if a global is a CDAC_GLOBAL_POINTER(), address should be a constexpr pointer or a constexpr -// uintptr_t -// -// -// -// This file is compiled using the target architecture. Preprocessor defines for the target -// platform will be available. It is ok to use `#ifdef`. - -#ifndef CDAC_BASELINE -#define CDAC_BASELINE(identifier) -#endif -#ifndef CDAC_TYPES_BEGIN -#define CDAC_TYPES_BEGIN() -#endif -#ifndef CDAC_TYPE_BEGIN -#define CDAC_TYPE_BEGIN(tyname) -#endif -#ifndef CDAC_TYPE_SIZE -#define CDAC_TYPE_SIZE(k) -#endif -#ifndef CDAC_TYPE_INDETERMINATE -#define CDAC_TYPE_INDETERMINATE(tyname) -#endif -#ifndef CDAC_TYPE_FIELD -#define CDAC_TYPE_FIELD(tyname,fieldtyname,fieldname,off) -#endif -#ifndef CDAC_TYPE_END -#define CDAC_TYPE_END(tyname) -#endif -#ifndef CDAC_TYPES_END -#define CDAC_TYPES_END() -#endif -#ifndef CDAC_GLOBALS_BEGIN -#define CDAC_GLOBALS_BEGIN() -#endif -#ifndef CDAC_GLOBAL -#define CDAC_GLOBAL(globalname,tyname,val) -#endif -#ifndef CDAC_GLOBAL_POINTER -#define CDAC_GLOBAL_POINTER(globalname,addr) -#endif -#ifndef CDAC_GLOBAL_STRING -#define CDAC_GLOBAL_STRING(globalname,stringval) -#endif -#ifndef CDAC_GLOBALS_END -#define CDAC_GLOBALS_END() -#endif - -CDAC_BASELINE("empty") -CDAC_TYPES_BEGIN() - -CDAC_TYPE_BEGIN(Thread) -CDAC_TYPE_INDETERMINATE(Thread) -CDAC_TYPE_FIELD(Thread, /*uint32*/, Id, cdac_data::Id) -CDAC_TYPE_FIELD(Thread, /*nuint*/, OSId, cdac_data::OSId) -CDAC_TYPE_FIELD(Thread, /*uint32*/, State, cdac_data::State) -CDAC_TYPE_FIELD(Thread, /*uint32*/, PreemptiveGCDisabled, cdac_data::PreemptiveGCDisabled) -CDAC_TYPE_FIELD(Thread, /*pointer*/, RuntimeThreadLocals, cdac_data::RuntimeThreadLocals) -CDAC_TYPE_FIELD(Thread, /*pointer*/, Frame, cdac_data::Frame) -CDAC_TYPE_FIELD(Thread, /*pointer*/, ExceptionTracker, cdac_data::ExceptionTracker) -CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_data::ExposedObject) -CDAC_TYPE_FIELD(Thread, GCHandle, LastThrownObject, cdac_data::LastThrownObject) -CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_data::Link) -#ifndef TARGET_UNIX -CDAC_TYPE_FIELD(Thread, /*pointer*/, TEB, cdac_data::TEB) -#endif -CDAC_TYPE_END(Thread) - -CDAC_TYPE_BEGIN(ThreadStore) -CDAC_TYPE_INDETERMINATE(ThreadStore) -CDAC_TYPE_FIELD(ThreadStore, /*SLink*/, FirstThreadLink, cdac_data::FirstThreadLink) -CDAC_TYPE_FIELD(ThreadStore, /*int32*/, ThreadCount, cdac_data::ThreadCount) -CDAC_TYPE_FIELD(ThreadStore, /*int32*/, UnstartedCount, cdac_data::UnstartedCount) -CDAC_TYPE_FIELD(ThreadStore, /*int32*/, BackgroundCount, cdac_data::BackgroundCount) -CDAC_TYPE_FIELD(ThreadStore, /*int32*/, PendingCount, cdac_data::PendingCount) -CDAC_TYPE_FIELD(ThreadStore, /*int32*/, DeadCount, cdac_data::DeadCount) -CDAC_TYPE_END(ThreadStore) - -CDAC_TYPE_BEGIN(RuntimeThreadLocals) -CDAC_TYPE_INDETERMINATE(RuntimeThreadLocals) -CDAC_TYPE_FIELD(RuntimeThreadLocals, /*EEAllocContext*/, AllocContext, offsetof(RuntimeThreadLocals, alloc_context)) -CDAC_TYPE_END(RuntimeThreadLocals) - -CDAC_TYPE_BEGIN(EEAllocContext) -CDAC_TYPE_INDETERMINATE(EEAllocContext) -CDAC_TYPE_FIELD(EEAllocContext, /*GCAllocContext*/, GCAllocationContext, offsetof(ee_alloc_context, m_GCAllocContext)) -CDAC_TYPE_END(EEAllocContext) - -CDAC_TYPE_BEGIN(GCAllocContext) -CDAC_TYPE_INDETERMINATE(GCAllocContext) -CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Pointer, offsetof(gc_alloc_context, alloc_ptr)) -CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Limit, offsetof(gc_alloc_context, alloc_limit)) -CDAC_TYPE_END(GCAllocContext) - -// Exception - -// Use exact managed type field names for the descriptor as field names often can't change due to binary serialization or implicit diagnostic contracts -CDAC_TYPE_BEGIN(Exception) -CDAC_TYPE_INDETERMINATE(Exception) -CDAC_TYPE_FIELD(Exception, /*pointer*/, _message, cdac_data::_message) -CDAC_TYPE_FIELD(Exception, /*pointer*/, _innerException, cdac_data::_innerException) -CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTrace, cdac_data::_stackTrace) -CDAC_TYPE_FIELD(Exception, /*pointer*/, _watsonBuckets, cdac_data::_watsonBuckets) -CDAC_TYPE_FIELD(Exception, /*pointer*/, _stackTraceString, cdac_data::_stackTraceString) -CDAC_TYPE_FIELD(Exception, /*pointer*/, _remoteStackTraceString, cdac_data::_remoteStackTraceString) -CDAC_TYPE_FIELD(Exception, /*int32*/, _HResult, cdac_data::_HResult) -CDAC_TYPE_FIELD(Exception, /*int32*/, _xcode, cdac_data::_xcode) -CDAC_TYPE_END(Exception) - -CDAC_TYPE_BEGIN(ExceptionInfo) -CDAC_TYPE_INDETERMINATE(ExceptionInfo) -CDAC_TYPE_FIELD(ExceptionInfo, /*pointer*/, ThrownObject, offsetof(ExInfo, m_hThrowable)) -CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExInfo, m_pPrevNestedInfo)) -CDAC_TYPE_END(ExceptionInfo) - - -CDAC_TYPE_BEGIN(GCHandle) -CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE)) -CDAC_TYPE_END(GCHandle) - -// Object - -CDAC_TYPE_BEGIN(Object) -CDAC_TYPE_INDETERMINATE(Object) -CDAC_TYPE_FIELD(Object, /*pointer*/, m_pMethTab, cdac_data::m_pMethTab) -CDAC_TYPE_END(Object) - -CDAC_TYPE_BEGIN(String) -CDAC_TYPE_INDETERMINATE(String) -CDAC_TYPE_FIELD(String, /*pointer*/, m_FirstChar, cdac_data::m_FirstChar) -CDAC_TYPE_FIELD(String, /*uint32*/, m_StringLength, cdac_data::m_StringLength) -CDAC_TYPE_END(String) - -CDAC_TYPE_BEGIN(Array) -CDAC_TYPE_SIZE(sizeof(ArrayBase)) -CDAC_TYPE_FIELD(Array, /*pointer*/, m_NumComponents, cdac_data::m_NumComponents) -CDAC_TYPE_END(Array) - -CDAC_TYPE_BEGIN(InteropSyncBlockInfo) -CDAC_TYPE_INDETERMINATE(InteropSyncBlockInfo) -#ifdef FEATURE_COMINTEROP -CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, CCW, cdac_data::CCW) -CDAC_TYPE_FIELD(InteropSyncBlockInfo, /*pointer*/, RCW, cdac_data::RCW) -#endif // FEATURE_COMINTEROP -CDAC_TYPE_END(InteropSyncBlockInfo) - -CDAC_TYPE_BEGIN(SyncBlock) -CDAC_TYPE_INDETERMINATE(SyncBlock) -CDAC_TYPE_FIELD(SyncBlock, /*pointer*/, InteropInfo, cdac_data::InteropInfo) -CDAC_TYPE_END(SyncBlock) - -CDAC_TYPE_BEGIN(SyncTableEntry) -CDAC_TYPE_SIZE(sizeof(SyncTableEntry)) -CDAC_TYPE_FIELD(SyncTableEntry, /*pointer*/, SyncBlock, offsetof(SyncTableEntry, m_SyncBlock)) -CDAC_TYPE_END(SyncTableEntry) - -// Loader - -CDAC_TYPE_BEGIN(Module) -CDAC_TYPE_INDETERMINATE(Module) -CDAC_TYPE_FIELD(Module, /*pointer*/, Assembly, cdac_data::Assembly) -CDAC_TYPE_FIELD(Module, /*pointer*/, PEAssembly, cdac_data::PEAssembly) -CDAC_TYPE_FIELD(Module, /*pointer*/, Base, cdac_data::Base) -CDAC_TYPE_FIELD(Module, /*uint32*/, Flags, cdac_data::Flags) -CDAC_TYPE_FIELD(Module, /*pointer*/, LoaderAllocator, cdac_data::LoaderAllocator) -CDAC_TYPE_FIELD(Module, /*pointer*/, DynamicMetadata, cdac_data::DynamicMetadata) -CDAC_TYPE_FIELD(Module, /*pointer*/, Path, cdac_data::Path) -CDAC_TYPE_FIELD(Module, /*pointer*/, FileName, cdac_data::FileName) -CDAC_TYPE_FIELD(Module, /*pointer*/, ReadyToRunInfo, cdac_data::ReadyToRunInfo) -CDAC_TYPE_FIELD(Module, /*pointer*/, GrowableSymbolStream, cdac_data::GrowableSymbolStream) - -CDAC_TYPE_FIELD(Module, /*pointer*/, FieldDefToDescMap, cdac_data::FieldDefToDescMap) -CDAC_TYPE_FIELD(Module, /*pointer*/, ManifestModuleReferencesMap, cdac_data::ManifestModuleReferencesMap) -CDAC_TYPE_FIELD(Module, /*pointer*/, MemberRefToDescMap, cdac_data::MemberRefToDescMap) -CDAC_TYPE_FIELD(Module, /*pointer*/, MethodDefToDescMap, cdac_data::MethodDefToDescMap) -CDAC_TYPE_FIELD(Module, /*pointer*/, TypeDefToMethodTableMap, cdac_data::TypeDefToMethodTableMap) -CDAC_TYPE_FIELD(Module, /*pointer*/, TypeRefToMethodTableMap, cdac_data::TypeRefToMethodTableMap) -CDAC_TYPE_FIELD(Module, /*pointer*/, MethodDefToILCodeVersioningStateMap, cdac_data::MethodDefToILCodeVersioningStateMap) -CDAC_TYPE_END(Module) - -CDAC_TYPE_BEGIN(ModuleLookupMap) -CDAC_TYPE_FIELD(ModuleLookupMap, /*pointer*/, TableData, offsetof(LookupMapBase, pTable)) -CDAC_TYPE_FIELD(ModuleLookupMap, /*pointer*/, Next, offsetof(LookupMapBase, pNext)) -CDAC_TYPE_FIELD(ModuleLookupMap, /*uint32*/, Count, offsetof(LookupMapBase, dwCount)) -CDAC_TYPE_FIELD(ModuleLookupMap, /*nuint*/, SupportedFlagsMask, offsetof(LookupMapBase, supportedFlags)) -CDAC_TYPE_END(ModuleLookupMap) - -CDAC_TYPE_BEGIN(Assembly) -CDAC_TYPE_INDETERMINATE(Assembly) -#ifdef FEATURE_COLLECTIBLE_TYPES -CDAC_TYPE_FIELD(Assembly, /*uint8*/, IsCollectible, cdac_data::IsCollectible) -#endif -CDAC_TYPE_FIELD(Assembly, /*pointer*/, Module, cdac_data::Module) -CDAC_TYPE_FIELD(Assembly, /*pointer*/, Error, cdac_data::Error) -CDAC_TYPE_FIELD(Assembly, /*uint32*/, NotifyFlags, cdac_data::NotifyFlags) -CDAC_TYPE_FIELD(Assembly, /*uint32*/, Level, cdac_data::Level) -CDAC_TYPE_END(Assembly) - -CDAC_TYPE_BEGIN(LoaderAllocator) -CDAC_TYPE_INDETERMINATE(LoaderAllocator) -CDAC_TYPE_FIELD(LoaderAllocator, /*uint32*/, ReferenceCount, cdac_data::ReferenceCount) -CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, HighFrequencyHeap, cdac_data::HighFrequencyHeap) -CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, LowFrequencyHeap, cdac_data::LowFrequencyHeap) -CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, StubHeap, cdac_data::StubHeap) -CDAC_TYPE_END(LoaderAllocator) - -CDAC_TYPE_BEGIN(PEAssembly) -CDAC_TYPE_INDETERMINATE(PEAssembly) -CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, PEImage, cdac_data::PEImage) -CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, AssemblyBinder, cdac_data::AssemblyBinder) -CDAC_TYPE_END(PEAssembly) - -CDAC_TYPE_BEGIN(AssemblyBinder) -CDAC_TYPE_INDETERMINATE(AssemblyBinder) -CDAC_TYPE_FIELD(AssemblyBinder, /*pointer*/, ManagedAssemblyLoadContext, cdac_data::ManagedAssemblyLoadContext) -CDAC_TYPE_END(AssemblyBinder) - -CDAC_TYPE_BEGIN(PEImage) -CDAC_TYPE_INDETERMINATE(PEImage) -CDAC_TYPE_FIELD(PEImage, /*pointer*/, LoadedImageLayout, cdac_data::LoadedImageLayout) -CDAC_TYPE_FIELD(PEImage, /*ProbeExtensionResult*/, ProbeExtensionResult, cdac_data::ProbeExtensionResult) -CDAC_TYPE_END(PEImage) - -CDAC_TYPE_BEGIN(PEImageLayout) -CDAC_TYPE_FIELD(PEImageLayout, /*pointer*/, Base, cdac_data::Base) -CDAC_TYPE_FIELD(PEImageLayout, /*uint32*/, Size, cdac_data::Size) -CDAC_TYPE_FIELD(PEImageLayout, /*uint32*/, Flags, cdac_data::Flags) -CDAC_TYPE_END(PEImageLayout) - -CDAC_TYPE_BEGIN(CGrowableSymbolStream) -CDAC_TYPE_INDETERMINATE(CGrowableSymbolStream) -CDAC_TYPE_FIELD(CGrowableSymbolStream, /*pointer*/, Buffer, cdac_data::Buffer) -CDAC_TYPE_FIELD(CGrowableSymbolStream, /*uint32*/, Size, cdac_data::Size) -CDAC_TYPE_END(CGrowableSymbolStream) - -CDAC_TYPE_BEGIN(ProbeExtensionResult) -CDAC_TYPE_INDETERMINATE(ProbeExtensionResult) -CDAC_TYPE_FIELD(ProbeExtensionResult, /*int32*/, Type, offsetof(ProbeExtensionResult, Type)) -CDAC_TYPE_END(ProbeExtensionResult) - -CDAC_TYPE_BEGIN(AppDomain) -CDAC_TYPE_INDETERMINATE(AppDomain) -CDAC_TYPE_FIELD(AppDomain, /*pointer*/, RootAssembly, cdac_data::RootAssembly) -CDAC_TYPE_FIELD(AppDomain, /*DomainAssemblyList*/, DomainAssemblyList, cdac_data::DomainAssemblyList) -CDAC_TYPE_FIELD(AppDomain, /*pointer*/, FriendlyName, cdac_data::FriendlyName) -CDAC_TYPE_END(AppDomain) - -CDAC_TYPE_BEGIN(SystemDomain) -CDAC_TYPE_INDETERMINATE(SystemDomain) -CDAC_TYPE_FIELD(SystemDomain, /*GlobalLoaderAllocator*/, GlobalLoaderAllocator, cdac_data::GlobalLoaderAllocator) -CDAC_TYPE_END(SystemDomain) - -CDAC_TYPE_BEGIN(ArrayListBase) -CDAC_TYPE_INDETERMINATE(ArrayListBase) -CDAC_TYPE_FIELD(ArrayListBase, /*uint32*/, Count, cdac_data::Count) -CDAC_TYPE_FIELD(ArrayListBase, /*pointer*/, FirstBlock, cdac_data::FirstBlock) -CDAC_TYPE_END(ArrayListBase) - -CDAC_TYPE_BEGIN(ArrayListBlock) -CDAC_TYPE_INDETERMINATE(ArrayListBlock) -CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, Next, cdac_data::Next) -CDAC_TYPE_FIELD(ArrayListBlock, /*uint32*/, Size, cdac_data::Size) -CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, ArrayStart, cdac_data::ArrayStart) -CDAC_TYPE_END(ArrayListBlock) - -// RuntimeTypeSystem - -CDAC_TYPE_BEGIN(MethodTable) -CDAC_TYPE_SIZE(sizeof(MethodTable)) -CDAC_TYPE_FIELD(MethodTable, /*uint32*/, MTFlags, cdac_data::MTFlags) -CDAC_TYPE_FIELD(MethodTable, /*uint32*/, BaseSize, cdac_data::BaseSize) -CDAC_TYPE_FIELD(MethodTable, /*uint32*/, MTFlags2, cdac_data::MTFlags2) -CDAC_TYPE_FIELD(MethodTable, /*nuint*/, EEClassOrCanonMT, cdac_data::EEClassOrCanonMT) -CDAC_TYPE_FIELD(MethodTable, /*pointer*/, Module, cdac_data::Module) -CDAC_TYPE_FIELD(MethodTable, /*pointer*/, ParentMethodTable, cdac_data::ParentMethodTable) -CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumInterfaces, cdac_data::NumInterfaces) -CDAC_TYPE_FIELD(MethodTable, /*uint16*/, NumVirtuals, cdac_data::NumVirtuals) -CDAC_TYPE_FIELD(MethodTable, /*pointer*/, PerInstInfo, cdac_data::PerInstInfo) -CDAC_TYPE_FIELD(MethodTable, /*pointer*/, AuxiliaryData, cdac_data::AuxiliaryData) -CDAC_TYPE_END(MethodTable) - -CDAC_TYPE_BEGIN(MethodTableAuxiliaryData) -CDAC_TYPE_INDETERMINATE(MethodTableAuxiliaryData) -CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*pointer*/, LoaderModule, offsetof(MethodTableAuxiliaryData, m_pLoaderModule)) -CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*int16*/, OffsetToNonVirtualSlots, offsetof(MethodTableAuxiliaryData, m_offsetToNonVirtualSlots)) -CDAC_TYPE_END(MethodTableAuxiliaryData) - -CDAC_TYPE_BEGIN(EEClass) -CDAC_TYPE_INDETERMINATE(EEClass) -CDAC_TYPE_FIELD(EEClass, /*pointer*/, MethodTable, cdac_data::MethodTable) -CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumMethods, cdac_data::NumMethods) -CDAC_TYPE_FIELD(EEClass, /*pointer*/, FieldDescList, cdac_data::FieldDescList) -CDAC_TYPE_FIELD(EEClass, /*uint32*/, CorTypeAttr, cdac_data::CorTypeAttr) -CDAC_TYPE_FIELD(EEClass, /*uint8*/, InternalCorElementType, cdac_data::InternalCorElementType) -CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumInstanceFields, cdac_data::NumInstanceFields) -CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumStaticFields, cdac_data::NumStaticFields) -CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumThreadStaticFields, cdac_data::NumThreadStaticFields) -CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumNonVirtualSlots, cdac_data::NumNonVirtualSlots) -CDAC_TYPE_END(EEClass) - -CDAC_TYPE_BEGIN(ArrayClass) -CDAC_TYPE_INDETERMINATE(ArrayClass) -CDAC_TYPE_FIELD(ArrayClass, /*uint8*/, Rank, cdac_data::Rank) -CDAC_TYPE_END(ArrayClass) - -CDAC_TYPE_BEGIN(GenericsDictInfo) -CDAC_TYPE_INDETERMINATE(GenericsDictInfo) -CDAC_TYPE_FIELD(GenericsDictInfo, /*uint16*/, NumDicts, offsetof(GenericsDictInfo, m_wNumDicts)) -CDAC_TYPE_FIELD(GenericsDictInfo, /*uint16*/, NumTypeArgs, offsetof(GenericsDictInfo, m_wNumTyPars)) -CDAC_TYPE_END(GenericsDictInfo) - -CDAC_TYPE_BEGIN(TypeDesc) -CDAC_TYPE_INDETERMINATE(TypeDesc) -CDAC_TYPE_FIELD(TypeDesc, /*uint32*/, TypeAndFlags, cdac_data::TypeAndFlags) -CDAC_TYPE_END(TypeDesc) - -CDAC_TYPE_BEGIN(ParamTypeDesc) -CDAC_TYPE_INDETERMINATE(ParamTypeDesc) -CDAC_TYPE_FIELD(ParamTypeDesc, /*pointer*/, TypeArg, cdac_data::TypeArg) -CDAC_TYPE_END(ParamTypeDesc) - -CDAC_TYPE_BEGIN(TypeVarTypeDesc) -CDAC_TYPE_INDETERMINATE(TypeVarTypeDesc) -CDAC_TYPE_FIELD(TypeVarTypeDesc, /*pointer*/, Module, cdac_data::Module) -CDAC_TYPE_FIELD(TypeVarTypeDesc, /*uint32*/, Token, cdac_data::Token) -CDAC_TYPE_END(TypeVarTypeDesc) - -CDAC_TYPE_BEGIN(FnPtrTypeDesc) -CDAC_TYPE_INDETERMINATE(FnPtrTypeDesc) -CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, NumArgs, cdac_data::NumArgs) -CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, CallConv, cdac_data::CallConv) -CDAC_TYPE_FIELD(FnPtrTypeDesc, /*uint32*/, RetAndArgTypes, cdac_data::RetAndArgTypes) -CDAC_TYPE_FIELD(FnPtrTypeDesc, /*pointer*/, LoaderModule, cdac_data::LoaderModule) -CDAC_TYPE_END(FnPtrTypeDesc) - -CDAC_TYPE_BEGIN(DynamicMetadata) -CDAC_TYPE_FIELD(DynamicMetadata, /*uint32*/, Size, cdac_data::Size) -CDAC_TYPE_FIELD(DynamicMetadata, /*inline byte array*/, Data, cdac_data::Data) -CDAC_TYPE_END(DynamicMetadata) - -#ifdef STRESS_LOG -CDAC_TYPE_BEGIN(StressLog) -CDAC_TYPE_SIZE(sizeof(StressLog)) -CDAC_TYPE_FIELD(StressLog, /* uint32 */, LoggedFacilities, cdac_offsets::facilitiesToLog) -CDAC_TYPE_FIELD(StressLog, /* uint32 */, Level, cdac_offsets::levelToLog) -CDAC_TYPE_FIELD(StressLog, /* uint32 */, MaxSizePerThread, cdac_offsets::MaxSizePerThread) -CDAC_TYPE_FIELD(StressLog, /* uint32 */, MaxSizeTotal, cdac_offsets::MaxSizeTotal) -CDAC_TYPE_FIELD(StressLog, /* uint32 */, TotalChunks, cdac_offsets::totalChunk) -CDAC_TYPE_FIELD(StressLog, /* pointer */, Logs, cdac_offsets::logs) -CDAC_TYPE_FIELD(StressLog, /* uint64 */, TickFrequency, cdac_offsets::tickFrequency) -CDAC_TYPE_FIELD(StressLog, /* uint64 */, StartTimestamp, cdac_offsets::startTimeStamp) -CDAC_TYPE_FIELD(StressLog, /* nuint */, ModuleOffset, cdac_offsets::moduleOffset) -CDAC_TYPE_END(StressLog) - -CDAC_TYPE_BEGIN(StressLogModuleDesc) -CDAC_TYPE_SIZE(cdac_offsets::ModuleDesc::type_size) -CDAC_TYPE_FIELD(StressLogModuleDesc, pointer, BaseAddress, cdac_offsets::ModuleDesc::baseAddress) -CDAC_TYPE_FIELD(StressLogModuleDesc, nuint, Size, cdac_offsets::ModuleDesc::size) -CDAC_TYPE_END(StressLogModuleDesc) - -CDAC_TYPE_BEGIN(ThreadStressLog) -CDAC_TYPE_INDETERMINATE(ThreadStressLog) -CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, Next, cdac_offsets::next) -CDAC_TYPE_FIELD(ThreadStressLog, uint64, ThreadId, cdac_offsets::threadId) -CDAC_TYPE_FIELD(ThreadStressLog, uint8, WriteHasWrapped, cdac_offsets::writeHasWrapped) -CDAC_TYPE_FIELD(ThreadStressLog, pointer, CurrentPtr, cdac_offsets::curPtr) -CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, ChunkListHead, cdac_offsets::chunkListHead) -CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, ChunkListTail, cdac_offsets::chunkListTail) -CDAC_TYPE_FIELD(ThreadStressLog, /* pointer */, CurrentWriteChunk, cdac_offsets::curWriteChunk) -CDAC_TYPE_END(ThreadStressLog) - -CDAC_TYPE_BEGIN(StressLogChunk) -CDAC_TYPE_SIZE(sizeof(StressLogChunk)) -CDAC_TYPE_FIELD(StressLogChunk, /* pointer */, Prev, offsetof(StressLogChunk, prev)) -CDAC_TYPE_FIELD(StressLogChunk, /* pointer */, Next, offsetof(StressLogChunk, next)) -CDAC_TYPE_FIELD(StressLogChunk, /* uint8[STRESSLOG_CHUNK_SIZE] */, Buf, offsetof(StressLogChunk, buf)) -CDAC_TYPE_FIELD(StressLogChunk, /* uint32 */, Sig1, offsetof(StressLogChunk, dwSig1)) -CDAC_TYPE_FIELD(StressLogChunk, /* uint32 */, Sig2, offsetof(StressLogChunk, dwSig2)) -CDAC_TYPE_END(StressLogChunk) - -// The StressMsg Header is the fixed size portion of the StressMsg -CDAC_TYPE_BEGIN(StressMsgHeader) -CDAC_TYPE_SIZE(sizeof(StressMsg)) -CDAC_TYPE_END(StressMsgHeader) - -CDAC_TYPE_BEGIN(StressMsg) -CDAC_TYPE_INDETERMINATE(StressMsg) -CDAC_TYPE_FIELD(StressMsg, StressMsgHeader, Header, 0) -CDAC_TYPE_FIELD(StressMsg, /* pointer */, Args, offsetof(StressMsg, args)) -CDAC_TYPE_END(StressMsg) -#endif - -CDAC_TYPE_BEGIN(MethodDesc) -CDAC_TYPE_SIZE(sizeof(MethodDesc)) -CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, ChunkIndex, cdac_data::ChunkIndex) -CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Slot, cdac_data::Slot) -CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags, cdac_data::Flags) -CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags3AndTokenRemainder, cdac_data::Flags3AndTokenRemainder) -CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, EntryPointFlags, cdac_data::EntryPointFlags) -CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, CodeData, cdac_data::CodeData) -#ifdef HAVE_GCCOVER -CDAC_TYPE_FIELD(MethodDesc, /*pointer*/, GCCoverageInfo, offsetof(MethodDesc, m_GcCover)) -#endif // HAVE_GCCOVER -CDAC_TYPE_END(MethodDesc) - -CDAC_TYPE_BEGIN(MethodDescChunk) -CDAC_TYPE_SIZE(sizeof(MethodDescChunk)) -CDAC_TYPE_FIELD(MethodDescChunk, /*pointer*/, MethodTable, cdac_data::MethodTable) -CDAC_TYPE_FIELD(MethodDescChunk, /*pointer*/, Next, cdac_data::Next) -CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Size, cdac_data::Size) -CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Count, cdac_data::Count) -CDAC_TYPE_FIELD(MethodDescChunk, /*uint16*/, FlagsAndTokenRange, cdac_data::FlagsAndTokenRange) -CDAC_TYPE_END(MethodDescChunk) - -CDAC_TYPE_BEGIN(NonVtableSlot) -CDAC_TYPE_SIZE(sizeof(MethodDesc::NonVtableSlot)) -CDAC_TYPE_END(NonVtableSlot) - -CDAC_TYPE_BEGIN(MethodImpl) -CDAC_TYPE_SIZE(sizeof(MethodImpl)) -CDAC_TYPE_END(MethodImpl) - -CDAC_TYPE_BEGIN(NativeCodeSlot) -CDAC_TYPE_SIZE(sizeof(MethodDesc::NativeCodeSlot)) -CDAC_TYPE_END(NativeCodeSlot) - -CDAC_TYPE_BEGIN(InstantiatedMethodDesc) -CDAC_TYPE_SIZE(sizeof(InstantiatedMethodDesc)) -CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*pointer*/, PerInstInfo, cdac_data::PerInstInfo) -CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*uint16*/, Flags2, cdac_data::Flags2) -CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*uint16*/, NumGenericArgs, cdac_data::NumGenericArgs) -CDAC_TYPE_END(InstantiatedMethodDesc) - -CDAC_TYPE_BEGIN(StoredSigMethodDesc) -CDAC_TYPE_INDETERMINATE(StoredSigMethodDesc) -CDAC_TYPE_FIELD(StoredSigMethodDesc, /*pointer*/, Sig, cdac_data::Sig) -CDAC_TYPE_FIELD(StoredSigMethodDesc, /*uint32*/, cSig, cdac_data::cSig) -CDAC_TYPE_FIELD(StoredSigMethodDesc, /*uint32*/, ExtendedFlags, cdac_data::ExtendedFlags) -CDAC_TYPE_END(StoredSigMethodDesc) - -CDAC_TYPE_BEGIN(DynamicMethodDesc) -CDAC_TYPE_SIZE(sizeof(DynamicMethodDesc)) -CDAC_TYPE_FIELD(DynamicMethodDesc, /*pointer*/, MethodName, cdac_data::MethodName) -CDAC_TYPE_END(DynamicMethodDesc) - -CDAC_TYPE_BEGIN(ArrayMethodDesc) -CDAC_TYPE_SIZE(sizeof(ArrayMethodDesc)) -CDAC_TYPE_END(ArrayMethodDesc) - -CDAC_TYPE_BEGIN(FCallMethodDesc) -CDAC_TYPE_SIZE(sizeof(FCallMethodDesc)) -CDAC_TYPE_END(FCallMethodDesc) - -CDAC_TYPE_BEGIN(PInvokeMethodDesc) -CDAC_TYPE_SIZE(sizeof(NDirectMethodDesc)) -CDAC_TYPE_END(PInvokeMethodDesc) - -CDAC_TYPE_BEGIN(EEImplMethodDesc) -CDAC_TYPE_SIZE(sizeof(EEImplMethodDesc)) -CDAC_TYPE_END(EEImplMethodDesc) - -#ifdef FEATURE_COMINTEROP -CDAC_TYPE_BEGIN(CLRToCOMCallMethodDesc) -CDAC_TYPE_SIZE(sizeof(CLRToCOMCallMethodDesc)) -CDAC_TYPE_END(CLRToCOMCallMethodDesc) -#endif // FEATURE_COMINTEROP - -CDAC_TYPE_BEGIN(CodePointer) -CDAC_TYPE_SIZE(sizeof(PCODE)) -CDAC_TYPE_END(CodePointer) - -CDAC_TYPE_BEGIN(MethodDescCodeData) -CDAC_TYPE_INDETERMINATE(MethodDescCodeData) -CDAC_TYPE_FIELD(MethodDescCodeData, /*CodePointer*/, TemporaryEntryPoint, offsetof(MethodDescCodeData,TemporaryEntryPoint)) -CDAC_TYPE_FIELD(MethodDescCodeData, /*pointer*/, VersioningState, offsetof(MethodDescCodeData,VersioningState)) -CDAC_TYPE_END(MethodDescCodeData) - -CDAC_TYPE_BEGIN(MethodDescVersioningState) -CDAC_TYPE_INDETERMINATE(MethodDescVersioningState) -CDAC_TYPE_FIELD(MethodDescVersioningState, /*pointer*/, NativeCodeVersionNode, cdac_data::NativeCodeVersionNode) -CDAC_TYPE_FIELD(MethodDescVersioningState, /*uint8*/, Flags, cdac_data::Flags) -CDAC_TYPE_END(MethodDescVersioningState) - -CDAC_TYPE_BEGIN(PrecodeMachineDescriptor) -CDAC_TYPE_INDETERMINATE(PrecodeMachineDescriptor) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, InvalidPrecodeType, offsetof(PrecodeMachineDescriptor, InvalidPrecodeType)) -#ifdef HAS_NDIRECT_IMPORT_PRECODE -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, PInvokeImportPrecodeType, offsetof(PrecodeMachineDescriptor, PInvokeImportPrecodeType)) -#endif - -#ifdef HAS_FIXUP_PRECODE -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupPrecodeType, offsetof(PrecodeMachineDescriptor, FixupPrecodeType)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupCodeOffset, offsetof(PrecodeMachineDescriptor, FixupCodeOffset)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupStubPrecodeSize, offsetof(PrecodeMachineDescriptor, FixupStubPrecodeSize)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, FixupBytes, offsetof(PrecodeMachineDescriptor, FixupBytes)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, FixupIgnoredBytes, offsetof(PrecodeMachineDescriptor, FixupIgnoredBytes)) -#endif // HAS_FIXUP_PRECODE - -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, StubPrecodeSize, offsetof(PrecodeMachineDescriptor, StubPrecodeSize)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, StubPrecodeType, offsetof(PrecodeMachineDescriptor, StubPrecodeType)) - -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, StubBytes, offsetof(PrecodeMachineDescriptor, StubBytes)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, StubIgnoredBytes, offsetof(PrecodeMachineDescriptor, StubIgnoredBytes)) - -#ifdef HAS_THISPTR_RETBUF_PRECODE -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, ThisPointerRetBufPrecodeType, offsetof(PrecodeMachineDescriptor, ThisPointerRetBufPrecodeType)) -#endif -#ifdef FEATURE_INTERPRETER -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, InterpreterPrecodeType, offsetof(PrecodeMachineDescriptor, InterpreterPrecodeType)) -#endif -#ifdef FEATURE_STUBPRECODE_DYNAMIC_HELPERS -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, DynamicHelperPrecodeType, offsetof(PrecodeMachineDescriptor, DynamicHelperPrecodeType)) -#endif -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, UMEntryPrecodeType, offsetof(PrecodeMachineDescriptor, UMEntryPrecodeType)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint32*/, StubCodePageSize, offsetof(PrecodeMachineDescriptor, StubCodePageSize)) -CDAC_TYPE_END(PrecodeMachineDescriptor) - -CDAC_TYPE_BEGIN(PlatformMetadata) -CDAC_TYPE_INDETERMINATE(PlatformMetadata) -CDAC_TYPE_FIELD(PlatformMetadata, /*PrecodeMachineDescriptor*/, PrecodeMachineDescriptor, offsetof(CDacPlatformMetadata, precode)) -CDAC_TYPE_FIELD(PlatformMetadata, /*uint8*/, CodePointerFlags, offsetof(CDacPlatformMetadata, codePointerFlags)) -CDAC_TYPE_END(PlatformMetadata) - -CDAC_TYPE_BEGIN(StubPrecodeData) -CDAC_TYPE_INDETERMINATE(StubPrecodeData) -CDAC_TYPE_FIELD(StubPrecodeData, /*pointer*/, SecretParam, offsetof(StubPrecodeData, SecretParam)) -CDAC_TYPE_FIELD(StubPrecodeData, /*uint8*/, Type, offsetof(StubPrecodeData, Type)) -CDAC_TYPE_END(StubPrecodeData) - -#ifdef HAS_THISPTR_RETBUF_PRECODE -CDAC_TYPE_BEGIN(ThisPtrRetBufPrecodeData) -CDAC_TYPE_INDETERMINATE(ThisPtrRetBufPrecodeData) -CDAC_TYPE_FIELD(ThisPtrRetBufPrecodeData, /*pointer*/, MethodDesc, offsetof(ThisPtrRetBufPrecodeData, MethodDesc)) -CDAC_TYPE_END(ThisPtrRetBufPrecodeData) -#endif - -CDAC_TYPE_BEGIN(FixupPrecodeData) -CDAC_TYPE_INDETERMINATE(FixupPrecodeData) -CDAC_TYPE_FIELD(FixupPrecodeData, /*pointer*/, MethodDesc, offsetof(FixupPrecodeData, MethodDesc)) -CDAC_TYPE_END(FixupPrecodeData) - -CDAC_TYPE_BEGIN(ReadyToRunInfo) -CDAC_TYPE_INDETERMINATE(ReadyToRunInfo) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, ReadyToRunHeader, cdac_data::ReadyToRunHeader) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, CompositeInfo, cdac_data::CompositeInfo) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumRuntimeFunctions, cdac_data::NumRuntimeFunctions) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, RuntimeFunctions, cdac_data::RuntimeFunctions) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumHotColdMap, cdac_data::NumHotColdMap) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, HotColdMap, cdac_data::HotColdMap) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_data::DelayLoadMethodCallThunks) -CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data::EntryPointToMethodDescMap) -CDAC_TYPE_END(ReadyToRunInfo) - -CDAC_TYPE_BEGIN(ReadyToRunHeader) -CDAC_TYPE_INDETERMINATE(READYTORUN_HEADER) -CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MajorVersion, offsetof(READYTORUN_HEADER, MajorVersion)) -CDAC_TYPE_FIELD(ReadyToRunHeader, /*uint16*/, MinorVersion, offsetof(READYTORUN_HEADER, MinorVersion)) -CDAC_TYPE_END(ReadyToRunHeader) - -CDAC_TYPE_BEGIN(ImageDataDirectory) -CDAC_TYPE_SIZE(sizeof(IMAGE_DATA_DIRECTORY)) -CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, VirtualAddress, offsetof(IMAGE_DATA_DIRECTORY, VirtualAddress)) -CDAC_TYPE_FIELD(ImageDataDirectory, /*uint32*/, Size, offsetof(IMAGE_DATA_DIRECTORY, Size)) -CDAC_TYPE_END(ImageDataDirectory) - -CDAC_TYPE_BEGIN(RuntimeFunction) -CDAC_TYPE_SIZE(sizeof(RUNTIME_FUNCTION)) -CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, BeginAddress, offsetof(RUNTIME_FUNCTION, BeginAddress)) -#ifdef TARGET_AMD64 -CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, EndAddress, offsetof(RUNTIME_FUNCTION, EndAddress)) -#endif -CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, UnwindData, offsetof(RUNTIME_FUNCTION, UnwindData)) -CDAC_TYPE_END(RuntimeFunction) - -CDAC_TYPE_BEGIN(UnwindInfo) -CDAC_TYPE_INDETERMINATE(UnwindInfo) -#ifdef TARGET_X86 -CDAC_TYPE_FIELD(UnwindInfo, /*uint32*/, FunctionLength, offsetof(UNWIND_INFO, FunctionLength)) -#endif -CDAC_TYPE_END(UnwindInfo) - -CDAC_TYPE_BEGIN(HashMap) -CDAC_TYPE_INDETERMINATE(HashMap) -CDAC_TYPE_FIELD(HashMap, /*pointer*/, Buckets, cdac_data::Buckets) -CDAC_TYPE_END(HashMap) - -CDAC_TYPE_BEGIN(Bucket) -CDAC_TYPE_SIZE(sizeof(Bucket)) -CDAC_TYPE_FIELD(Bucket, /*pointer*/, Keys, offsetof(Bucket, m_rgKeys)) -CDAC_TYPE_FIELD(Bucket, /*pointer*/, Values, offsetof(Bucket, m_rgValues)) -CDAC_TYPE_END(Bucket) - -CDAC_TYPE_BEGIN(RangeSectionMap) -CDAC_TYPE_INDETERMINATE(RangeSectionMap) -CDAC_TYPE_FIELD(RangeSectionMap, /*pointer*/, TopLevelData, cdac_data::TopLevelData) -CDAC_TYPE_END(RangeSectionMap) - -CDAC_TYPE_BEGIN(RangeSectionFragment) -CDAC_TYPE_INDETERMINATE(RangeSectionFragment) -CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, RangeBegin, cdac_data::RangeSectionFragment::RangeBegin) -CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, RangeEndOpen, cdac_data::RangeSectionFragment::RangeEndOpen) -CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, RangeSection, cdac_data::RangeSectionFragment::RangeSection) -CDAC_TYPE_FIELD(RangeSectionFragment, /*pointer*/, Next, cdac_data::RangeSectionFragment::Next) -CDAC_TYPE_END(RangeSectionFragment) - -CDAC_TYPE_BEGIN(RangeSection) -CDAC_TYPE_INDETERMINATE(RangeSection) -CDAC_TYPE_FIELD(RangeSection, /*pointer*/, RangeBegin, cdac_data::RangeBegin) -CDAC_TYPE_FIELD(RangeSection, /*pointer*/, RangeEndOpen, cdac_data::RangeEndOpen) -CDAC_TYPE_FIELD(RangeSection, /*pointer*/, NextForDelete, cdac_data::NextForDelete) -CDAC_TYPE_FIELD(RangeSection, /*pointer*/, JitManager, cdac_data::JitManager) -CDAC_TYPE_FIELD(RangeSection, /*int32_t*/, Flags, cdac_data::Flags) -CDAC_TYPE_FIELD(RangeSection, /*pointer*/, HeapList, cdac_data::HeapList) -CDAC_TYPE_FIELD(RangeSection, /*pointer*/, R2RModule, cdac_data::R2RModule) -CDAC_TYPE_END(RangeSection) - -CDAC_TYPE_BEGIN(RealCodeHeader) -CDAC_TYPE_INDETERMINATE(RealCodeHeader) -CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, MethodDesc, offsetof(RealCodeHeader, phdrMDesc)) -CDAC_TYPE_FIELD(RealCodeHeader, /*pointer*/, GCInfo, offsetof(RealCodeHeader, phdrJitGCInfo)) -#ifdef FEATURE_EH_FUNCLETS -CDAC_TYPE_FIELD(RealCodeHeader, /*uint32*/, NumUnwindInfos, offsetof(RealCodeHeader, nUnwindInfos)) -CDAC_TYPE_FIELD(RealCodeHeader, /* T_RUNTIME_FUNCTION */, UnwindInfos, offsetof(RealCodeHeader, unwindInfos)) -#endif // FEATURE_EH_FUNCLETS -CDAC_TYPE_END(RealCodeHeader) - -CDAC_TYPE_BEGIN(CodeHeapListNode) -CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, Next, offsetof(HeapList, hpNext)) -CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, StartAddress, offsetof(HeapList, startAddress)) -CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, EndAddress, offsetof(HeapList, endAddress)) -CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, MapBase, offsetof(HeapList, mapBase)) -CDAC_TYPE_FIELD(CodeHeapListNode, /*pointer*/, HeaderMap, offsetof(HeapList, pHdrMap)) -CDAC_TYPE_END(CodeHeapListNode) - -CDAC_TYPE_BEGIN(ILCodeVersioningState) -CDAC_TYPE_INDETERMINATE(ILCodeVersioningState) -CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, FirstVersionNode, cdac_data::FirstVersionNode) -CDAC_TYPE_FIELD(ILCodeVersioningState, /*uint32*/, ActiveVersionKind, cdac_data::ActiveVersionKind) -CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, ActiveVersionNode, cdac_data::ActiveVersionNode) -CDAC_TYPE_FIELD(ILCodeVersioningState, /*pointer*/, ActiveVersionModule, cdac_data::ActiveVersionModule) -CDAC_TYPE_FIELD(ILCodeVersioningState, /*uint32*/, ActiveVersionMethodDef, cdac_data::ActiveVersionMethodDef) -CDAC_TYPE_END(ILCodeVersioningState) - -CDAC_TYPE_BEGIN(NativeCodeVersionNode) -CDAC_TYPE_INDETERMINATE(NativeCodeVersionNode) -CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, Next, cdac_data::Next) -CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, MethodDesc, cdac_data::MethodDesc) -CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, NativeCode, cdac_data::NativeCode) -CDAC_TYPE_FIELD(NativeCodeVersionNode, /*uint32*/, Flags, cdac_data::Flags) -CDAC_TYPE_FIELD(NativeCodeVersionNode, /*nuint*/, ILVersionId, cdac_data::ILVersionId) -#ifdef HAVE_GCCOVER -CDAC_TYPE_FIELD(NativeCodeVersionNode, /*pointer*/, GCCoverageInfo, cdac_data::GCCoverageInfo) -#endif // HAVE_GCCOVER -CDAC_TYPE_END(NativeCodeVersionNode) - -CDAC_TYPE_BEGIN(ILCodeVersionNode) -CDAC_TYPE_INDETERMINATE(ILCodeVersionNode) -CDAC_TYPE_FIELD(ILCodeVersionNode, /*nuint*/, VersionId, cdac_data::VersionId) -CDAC_TYPE_FIELD(ILCodeVersionNode, /*pointer*/, Next, cdac_data::Next) -CDAC_TYPE_FIELD(ILCodeVersionNode, /*uint32*/, RejitState, cdac_data::RejitState) -CDAC_TYPE_END(ILCodeVersionNode) - -CDAC_TYPE_BEGIN(ProfControlBlock) -CDAC_TYPE_FIELD(ProfControlBlock, /*uint64*/, GlobalEventMask, offsetof(ProfControlBlock, globalEventMask)) -CDAC_TYPE_END(ProfControlBlock) - -#ifdef HAVE_GCCOVER -CDAC_TYPE_BEGIN(GCCoverageInfo) -CDAC_TYPE_INDETERMINATE(GCCoverageInfo) -CDAC_TYPE_FIELD(GCCoverageInfo, /*pointer*/, SavedCode, offsetof(GCCoverageInfo, savedCode)) -CDAC_TYPE_END(GCCoverageInfo) -#endif // HAVE_GCCOVER - -CDAC_TYPE_BEGIN(Frame) -CDAC_TYPE_INDETERMINATE(Frame) -CDAC_TYPE_FIELD(Frame, /*pointer*/, Next, cdac_data::Next) -CDAC_TYPE_END(Frame) - -CDAC_TYPE_BEGIN(InlinedCallFrame) -CDAC_TYPE_SIZE(sizeof(InlinedCallFrame)) -CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CallSiteSP, offsetof(InlinedCallFrame, m_pCallSiteSP)) -CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CallerReturnAddress, offsetof(InlinedCallFrame, m_pCallerReturnAddress)) -CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CalleeSavedFP, offsetof(InlinedCallFrame, m_pCalleeSavedFP)) -#ifdef TARGET_ARM -CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, SPAfterProlog, offsetof(InlinedCallFrame, m_pSPAfterProlog)) -#endif // TARGET_ARM -CDAC_TYPE_END(InlinedCallFrame) - -CDAC_TYPE_BEGIN(SoftwareExceptionFrame) -CDAC_TYPE_SIZE(sizeof(SoftwareExceptionFrame)) -CDAC_TYPE_FIELD(SoftwareExceptionFrame, /*T_CONTEXT*/, TargetContext, cdac_data::TargetContext) -CDAC_TYPE_FIELD(SoftwareExceptionFrame, /*pointer*/, ReturnAddress, cdac_data::ReturnAddress) -CDAC_TYPE_END(SoftwareExceptionFrame) - -CDAC_TYPE_BEGIN(FramedMethodFrame) -CDAC_TYPE_SIZE(sizeof(FramedMethodFrame)) -CDAC_TYPE_FIELD(FramedMethodFrame, /*pointer*/, TransitionBlockPtr, cdac_data::TransitionBlockPtr) -CDAC_TYPE_END(FramedMethodFrame) - -CDAC_TYPE_BEGIN(TransitionBlock) -CDAC_TYPE_SIZE(sizeof(TransitionBlock)) -CDAC_TYPE_FIELD(TransitionBlock, /*pointer*/, ReturnAddress, offsetof(TransitionBlock, m_ReturnAddress)) -CDAC_TYPE_FIELD(TransitionBlock, /*CalleeSavedRegisters*/, CalleeSavedRegisters, offsetof(TransitionBlock, m_calleeSavedRegisters)) -#ifdef TARGET_ARM -CDAC_TYPE_FIELD(TransitionBlock, /*ArgumentRegisters*/, ArgumentRegisters, offsetof(TransitionBlock, m_argumentRegisters)) -#endif // TARGET_ARM -CDAC_TYPE_END(TransitionBlock) - -#ifdef DEBUGGING_SUPPORTED -CDAC_TYPE_BEGIN(FuncEvalFrame) -CDAC_TYPE_SIZE(sizeof(FuncEvalFrame)) -CDAC_TYPE_FIELD(FuncEvalFrame, /*pointer*/, DebuggerEvalPtr, cdac_data::DebuggerEvalPtr) -CDAC_TYPE_END(FuncEvalFrame) - -CDAC_TYPE_BEGIN(DebuggerEval) -CDAC_TYPE_SIZE(sizeof(DebuggerEval)) -CDAC_TYPE_FIELD(DebuggerEval, /*T_CONTEXT*/, TargetContext, offsetof(DebuggerEval, m_context)) -CDAC_TYPE_FIELD(DebuggerEval, /*bool*/, EvalDuringException, offsetof(DebuggerEval, m_evalDuringException)) -CDAC_TYPE_END(DebuggerEval) -#endif // DEBUGGING_SUPPORTED - -#ifdef FEATURE_HIJACK -CDAC_TYPE_BEGIN(ResumableFrame) -CDAC_TYPE_SIZE(sizeof(ResumableFrame)) -CDAC_TYPE_FIELD(ResumableFrame, /*pointer*/, TargetContextPtr, cdac_data::TargetContextPtr) -CDAC_TYPE_END(ResumableFrame) - -CDAC_TYPE_BEGIN(HijackFrame) -CDAC_TYPE_SIZE(sizeof(HijackFrame)) -CDAC_TYPE_FIELD(HijackFrame, /*pointer*/, ReturnAddress, cdac_data::ReturnAddress) -CDAC_TYPE_FIELD(HijackFrame, /*pointer*/, HijackArgsPtr, cdac_data::HijackArgsPtr) -CDAC_TYPE_END(HijackFrame) - -// HijackArgs struct is different on each platform -CDAC_TYPE_BEGIN(HijackArgs) -CDAC_TYPE_SIZE(sizeof(HijackArgs)) -#if defined(TARGET_AMD64) - -CDAC_TYPE_FIELD(HijackArgs, /*CalleeSavedRegisters*/, CalleeSavedRegisters, offsetof(HijackArgs, Regs)) -#ifdef TARGET_WINDOWS -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Rsp, offsetof(HijackArgs, Rsp)) -#endif // TARGET_WINDOWS - -#elif defined(TARGET_ARM64) - -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X0, offsetof(HijackArgs, X0)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X1, offsetof(HijackArgs, X1)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X19, offsetof(HijackArgs, X19)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X20, offsetof(HijackArgs, X20)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X21, offsetof(HijackArgs, X21)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X22, offsetof(HijackArgs, X22)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X23, offsetof(HijackArgs, X23)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X24, offsetof(HijackArgs, X24)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X25, offsetof(HijackArgs, X25)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X26, offsetof(HijackArgs, X26)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X27, offsetof(HijackArgs, X27)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, X28, offsetof(HijackArgs, X28)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Fp, offsetof(HijackArgs, X29)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Lr, offsetof(HijackArgs, Lr)) - -#elif defined(TARGET_X86) - -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Edi, offsetof(HijackArgs, Edi)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Esi, offsetof(HijackArgs, Esi)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Ebx, offsetof(HijackArgs, Ebx)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Edx, offsetof(HijackArgs, Edx)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Ecx, offsetof(HijackArgs, Ecx)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Eax, offsetof(HijackArgs, Eax)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Ebp, offsetof(HijackArgs, Ebp)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, Eip, offsetof(HijackArgs, Eip)) - -#elif defined(TARGET_ARM) - -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R0, offsetof(HijackArgs, R0)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R2, offsetof(HijackArgs, R2)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R4, offsetof(HijackArgs, R4)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R5, offsetof(HijackArgs, R5)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R6, offsetof(HijackArgs, R6)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R7, offsetof(HijackArgs, R7)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R8, offsetof(HijackArgs, R8)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R9, offsetof(HijackArgs, R9)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R10, offsetof(HijackArgs, R10)) -CDAC_TYPE_FIELD(HijackArgs, /*pointer*/, R11, offsetof(HijackArgs, R11)) - -#endif // Platform switch -CDAC_TYPE_END(HijackArgs) -#endif // FEATURE_HIJACK - -CDAC_TYPE_BEGIN(FaultingExceptionFrame) -CDAC_TYPE_SIZE(sizeof(FaultingExceptionFrame)) -#ifdef FEATURE_EH_FUNCLETS -CDAC_TYPE_FIELD(FaultingExceptionFrame, /*T_CONTEXT*/, TargetContext, cdac_data::TargetContext) -#endif // FEATURE_EH_FUNCLETS -CDAC_TYPE_END(FaultingExceptionFrame) - -#if defined(TARGET_X86) && !defined(UNIX_X86_ABI) -CDAC_TYPE_BEGIN(TailCallFrame) -CDAC_TYPE_SIZE(sizeof(TailCallFrame)) -CDAC_TYPE_FIELD(TailCallFrame, /*CalleeSavedRegisters*/, CalleeSavedRegisters, cdac_data::CalleeSavedRegisters) -CDAC_TYPE_FIELD(TailCallFrame, /*pointer*/, ReturnAddress, cdac_data::ReturnAddress) -CDAC_TYPE_END(TailCallFrame) -#endif // TARGET_X86 && !UNIX_X86_ABI - -// ArgumentRegisters struct is different on each platform -CDAC_TYPE_BEGIN(ArgumentRegisters) -CDAC_TYPE_SIZE(sizeof(ArgumentRegisters)) -#if defined(TARGET_ARM) - -CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R0, offsetof(ArgumentRegisters, r[0])) -CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R1, offsetof(ArgumentRegisters, r[1])) -CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R2, offsetof(ArgumentRegisters, r[2])) -CDAC_TYPE_FIELD(ArgumentRegisters, /*nuint*/, R3, offsetof(ArgumentRegisters, r[3])) - -#endif // TARGET_ARM -CDAC_TYPE_END(ArgumentRegisters) - -// CalleeSavedRegisters struct is different on each platform -CDAC_TYPE_BEGIN(CalleeSavedRegisters) -CDAC_TYPE_SIZE(sizeof(CalleeSavedRegisters)) -#if defined(TARGET_AMD64) || defined(TARGET_X86) - -#define CALLEE_SAVED_REGISTER(regname) \ - CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, regname, offsetof(CalleeSavedRegisters, regname)) -ENUM_CALLEE_SAVED_REGISTERS() -#undef CALLEE_SAVED_REGISTER - -#elif defined(TARGET_ARM) - -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R4, offsetof(CalleeSavedRegisters, r4)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R5, offsetof(CalleeSavedRegisters, r5)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R6, offsetof(CalleeSavedRegisters, r6)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R7, offsetof(CalleeSavedRegisters, r7)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R8, offsetof(CalleeSavedRegisters, r8)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R9, offsetof(CalleeSavedRegisters, r9)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R10, offsetof(CalleeSavedRegisters, r10)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, R11, offsetof(CalleeSavedRegisters, r11)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, Lr, offsetof(CalleeSavedRegisters, r14)) - -#elif defined(TARGET_ARM64) - -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X19, offsetof(CalleeSavedRegisters, x19)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X20, offsetof(CalleeSavedRegisters, x20)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X21, offsetof(CalleeSavedRegisters, x21)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X22, offsetof(CalleeSavedRegisters, x22)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X23, offsetof(CalleeSavedRegisters, x23)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X24, offsetof(CalleeSavedRegisters, x24)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X25, offsetof(CalleeSavedRegisters, x25)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X26, offsetof(CalleeSavedRegisters, x26)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X27, offsetof(CalleeSavedRegisters, x27)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, X28, offsetof(CalleeSavedRegisters, x28)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, Fp, offsetof(CalleeSavedRegisters, x29)) -CDAC_TYPE_FIELD(CalleeSavedRegisters, /*nuint*/, Lr, offsetof(CalleeSavedRegisters, x30)) - -#endif // Platform switch -CDAC_TYPE_END(CalleeSavedRegisters) - -CDAC_TYPES_END() - -CDAC_GLOBALS_BEGIN() - -#if defined(TARGET_UNIX) -CDAC_GLOBAL_STRING(OperatingSystem, unix) -#elif defined(TARGET_WINDOWS) -CDAC_GLOBAL_STRING(OperatingSystem, windows) -#else -#error TARGET_{OS} define is not recognized by the cDAC. Update this switch and the enum values in IRuntimeInfo.cs -#endif - -#if defined(TARGET_X86) -CDAC_GLOBAL_STRING(Architecture, x86) -#elif defined(TARGET_AMD64) -CDAC_GLOBAL_STRING(Architecture, x64) -#elif defined(TARGET_ARM) -CDAC_GLOBAL_STRING(Architecture, arm) -#elif defined(TARGET_ARM64) -CDAC_GLOBAL_STRING(Architecture, arm64) -#elif defined(TARGET_LOONGARCH64) -CDAC_GLOBAL_STRING(Architecture, loongarch64) -#elif defined(TARGET_RISCV64) -CDAC_GLOBAL_STRING(Architecture, riscv64) -#else -#error TARGET_{ARCH} define is not recognized by the cDAC. Update this switch and the enum values in IRuntimeInfo.cs -#endif - -CDAC_GLOBAL_STRING(RID, RID_STRING) - -CDAC_GLOBAL(GCInfoVersion, uint32, GCINFO_VERSION) - -CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain) -CDAC_GLOBAL_POINTER(SystemDomain, cdac_data::SystemDomainPtr) -CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) -CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread) -CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread) - -// Add FrameIdentifier for all defined Frame types. Used to differentiate Frame objects. -#define FRAME_TYPE_NAME(frameType) \ - CDAC_GLOBAL(frameType##Identifier, nuint, (uint64_t)FrameIdentifier::frameType) - - #include "frames.h" -#undef FRAME_TYPE_NAME - -CDAC_GLOBAL(MethodDescTokenRemainderBitCount, uint8, METHOD_TOKEN_REMAINDER_BIT_COUNT) -#if FEATURE_COMINTEROP -CDAC_GLOBAL(FeatureCOMInterop, uint8, 1) -#else -CDAC_GLOBAL(FeatureCOMInterop, uint8, 0) -#endif -// See Object::GetGCSafeMethodTable -#ifdef TARGET_64BIT -CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1 | 1 << 2) -#else -CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1) -#endif //TARGET_64BIT -CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION) -CDAC_GLOBAL(DirectorySeparator, uint8, (uint8_t)DIRECTORY_SEPARATOR_CHAR_A) -CDAC_GLOBAL(HashMapSlotsPerBucket, uint32, SLOTS_PER_BUCKET) -CDAC_GLOBAL(HashMapValueMask, uint64, VALUE_MASK) -CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT) -CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE) -CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data::SyncBlockValue) -CDAC_GLOBAL(StubCodeBlockLast, uint8, STUB_CODE_BLOCK_LAST) -CDAC_GLOBAL(DefaultADID, uint32, DefaultADID) -CDAC_GLOBAL(MaxClrNotificationArgs, uint32, MAX_CLR_NOTIFICATION_ARGS) -CDAC_GLOBAL_POINTER(ClrNotificationArguments, &::g_clrNotificationArguments) -CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data::ArrayBoundsZero) -CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass) -CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable) -CDAC_GLOBAL_POINTER(ObjectMethodTable, &::g_pObjectClass) -CDAC_GLOBAL_POINTER(ObjectArrayMethodTable, &::g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]) -CDAC_GLOBAL_POINTER(StringMethodTable, &::g_pStringClass) -CDAC_GLOBAL_POINTER(SyncTableEntries, &::g_pSyncTable) -CDAC_GLOBAL_POINTER(MiniMetaDataBuffAddress, &::g_MiniMetaDataBuffAddress) -CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize) -CDAC_GLOBAL_POINTER(DacNotificationFlags, &::g_dacNotificationFlags) -CDAC_GLOBAL_POINTER(OffsetOfCurrentThreadInfo, &::g_offsetOfCurrentThreadInfo) -#ifdef TARGET_WINDOWS -CDAC_GLOBAL_POINTER(TlsIndexBase, &::_tls_index) -#endif // TARGET_WINDOWS -#ifdef STRESS_LOG -CDAC_GLOBAL(StressLogEnabled, uint8, 1) -CDAC_GLOBAL_POINTER(StressLog, &g_pStressLog) -CDAC_GLOBAL(StressLogHasModuleTable, uint8, 1) -CDAC_GLOBAL_POINTER(StressLogModuleTable, &g_pStressLog->modules) -CDAC_GLOBAL(StressLogMaxModules, uint64, cdac_offsets::MAX_MODULES) -CDAC_GLOBAL(StressLogChunkSize, uint32, STRESSLOG_CHUNK_SIZE) -CDAC_GLOBAL(StressLogValidChunkSig, uint32, StressLogChunk::ValidChunkSig) -CDAC_GLOBAL(StressLogMaxMessageSize, uint64, (uint64_t)StressMsg::maxMsgSize) -#else -CDAC_GLOBAL(StressLogEnabled, uint8, 0) -#endif -CDAC_GLOBAL_POINTER(ExecutionManagerCodeRangeMapAddress, cdac_data::CodeRangeMapAddress) -CDAC_GLOBAL_POINTER(PlatformMetadata, &::g_cdacPlatformMetadata) -CDAC_GLOBAL_POINTER(ProfilerControlBlock, &::g_profControlBlock) -CDAC_GLOBALS_END() - -#undef CDAC_BASELINE -#undef CDAC_TYPES_BEGIN -#undef CDAC_TYPE_BEGIN -#undef CDAC_TYPE_INDETERMINATE -#undef CDAC_TYPE_SIZE -#undef CDAC_TYPE_FIELD -#undef CDAC_TYPE_END -#undef CDAC_TYPES_END -#undef CDAC_GLOBALS_BEGIN -#undef CDAC_GLOBAL -#undef CDAC_GLOBAL_POINTER -#undef CDAC_GLOBAL_STRING -#undef CDAC_GLOBALS_END diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs deleted file mode 100644 index c2b9bbae1c6ea4..00000000000000 --- a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/Empty.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; - -namespace System.Collections.Generic -{ - // - // Helper class to store reusable empty IEnumerables. - // - internal static class Empty - { - // - // Returns a reusable empty IEnumerable (that does not secretly implement more advanced collection interfaces.) - // - public static IEnumerable Enumerable - { - get - { - return _enumerable; - } - } - - private sealed class EmptyEnumImpl : IEnumerable, IEnumerator - { - public IEnumerator GetEnumerator() - { - return this; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this; - } - - public T Current - { - get { throw new InvalidOperationException(); } - } - - object IEnumerator.Current - { - get { throw new InvalidOperationException(); } - } - - public bool MoveNext() - { - return false; - } - - public void Reset() - { - } - - public void Dispose() - { - } - } - - private static IEnumerable _enumerable = new EmptyEnumImpl(); - } -} diff --git a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs b/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs deleted file mode 100644 index fc068b9c31e009..00000000000000 --- a/src/coreclr/nativeaot/Common/src/System/Collections/Generic/LowLevelList.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** -** Private version of List for internal System.Private.CoreLib use. This -** permits sharing more source between BCL and System.Private.CoreLib (as well as the -** fact that List is just a useful class in general.) -** -** This does not strive to implement the full api surface area -** (but any portion it does implement should match the real List's -** behavior.) -** -** This file is a subset of System.Collections\System\Collections\Generics\List.cs -** and should be kept in sync with that file. -** -===========================================================*/ - -using System; -using System.Diagnostics; - -namespace System.Collections.Generic -{ - // Implements a variable-size List that uses an array of objects to store the - // elements. A List has a capacity, which is the allocated length - // of the internal array. As elements are added to a List, the capacity - // of the List is automatically increased as required by reallocating the - // internal array. - // - // LowLevelList with no interface implementation minimizes both code and data size. - // Data size is smaller because there will be minimal virtual function table. - // Code size is smaller because only functions called will be in the binary. - [DebuggerDisplay("Count = {Count}")] - internal class LowLevelList - { - private const int _defaultCapacity = 4; - - protected T[] _items; - protected int _size; - protected int _version; - -#pragma warning disable CA1825 // avoid the extra generic instantiation for Array.Empty() - private static readonly T[] s_emptyArray = new T[0]; -#pragma warning restore CA1825 - - // Constructs a List. The list is initially empty and has a capacity - // of zero. Upon adding the first element to the list the capacity is - // increased to 4, and then increased in multiples of two as required. - public LowLevelList() - { - _items = s_emptyArray; - } - - // Constructs a List with a given initial capacity. The list is - // initially empty, but will have room for the given number of elements - // before any reallocations are required. - // - public LowLevelList(int capacity) - { - ArgumentOutOfRangeException.ThrowIfNegative(capacity); - - if (capacity == 0) - _items = s_emptyArray; - else - _items = new T[capacity]; - } - - // Constructs a List, copying the contents of the given collection. The - // size and capacity of the new list will both be equal to the size of the - // given collection. - // - public LowLevelList(IEnumerable collection) - { - ArgumentNullException.ThrowIfNull(collection); - - ICollection? c = collection as ICollection; - if (c != null) - { - int count = c.Count; - if (count == 0) - { - _items = s_emptyArray; - } - else - { - _items = new T[count]; - c.CopyTo(_items, 0); - _size = count; - } - } - else - { - _size = 0; - _items = s_emptyArray; - // This enumerable could be empty. Let Add allocate a new array, if needed. - // Note it will also go to _defaultCapacity first, not 1, then 2, etc. - - using (IEnumerator en = collection.GetEnumerator()) - { - while (en.MoveNext()) - { - Add(en.Current); - } - } - } - } - - // Gets and sets the capacity of this list. The capacity is the size of - // the internal array used to hold items. When set, the internal - // array of the list is reallocated to the given capacity. - // - public int Capacity - { - get - { - return _items.Length; - } - set - { - ArgumentOutOfRangeException.ThrowIfLessThan(value, _size); - - if (value != _items.Length) - { - if (value > 0) - { - T[] newItems = new T[value]; - Array.Copy(_items, 0, newItems, 0, _size); - _items = newItems; - } - else - { - _items = s_emptyArray; - } - } - } - } - - // Read-only property describing how many elements are in the List. - public int Count - { - get - { - return _size; - } - } - - // Sets or Gets the element at the given index. - // - public T this[int index] - { - get - { - // Following trick can reduce the range check by one - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)_size, nameof(index)); - return _items[index]; - } - - set - { - ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)_size, nameof(index)); - _items[index] = value; - _version++; - } - } - - - // Adds the given object to the end of this list. The size of the list is - // increased by one. If required, the capacity of the list is doubled - // before adding the new element. - // - public void Add(T item) - { - if (_size == _items.Length) EnsureCapacity(_size + 1); - _items[_size++] = item; - _version++; - } - - // Ensures that the capacity of this list is at least the given minimum - // value. If the current capacity of the list is less than min, the - // capacity is increased to twice the current capacity or to min, - // whichever is larger. - private void EnsureCapacity(int min) - { - if (_items.Length < min) - { - int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2; - // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. - // Note that this check works even when _items.Length overflowed thanks to the (uint) cast - //if ((uint)newCapacity > Array.MaxLength) newCapacity = Array.MaxLength; - if (newCapacity < min) newCapacity = min; - Capacity = newCapacity; - } - } - } -} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs deleted file mode 100644 index 1854e6ddeb210d..00000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs +++ /dev/null @@ -1,864 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Reflection.Runtime.TypeInfos; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; - -using Internal.Reflection.Augments; -using Internal.Reflection.Core.Execution; -using Internal.Runtime; - -using Debug = System.Diagnostics.Debug; - -namespace System -{ - internal sealed unsafe class RuntimeType : TypeInfo, ICloneable - { - private MethodTable* _pUnderlyingEEType; - private IntPtr _runtimeTypeInfoHandle; - - internal RuntimeType(MethodTable* pEEType) - { - _pUnderlyingEEType = pEEType; - } - - internal RuntimeType(RuntimeTypeInfo runtimeTypeInfo) - { - // This needs to be a strong handle to prevent the type from being collected and re-created that would end up leaking the handle. - _runtimeTypeInfoHandle = RuntimeImports.RhHandleAlloc(runtimeTypeInfo, GCHandleType.Normal); - } - - internal void DangerousSetUnderlyingEEType(MethodTable* pEEType) - { - Debug.Assert(_pUnderlyingEEType == null); - _pUnderlyingEEType = pEEType; - } - - internal void Free() - { - RuntimeImports.RhHandleFree(_runtimeTypeInfoHandle); - } - - internal MethodTable* ToMethodTableMayBeNull() => _pUnderlyingEEType; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal RuntimeTypeInfo GetRuntimeTypeInfo() - { - IntPtr handle = _runtimeTypeInfoHandle; - if (handle != default) - { - object? runtimeTypeInfo = RuntimeImports.RhHandleGet(handle); - if (runtimeTypeInfo != null) - { - return Unsafe.As(runtimeTypeInfo); - } - } - return InitializeRuntimeTypeInfoHandle(); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private RuntimeTypeInfo InitializeRuntimeTypeInfoHandle() - { - RuntimeTypeInfo runtimeTypeInfo = ExecutionDomain.GetRuntimeTypeInfo(_pUnderlyingEEType); - - // We assume that the RuntimeTypeInfo unifiers pick a winner when multiple threads - // race to create RuntimeTypeInfo. - - IntPtr handle = _runtimeTypeInfoHandle; - if (handle == default) - { - IntPtr tempHandle = RuntimeImports.RhHandleAlloc(runtimeTypeInfo, GCHandleType.Weak); - if (Interlocked.CompareExchange(ref _runtimeTypeInfoHandle, tempHandle, default) != default) - RuntimeImports.RhHandleFree(tempHandle); - } - else - { - RuntimeImports.RhHandleSet(handle, runtimeTypeInfo); - } - - return runtimeTypeInfo; - } - - public override string? GetEnumName(object value) - { - ArgumentNullException.ThrowIfNull(value); - - ulong rawValue; - if (!Enum.TryGetUnboxedValueOfEnumOrInteger(value, out rawValue)) - throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value)); - - // For desktop compatibility, do not bounce an incoming integer that's the wrong size. - // Do a value-preserving cast of both it and the enum values and do a 64-bit compare. - - if (!IsActualEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); - - return Enum.GetName(this, rawValue); - } - - public override string[] GetEnumNames() - { - if (!IsActualEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); - - string[] ret = Enum.GetNamesNoCopy(this); - - // Make a copy since we can't hand out the same array since users can modify them - return new ReadOnlySpan(ret).ToArray(); - } - - public override Type GetEnumUnderlyingType() - { - if (!IsActualEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); - - return Enum.InternalGetUnderlyingType(this); - } - - public override bool IsEnumDefined(object value) - { - ArgumentNullException.ThrowIfNull(value); - - if (!IsActualEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); - - if (value is string valueAsString) - { - EnumInfo enumInfo = Enum.GetEnumInfo(this); - foreach (string name in enumInfo.Names) - { - if (valueAsString == name) - return true; - } - return false; - } - else - { - ulong rawValue; - if (!Enum.TryGetUnboxedValueOfEnumOrInteger(value, out rawValue)) - { - if (Type.IsIntegerType(value.GetType())) - throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), Enum.InternalGetUnderlyingType(this))); - else - throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType); - } - - if (value is Enum) - { - if (value.GetMethodTable() != this.ToMethodTableMayBeNull()) - throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, value.GetType(), this)); - } - else - { - Type underlyingType = Enum.InternalGetUnderlyingType(this); - if (!(underlyingType.TypeHandle.ToMethodTable() == value.GetMethodTable())) - throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, value.GetType(), underlyingType)); - } - - return Enum.GetName(this, rawValue) != null; - } - } - - [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues overload instead.")] - public override Array GetEnumValues() - { - if (!IsActualEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); - - Array values = Enum.GetValuesAsUnderlyingTypeNoCopy(this); - int count = values.Length; - - // Without universal shared generics, chances are slim that we'll have the appropriate - // array type available. Offer an escape hatch that avoids a missing metadata exception - // at the cost of a small appcompat risk. - Array result = AppContext.TryGetSwitch("Switch.System.Enum.RelaxedGetValues", out bool isRelaxed) && isRelaxed ? - Array.CreateInstance(Enum.InternalGetUnderlyingType(this), count) : - Array.CreateInstance(this, count); - - Array.Copy(values, result, values.Length); - return result; - } - - public override Array GetEnumValuesAsUnderlyingType() - { - if (!IsActualEnum) - throw new ArgumentException(SR.Arg_MustBeEnum, "enumType"); - - return Enum.GetValuesAsUnderlyingType(this); - } - - public override int GetHashCode() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return ((nuint)pEEType).GetHashCode(); - return RuntimeHelpers.GetHashCode(this); - } - - public override RuntimeTypeHandle TypeHandle - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return new RuntimeTypeHandle(pEEType); - return GetRuntimeTypeInfo().TypeHandle; - } - } - - internal new unsafe bool IsInterface - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsInterface; - return GetRuntimeTypeInfo().IsInterface; - } - } - - protected override bool IsValueTypeImpl() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsValueType; - return GetRuntimeTypeInfo().IsValueType; - } - - internal bool IsActualValueType - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsValueType; - return GetRuntimeTypeInfo().IsActualValueType; - } - } - - public override unsafe bool IsEnum - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsEnum; - return GetRuntimeTypeInfo().IsEnum; - } - } - - internal unsafe bool IsActualEnum - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsEnum; - return GetRuntimeTypeInfo().IsActualEnum; - } - } - - protected override unsafe bool IsArrayImpl() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsArray; - return GetRuntimeTypeInfo().IsArray; - } - - protected override unsafe bool IsByRefImpl() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsByRef; - return GetRuntimeTypeInfo().IsByRef; - } - - protected override unsafe bool IsPointerImpl() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsPointer; - return GetRuntimeTypeInfo().IsPointer; - } - - protected override unsafe bool HasElementTypeImpl() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsParameterizedType; - return GetRuntimeTypeInfo().HasElementType; - } - - public override Type? GetElementType() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsParameterizedType ? GetTypeFromMethodTable(pEEType->RelatedParameterType) : null; - return GetRuntimeTypeInfo().GetElementType(); - } - - public override int GetArrayRank() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsArray ? pEEType->ArrayRank : throw new ArgumentException(SR.Argument_HasToBeArrayClass); - return GetRuntimeTypeInfo().GetArrayRank(); - } - - public override Type? BaseType - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - { - if (pEEType->IsCanonical) - { - MethodTable* pBaseType = pEEType->NonArrayBaseType; - return (pBaseType != null) ? GetTypeFromMethodTable(pBaseType) : null; - } - - if (pEEType->IsArray) - { - return typeof(Array); - } - - if (!pEEType->IsGenericTypeDefinition) - { - return null; - } - } - - return GetRuntimeTypeInfo().BaseType; - } - } - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] - public override Type[] GetInterfaces() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null && !pEEType->IsGenericTypeDefinition) - { - int count = pEEType->NumInterfaces; - if (count == 0) - return EmptyTypes; - - Type[] result = new Type[count]; - for (int i = 0; i < result.Length; i++) - { - result[i] = GetTypeFromMethodTable(pEEType->InterfaceMap[i]); - } - return result; - } - - return GetRuntimeTypeInfo().GetInterfaces(); - } - - public override bool IsTypeDefinition - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return (pEEType->IsCanonical && !pEEType->IsGeneric) || pEEType->IsGenericTypeDefinition; - return GetRuntimeTypeInfo().IsTypeDefinition; - } - } - - public override bool IsGenericType - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsGeneric || pEEType->IsGenericTypeDefinition; - return GetRuntimeTypeInfo().IsGenericType; - } - } - - public override bool IsGenericTypeDefinition - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsGenericTypeDefinition; - return GetRuntimeTypeInfo().IsGenericTypeDefinition; - } - } - - public override bool IsConstructedGenericType - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsGeneric; - return GetRuntimeTypeInfo().IsConstructedGenericType; - } - } - - public override Type GetGenericTypeDefinition() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - { - return pEEType->IsGeneric ? GetTypeFromMethodTable(pEEType->GenericDefinition) : - pEEType->IsGenericTypeDefinition ? this : - throw new InvalidOperationException(SR.InvalidOperation_NotGenericType); - } - return GetRuntimeTypeInfo().GetGenericTypeDefinition(); - } - - public override Type[] GenericTypeArguments - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - { - if (!pEEType->IsGeneric) - return EmptyTypes; - - MethodTableList genericArguments = pEEType->GenericArguments; - - Type[] result = new Type[pEEType->GenericArity]; - for (int i = 0; i < result.Length; i++) - { - result[i] = GetTypeFromMethodTable(genericArguments[i]); - } - return result; - } - - return GetRuntimeTypeInfo().GenericTypeArguments; - } - } - - public override Type[] GetGenericArguments() - { - if (IsConstructedGenericType) - return GenericTypeArguments; - if (IsGenericTypeDefinition) - return GenericTypeParameters; - return EmptyTypes; - } - - public override bool IsGenericParameter - { - get - { - if (_pUnderlyingEEType != null) - return false; - return GetRuntimeTypeInfo().IsGenericParameter; - } - } - - public override bool IsGenericTypeParameter - { - get - { - if (_pUnderlyingEEType != null) - return false; - return GetRuntimeTypeInfo().IsGenericTypeParameter; - } - } - - public override bool IsGenericMethodParameter - { - get - { - if (_pUnderlyingEEType != null) - return false; - return GetRuntimeTypeInfo().IsGenericMethodParameter; - } - } - - public override bool ContainsGenericParameters - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsGenericTypeDefinition; - return GetRuntimeTypeInfo().ContainsGenericParameters; - } - } - - protected override bool IsPrimitiveImpl() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsActualPrimitive; - return GetRuntimeTypeInfo().IsPrimitive; - } - - public override bool IsSZArray - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->ElementType == EETypeElementType.SzArray; - return GetRuntimeTypeInfo().IsSZArray; - } - } - - public override bool IsVariableBoundArray - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->ElementType == EETypeElementType.Array; - return GetRuntimeTypeInfo().IsVariableBoundArray; - } - } - - public override bool IsByRefLike - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsByRefLike; - return GetRuntimeTypeInfo().IsByRefLike; - } - } - - public override bool IsFunctionPointer - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsFunctionPointer; - return GetRuntimeTypeInfo().IsFunctionPointer; - } - } - - public override bool IsUnmanagedFunctionPointer - { - get - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - return pEEType->IsFunctionPointer && pEEType->IsUnmanagedFunctionPointer; - return GetRuntimeTypeInfo().IsUnmanagedFunctionPointer; - } - } - - public override Type[] GetFunctionPointerParameterTypes() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - { - if (!pEEType->IsFunctionPointer) - throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); - - uint count = pEEType->NumFunctionPointerParameters; - if (count == 0) - return EmptyTypes; - - MethodTableList parameterTypes = pEEType->FunctionPointerParameters; - - Type[] result = new Type[count]; - for (int i = 0; i < result.Length; i++) - { - result[i] = GetTypeFromMethodTable(parameterTypes[i]); - } - return result; - } - - return GetRuntimeTypeInfo().GetFunctionPointerParameterTypes(); - } - - public override Type GetFunctionPointerReturnType() - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType != null) - { - if (!pEEType->IsFunctionPointer) - throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); - - return GetTypeFromMethodTable(pEEType->FunctionPointerReturnType); - } - - return GetRuntimeTypeInfo().GetFunctionPointerReturnType(); - } - - public override bool IsAssignableFrom([NotNullWhen(true)] Type? c) - { - if (c == null) - return false; - - if (object.ReferenceEquals(c, this)) - return true; - - if (c.UnderlyingSystemType is not RuntimeType fromRuntimeType) - return false; // Desktop compat: If typeInfo is null, or implemented by a different Reflection implementation, return "false." - - if (fromRuntimeType._pUnderlyingEEType != null && _pUnderlyingEEType != null) - { - // If both types have type handles, let MRT handle this. It's not dependent on metadata. - if (RuntimeImports.AreTypesAssignable(fromRuntimeType._pUnderlyingEEType, _pUnderlyingEEType)) - return true; - - // Runtime IsAssignableFrom does not handle casts from generic type definitions: always returns false. For those, we fall through to the - // managed implementation. For everyone else, return "false". - // - // Runtime IsAssignableFrom does not handle pointer -> UIntPtr cast. - if (!fromRuntimeType._pUnderlyingEEType->IsGenericTypeDefinition || fromRuntimeType._pUnderlyingEEType->IsPointer) - return false; - } - - // If we got here, the types are open, or reduced away, or otherwise lacking in type handles. Perform the IsAssignability check using metadata. - return GetRuntimeTypeInfo().IsAssignableFrom(fromRuntimeType); - } - - public override bool IsInstanceOfType([NotNullWhen(true)] object? o) - { - MethodTable* pEEType = _pUnderlyingEEType; - if (pEEType == null || pEEType->IsGenericTypeDefinition) - return false; - if (pEEType->IsNullable) - pEEType = pEEType->NullableType; - return TypeCast.IsInstanceOfAny(pEEType, o) != null; - } - - // - // Methods that do not directly depend on _pUnderlyingEEType - // - - public override string ToString() - { - return GetRuntimeTypeInfo().ToString(); - } - - public override bool Equals(object? obj) => ReferenceEquals(obj, this); - - object ICloneable.Clone() => this; - - public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) - => typeInfo != null && IsAssignableFrom(typeInfo.AsType()); - - public override bool IsSecurityCritical => true; - public override bool IsSecuritySafeCritical => false; - public override bool IsSecurityTransparent => false; - - public override Type UnderlyingSystemType => this; - - public override bool IsCollectible => false; - - public override MemberTypes MemberType => GetRuntimeTypeInfo().MemberType; - - public override int MetadataToken => GetRuntimeTypeInfo().MetadataToken; - - public override Type? DeclaringType => GetRuntimeTypeInfo().DeclaringType; - public override Type? ReflectedType => DeclaringType; - - public override MethodBase? DeclaringMethod => GetRuntimeTypeInfo().DeclaringMethod; - - public override StructLayoutAttribute StructLayoutAttribute => GetRuntimeTypeInfo().StructLayoutAttribute; - - protected override bool IsCOMObjectImpl() => false; - - protected override TypeCode GetTypeCodeImpl() => ReflectionAugments.GetRuntimeTypeCode(this); - - protected override TypeAttributes GetAttributeFlagsImpl() => GetRuntimeTypeInfo().Attributes; - - public override Type[] GenericTypeParameters - => GetRuntimeTypeInfo().GenericTypeParameters; - - public override int GenericParameterPosition - => GetRuntimeTypeInfo().GenericParameterPosition; - public override GenericParameterAttributes GenericParameterAttributes - => GetRuntimeTypeInfo().GenericParameterAttributes; - public override Type[] GetGenericParameterConstraints() - => GetRuntimeTypeInfo().GetGenericParameterConstraints(); - - public override Type[] GetFunctionPointerCallingConventions() - { - if (!IsFunctionPointer) - throw new InvalidOperationException(SR.InvalidOperation_NotFunctionPointer); - - // Requires a modified type to return the modifiers. - return EmptyTypes; - } - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - protected override ConstructorInfo? GetConstructorImpl(BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[] types, ParameterModifier[]? modifiers) - => GetRuntimeTypeInfo().GetConstructorImpl(bindingAttr, binder, callConvention, types, modifiers); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetConstructors(bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] - public override EventInfo? GetEvent(string name, BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetEvent(name, bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] - public override EventInfo[] GetEvents(BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetEvents(bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] - public override FieldInfo? GetField(string name, BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetField(name, bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] - public override FieldInfo[] GetFields(BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetFields(bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] - protected override MethodInfo? GetMethodImpl(string name, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) - => GetRuntimeTypeInfo().GetMethodImpl(name, RuntimeTypeInfo.GenericParameterCountAny, bindingAttr, binder, callConvention, types, modifiers); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] - protected override MethodInfo? GetMethodImpl(string name, int genericParameterCount, BindingFlags bindingAttr, Binder? binder, CallingConventions callConvention, Type[]? types, ParameterModifier[]? modifiers) - => GetRuntimeTypeInfo().GetMethodImpl(name, genericParameterCount, bindingAttr, binder, callConvention, types, modifiers); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] - public override MethodInfo[] GetMethods(BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetMethods(bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] - public override Type? GetNestedType(string name, BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetNestedType(name, bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] - public override Type[] GetNestedTypes(BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetNestedTypes(bindingAttr); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] - protected override PropertyInfo? GetPropertyImpl(string name, BindingFlags bindingAttr, Binder? binder, Type? returnType, Type[]? types, ParameterModifier[]? modifiers) - => GetRuntimeTypeInfo().GetPropertyImpl(name, bindingAttr, binder, returnType, types, modifiers); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] - public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetProperties(bindingAttr); - - [DynamicallyAccessedMembers(GetAllMembers)] - public override MemberInfo[] GetMember(string name, BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetMember(name, bindingAttr); - - [DynamicallyAccessedMembers(GetAllMembers)] - public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetMember(name, type, bindingAttr); - - [DynamicallyAccessedMembers(GetAllMembers)] - public override MemberInfo[] GetMembers(BindingFlags bindingAttr) - => GetRuntimeTypeInfo().GetMembers(bindingAttr); - - public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) - => GetRuntimeTypeInfo().GetMemberWithSameMetadataDefinitionAs(member); - - [DynamicallyAccessedMembers(InvokeMemberMembers)] - public override object? InvokeMember(string name, BindingFlags invokeAttr, Binder? binder, object? target, object?[]? args, ParameterModifier[]? modifiers, CultureInfo? culture, string[]? namedParameters) - => GetRuntimeTypeInfo().InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters); - - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] - public override Type? GetInterface(string name, bool ignoreCase) - => GetRuntimeTypeInfo().GetInterface(name, ignoreCase); - - public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type interfaceType) - => GetRuntimeTypeInfo().GetInterfaceMap(interfaceType); - - [DynamicallyAccessedMembers( - DynamicallyAccessedMemberTypes.PublicFields - | DynamicallyAccessedMemberTypes.PublicMethods - | DynamicallyAccessedMemberTypes.PublicEvents - | DynamicallyAccessedMemberTypes.PublicProperties - | DynamicallyAccessedMemberTypes.PublicConstructors - | DynamicallyAccessedMemberTypes.PublicNestedTypes)] - public override MemberInfo[] GetDefaultMembers() - => GetRuntimeTypeInfo().GetDefaultMembers(); - - public override bool IsDefined(Type attributeType, bool inherit) - => GetRuntimeTypeInfo().IsDefined(attributeType, inherit); - - public override object[] GetCustomAttributes(bool inherit) - { - return GetRuntimeTypeInfo().GetCustomAttributes(inherit); - } - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - { - return GetRuntimeTypeInfo().GetCustomAttributes(attributeType, inherit); - } - - public override IEnumerable CustomAttributes - { - get - { - return GetRuntimeTypeInfo().CustomAttributes; - } - } - - public override IList GetCustomAttributesData() - { - return GetRuntimeTypeInfo().GetCustomAttributesData(); - } - - public override string Name - { - get - { - return GetRuntimeTypeInfo().Name; - } - } - - public override string? Namespace - { - get - { - return GetRuntimeTypeInfo().Namespace; - } - } - - public override string? AssemblyQualifiedName => GetRuntimeTypeInfo().AssemblyQualifiedName; - - public override string? FullName => GetRuntimeTypeInfo().FullName; - - public override Assembly Assembly - { - get - { - return GetRuntimeTypeInfo().Assembly; - } - } - - public override Module Module => GetRuntimeTypeInfo().Module; - - public override Guid GUID => GetRuntimeTypeInfo().GUID; - - public override bool HasSameMetadataDefinitionAs(MemberInfo other) => GetRuntimeTypeInfo().HasSameMetadataDefinitionAs(other); - - public override Type MakePointerType() - => GetRuntimeTypeInfo().MakePointerType(); - - public override Type MakeByRefType() - => GetRuntimeTypeInfo().MakeByRefType(); - - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] - public override Type MakeArrayType() - => GetRuntimeTypeInfo().MakeArrayType(); - - [RequiresDynamicCode("The code for an array of the specified type might not be available.")] - public override Type MakeArrayType(int rank) - => GetRuntimeTypeInfo().MakeArrayType(rank); - - [RequiresDynamicCode("The native code for this instantiation might not be available at runtime.")] - [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")] - public override Type MakeGenericType(params Type[] instantiation) - => GetRuntimeTypeInfo().MakeGenericType(instantiation); - } -} diff --git a/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs b/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs deleted file mode 100644 index 8cc99bc095026b..00000000000000 --- a/src/libraries/System.Data.OleDb/src/OleDbComWrappers.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.Data.Common; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// We need to target netstandard2.0, so keep using ref for MemoryMarshal.Write -// CS9191: The 'ref' modifier for argument 2 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. -#pragma warning disable CS9191 - -namespace System.Data.OleDb -{ - /// - /// The ComWrappers implementation for System.Data.OleDb's COM interop usages. - /// - /// Supports IErrorInfo COM interface. - /// - internal sealed unsafe class OleDbComWrappers : ComWrappers - { - private const int S_OK = (int)OleDbHResult.S_OK; - private static readonly Guid IID_IErrorInfo = new Guid(0x1CF2B120, 0x547D, 0x101B, 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19); - - internal static OleDbComWrappers Instance { get; } = new OleDbComWrappers(); - - private OleDbComWrappers() { } - - protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) - { - throw new NotImplementedException(); - } - - protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) - { - Debug.Assert(flags == CreateObjectFlags.UniqueInstance); - - Guid errorInfoIID = IID_IErrorInfo; -#pragma warning disable CS9191 // The 'ref' modifier for argument 1 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. - int hr = Marshal.QueryInterface(externalComObject, ref errorInfoIID, out IntPtr comObject); -#pragma warning restore CS9191 - if (hr == S_OK) - { - return new ErrorInfoWrapper(comObject); - } - - throw new NotImplementedException(); - } - - protected override void ReleaseObjects(IEnumerable objects) - { - throw new NotImplementedException(); - } - - // Doc and type layout: https://learn.microsoft.com/windows/win32/api/oaidl/nn-oaidl-ierrorinfo - private sealed class ErrorInfoWrapper : UnsafeNativeMethods.IErrorInfo, IDisposable - { - private readonly IntPtr _wrappedInstance; - - public ErrorInfoWrapper(IntPtr wrappedInstance) - { - _wrappedInstance = wrappedInstance; - } - - public void Dispose() - { - Marshal.Release(_wrappedInstance); - } - - [Obsolete("not used", true)] - void UnsafeNativeMethods.IErrorInfo.GetGUID(/*deleted parameter signature*/) - { - throw new NotImplementedException(); - } - - public unsafe System.Data.OleDb.OleDbHResult GetSource(out string? source) - { - IntPtr pSource = IntPtr.Zero; - int errorCode = ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 4 /* IErrorInfo.GetSource slot */))) - (_wrappedInstance, &pSource); - if (pSource == IntPtr.Zero || errorCode < 0) - { - source = null; - } - else - { - source = Marshal.PtrToStringBSTR(pSource); - } - - if (pSource != IntPtr.Zero) - { - Marshal.FreeBSTR(pSource); - } - - return (System.Data.OleDb.OleDbHResult)errorCode; - } - - public unsafe System.Data.OleDb.OleDbHResult GetDescription(out string? description) - { - IntPtr pDescription = IntPtr.Zero; - int errorCode = ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 5 /* IErrorInfo.GetDescription slot */))) - (_wrappedInstance, &pDescription); - if (pDescription == IntPtr.Zero || errorCode < 0) - { - description = null; - } - else - { - description = Marshal.PtrToStringBSTR(pDescription); - } - - if (pDescription != IntPtr.Zero) - { - Marshal.FreeBSTR(pDescription); - } - - return (System.Data.OleDb.OleDbHResult)errorCode; - } - } - - } -} diff --git a/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs b/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs deleted file mode 100644 index 13e6642ee1fc02..00000000000000 --- a/src/libraries/System.Linq/src/System/Linq/Skip.SizeOpt.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; - -namespace System.Linq -{ - public static partial class Enumerable - { - private static IEnumerable SizeOptimizedSkipIterator(IEnumerable source, int count) - { - using IEnumerator e = source.GetEnumerator(); - while (count > 0 && e.MoveNext()) count--; - if (count <= 0) - { - while (e.MoveNext()) yield return e.Current; - } - } - } -} diff --git a/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs b/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs deleted file mode 100644 index 6f2bd0d9b0fa6b..00000000000000 --- a/src/libraries/System.Linq/src/System/Linq/Take.SizeOpt.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics; - -namespace System.Linq -{ - public static partial class Enumerable - { - private static IEnumerable SizeOptimizedTakeIterator(IEnumerable source, int count) - { - Debug.Assert(count > 0); - - foreach (TSource element in source) - { - yield return element; - if (--count == 0) break; - } - } - - private static IEnumerable SizeOptimizedTakeRangeIterator(IEnumerable source, int startIndex, int endIndex) - { - Debug.Assert(source is not null); - Debug.Assert(startIndex >= 0 && startIndex < endIndex); - - using IEnumerator e = source.GetEnumerator(); - - int index = 0; - while (index < startIndex && e.MoveNext()) - { - ++index; - } - - if (index < startIndex) - { - yield break; - } - - while (index < endIndex && e.MoveNext()) - { - yield return e.Current; - ++index; - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs index 162d8e5a89c9c6..fbeb95a922689b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelLifoSemaphore.Unix.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace System.Threading @@ -11,8 +12,6 @@ namespace System.Threading /// internal sealed partial class LowLevelLifoSemaphore : IDisposable { - // Declared nullable even though it is initialized in Create - // as Roslyn doesn't see that it's set in Create and Create is called from all constructors. private WaitSubsystem.WaitableObject _semaphore; [MemberNotNull(nameof(_semaphore))] diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs deleted file mode 100644 index 53d6f7f164275a..00000000000000 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserOptions.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Reflection.Metadata -{ - public sealed class TypeNameParseOptions - { - private int _maxNodes = 20; - - /// - /// Limits the maximum value of node count that parser can handle. - /// - /// - /// - /// Setting this to a large value can render susceptible to Denial of Service - /// attacks when parsing or handling malicious input. - /// - /// The default value is 20. - /// - public int MaxNodes - { - get => _maxNodes; - set - { -#if NET8_0_OR_GREATER - ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, 0, nameof(value)); -#else - if (value <= 0) - { - throw new ArgumentOutOfRangeException(paramName: nameof(value)); - } -#endif - - _maxNodes = value; - } - } - } -} diff --git a/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs b/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs deleted file mode 100644 index ab5df917fdb5fd..00000000000000 --- a/src/libraries/System.Resources.Extensions/tests/BinaryFormatTests/Legacy/TheoryDataExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Xunit; - -internal static class TheoryDataExtensions -{ - /// - /// Converts an IEnumerable into an Xunit theory compatible enumerable. - /// - public static TheoryData ToTheoryData(this IEnumerable data) - { - TheoryData theoryData = []; - foreach (var item in data) - { - theoryData.Add(item); - } - - return theoryData; - } - - /// - /// Converts an IEnumerable into an Xunit theory compatible enumerable. - /// - public static TheoryData ToTheoryData(this IEnumerable<(T1, T2)> data) - { - TheoryData theoryData = []; - foreach (var item in data) - { - theoryData.Add(item.Item1, item.Item2); - } - - return theoryData; - } - - /// - /// Converts an IEnumerable into an Xunit theory compatible enumerable. - /// - public static TheoryData ToTheoryData(this IEnumerable<(T1, T2, T3)> data) - { - TheoryData theoryData = []; - foreach (var item in data) - { - theoryData.Add(item.Item1, item.Item2, item.Item3); - } - - return theoryData; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs deleted file mode 100644 index 658606d84ee3cf..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IArrayOfStatelessElements.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("F4963CBF-10AF-460B-8495-107782187705")] - internal partial interface IArrayOfStatelessElements - { - void Method([MarshalUsing(CountElementName = nameof(size))] StatelessType[] param, int size); - void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessType[] param, int size); - void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType[] param, int size); - void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType[] param, int size); - void MethodContentsIn([MarshalUsing(CountElementName = nameof(size))][In] StatelessType[] param, int size); - void MethodContentsOut([MarshalUsing(CountElementName = nameof(size))][Out] StatelessType[] param, int size); - void MethodContentsInOut([MarshalUsing(CountElementName = nameof(size))][In, Out] StatelessType[] param, int size); - } - - [GeneratedComClass] - internal partial class ArrayOfStatelessElements : IArrayOfStatelessElements - { - public void Method(StatelessType[] param, int size) - { - } - public void MethodContentsIn(StatelessType[] param, int size) - { - // We should be able to modify the contents and the caller shouldn't see it - for (int i = 0; i < size; i++) - { - param[i] = new StatelessType() { I = param[i].I * 2 }; - } - } - public void MethodContentsInOut(StatelessType[] param, int size) - { - for (int i = 0; i < size; i++) - { - param[i] = new StatelessType() { I = param[i].I * 2 }; - } - } - public void MethodContentsOut(StatelessType[] param, int size) - { - for (int i = 0; i < size; i++) - { - param[i] = new StatelessType() { I = i }; - } - } - public void MethodIn(in StatelessType[] param, int size) - { - // We should be able to modify the contents and the caller shouldn't see it - for (int i = 0; i < size; i++) - { - param[i] = new StatelessType() { I = param[i].I * 2 }; - } - } - public void MethodOut(out StatelessType[] param, int size) - { - param = new StatelessType[size]; - for (int i = 0; i < size; i++) - { - param[i] = new StatelessType() { I = i }; - } - } - public void MethodRef(ref StatelessType[] param, int size) - { - for (int i = 0; i < size; i++) - { - param[i] = new StatelessType() { I = param[i].I * 2 }; - } - } - } - - [GeneratedComClass] - internal partial class ArrayOfStatelessElementsThrows : IArrayOfStatelessElements - { - public void Method(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - public void MethodContentsIn(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - public void MethodContentsInOut(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - public void MethodContentsOut(StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - public void MethodIn(in StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - public void MethodOut(out StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - public void MethodRef(ref StatelessType[] param, int size) => throw new ManagedComMethodFailureException(); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs deleted file mode 100644 index 4e71832850fefc..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IBool.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("5A9D3ED6-CC17-4FB9-8F82-0070489B7213")] - internal partial interface IBool - { - [return: MarshalAs(UnmanagedType.I1)] - bool Get(); - void Set([MarshalAs(UnmanagedType.I1)] bool value); - } - - [GeneratedComClass] - internal partial class IBoolImpl : IBool - { - bool _data; - public bool Get() => _data; - public void Set(bool value) => _data = value; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs deleted file mode 100644 index 7a1c97a7b37302..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ICustomStringMarshallingUtf16.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [Guid(IID)] - [GeneratedComInterface(StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Utf16StringMarshaller))] - internal partial interface ICustomStringMarshallingUtf16 - { - public string GetString(); - - public void SetString(string value); - - public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs deleted file mode 100644 index f6cd4ff8afc55b..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IDerived.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid(IID)] - internal partial interface IDerived : IGetAndSetInt - { - void SetName([MarshalUsing(typeof(Utf16StringMarshaller))] string name); - - [return: MarshalUsing(typeof(Utf16StringMarshaller))] - string GetName(); - - internal new const string IID = "7F0DB364-3C04-4487-9193-4BB05DC7B654"; - } - - [GeneratedComClass] - internal partial class Derived : GetAndSetInt, IDerived - { - string _data = "hello"; - public string GetName() => _data; - public void SetName(string name) => _data = name; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs deleted file mode 100644 index 86af99aa4ff726..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEmpty.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid(IID)] - internal partial interface IEmpty - { - public const string IID = "95D19F50-F2D8-4E61-884B-0A9162EA4646"; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs deleted file mode 100644 index db5249a950bcab..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IEnumUnknown.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("00000100-0000-0000-C000-000000000046")] - internal partial interface IEnumUnknown - { - void Next(uint celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface)] object[] rgelt, out uint pceltFetched); - void Skip(uint celt); - void Reset(); - void Clone(out IEnumUnknown ppenum); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs deleted file mode 100644 index f63cbbb91ab1de..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IFloat.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("9FA4A8A9-2D8F-48A8-B6FB-B44B5F1B9FB6")] - internal partial interface IFloat - { - float Get(); - void Set(float value); - } - - [GeneratedComClass] - internal partial class IFloatImpl : IFloat - { - float _data; - public float Get() => _data; - public void Set(float value) => _data = value; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs deleted file mode 100644 index 3f1b18f34e9beb..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetAndSetInt.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid(IID)] - internal partial interface IGetAndSetInt - { - int GetInt(); - - public void SetInt(int x); - - public const string IID = "2c3f9903-b586-46b1-881b-adfce9af47b1"; - } - [GeneratedComClass] - internal partial class GetAndSetInt : IGetAndSetInt - { - int _data = 0; - public int GetInt() => _data; - public void SetInt(int x) => _data = x; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs deleted file mode 100644 index f2283708678df1..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IGetIntArray.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid(IID)] - internal partial interface IGetIntArray - { - [return: MarshalUsing(ConstantElementCount = 10)] - int[] GetInts(); - - public const string IID = "7D802A0A-630A-4C8E-A21F-771CC9031FB9"; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs deleted file mode 100644 index 9d2fc102ba5df4..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IHide.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("023EA72A-ECAA-4B65-9D96-2122CFADE16C")] - internal partial interface IHide - { - int SameMethod(); - int DifferentMethod(); - } - - [GeneratedComInterface] - [Guid("5293B3B1-4994-425C-803E-A21A5011E077")] - internal partial interface IHide2 : IHide - { - new int SameMethod(); - int DifferentMethod2(); - } - - internal interface UnrelatedInterfaceWithSameMethod - { - int SameMethod(); - int DifferentMethod3(); - } - - [GeneratedComInterface] - [Guid("5DD35432-4987-488D-94F1-7682D7E4405C")] - internal partial interface IHide3 : IHide2, UnrelatedInterfaceWithSameMethod - { - new int SameMethod(); - new int DifferentMethod3(); - } - - [GeneratedComClass] - [Guid("2D36BD6D-C80E-4F00-86E9-8D1B4A0CB59A")] - /// - /// Implements IHides3 and returns the expected VTable index for each method. - /// - internal partial class HideBaseMethods : IHide3 - { - int IHide.SameMethod() => 3; - int IHide.DifferentMethod() => 4; - int IHide2.SameMethod() => 5; - int IHide2.DifferentMethod2() => 6; - int IHide3.SameMethod() => 7; - int IHide3.DifferentMethod3() => 8; - int UnrelatedInterfaceWithSameMethod.SameMethod() => -1; - int UnrelatedInterfaceWithSameMethod.DifferentMethod3() => -1; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs deleted file mode 100644 index e50344f3a268fd..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInt.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("EE6D1F2A-3418-4317-A87C-35488F6546AB")] - internal partial interface IInt - { - public int Get(); - public void Set(int value); - public void SwapRef(ref int value); - public void GetOut(out int value); - public void SetIn(in int value); - } - - [GeneratedComClass] - internal partial class IIntImpl : IInt - { - int _data; - - public int Get() => _data; - - public void Set(int value) => _data = value; - - public void SetIn(in int value) - { - _data = value; - } - - public void GetOut(out int value) - { - value = _data; - } - - public void SwapRef(ref int value) - { - var tmp = _data; - _data = value; - value = tmp; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs deleted file mode 100644 index fbc68dcf4dbca0..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IIntArray.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] - internal partial interface IIntArray - { - [return: MarshalUsing(CountElementName = nameof(size))] - int[] GetReturn(out int size); - int GetOut([MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] out int[] array); - void SetContents([MarshalUsing(CountElementName = nameof(size))] int[] array, int size); - void FillAscending([Out][MarshalUsing(CountElementName = nameof(size))] int[] array, int size); - void Double([In, Out][MarshalUsing(CountElementName = nameof(size))] int[] array, int size); - void PassIn([MarshalUsing(CountElementName = nameof(size))] in int[] array, int size); - void SwapArray([MarshalUsing(CountElementName = nameof(size))] ref int[] array, int size); - } - - [GeneratedComClass] - internal partial class IIntArrayImpl : IIntArray - { - int[] _data; - public int[] GetReturn(out int size) - { - size = _data.Length; - return _data; - } - public int GetOut(out int[] array) - { - array = _data; - return array.Length; - } - public void SetContents(int[] array, int size) - { - _data = new int[size]; - array.CopyTo(_data, 0); - } - - public void FillAscending(int[] array, int size) - { - for (int i = 0; i < size; i++) - { - array[i] = i; - } - } - public void Double(int[] array, int size) - { - for (int i = 0; i < size; i++) - { - array[i] = array[i] * 2; - } - - } - - public void PassIn([MarshalUsing(CountElementName = "size")] in int[] array, int size) - { - _data = new int[size]; - array.CopyTo(_data, 0); - } - public void SwapArray([MarshalUsing(CountElementName = "size")] ref int[] array, int size) - { - var temp = _data; - _data = array; - array = temp; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs deleted file mode 100644 index 95c6a13335b44d..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IInterface.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("A4857398-06FB-4A6E-81DB-35461BE999C5")] - internal partial interface IInterface - { - public IInt Get(); - public void SetInt(IInt value); - public void SwapRef(ref IInt value); - public void GetOut(out IInt value); - public void InInt(in IInt value); - } - - [GeneratedComClass] - internal partial class IInterfaceImpl : IInterface - { - IInt _data = new IIntImpl(); - - IInt IInterface.Get() => _data; - - void IInterface.InInt(in IInt value) - { - var i = value.Get(); - } - - void IInterface.GetOut(out IInt value) - { - value = _data; - } - - void IInterface.SwapRef(ref IInt value) - { - var tmp = _data; - _data = value; - value = tmp; - } - - void IInterface.SetInt(IInt value) - { - int x = value.Get(); - value.Set(x); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs deleted file mode 100644 index c1153e377fc656..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IJaggedIntArray.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] - internal partial interface IJaggedIntArray - { - [return: MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - int[][] Get( - [MarshalUsing(CountElementName = nameof(length))] - out int[] widths, - out int length); - - int Get2( - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - out int[][] array, - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] - out int[] widths); - - void Set( - [MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths))] - int[][] array, - [MarshalUsing(CountElementName = nameof(length))] - int[] widths, - int length); - } - - [GeneratedComClass] - internal partial class IJaggedIntArrayImpl : IJaggedIntArray - { - int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; - int[] _widths = new int[] { 3, 2, 4 }; - public int[][] Get(out int[] widths, out int length) - { - widths = _widths; - length = _data.Length; - return _data; - } - public int Get2(out int[][] array, out int[] widths) - { - array = _data; - widths = _widths; - return array.Length; - } - public void Set(int[][] array, int[] widths, int length) - { - _data = array; - _widths = widths; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs deleted file mode 100644 index e29b21b33a54a0..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IPointProvider.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Drawing; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("E4461914-4202-479F-8427-620E915F84B9")] - internal partial interface IPointProvider - { - [PreserveSig] - Point GetPoint(); - - [PreserveSig] - [return:MarshalAs(UnmanagedType.Error)] - HResult SetPoint(Point point); - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HResult - { - public int Value; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs deleted file mode 100644 index 3a86b74a70b5c2..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IRefStrings.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(StringMarshalling = System.Runtime.InteropServices.StringMarshalling.Utf8)] - [Guid(IID)] - internal partial interface IRefStrings - { - public const string IID = "5146B7DB-0588-469B-B8E5-B38090A2FC15"; - void RefString(ref string value); - void InString(in string value); - void OutString(out string value); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs deleted file mode 100644 index 50a25b520939fb..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISafeHandles.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; -using Microsoft.Win32.SafeHandles; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] - internal partial interface ISafeFileHandle - { - void Method(SafeFileHandle p); - void MethodIn(in SafeFileHandle p); - void MethodOut(out SafeFileHandle p); - void MethodRef(ref SafeFileHandle p); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs deleted file mode 100644 index 89e5e6a31e5bbb..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulAllShapes.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] - internal partial interface IStatefulAllShapes - { - void Method(StatefulAllShapesType param); - void MethodIn(in StatefulAllShapesType param); - void MethodOut(out StatefulAllShapesType param); - void MethodRef(ref StatefulAllShapesType param); - StatefulAllShapesType Return(); - [PreserveSig] - StatefulAllShapesType ReturnPreserveSig(); - } - - [NativeMarshalling(typeof(AllStatefulMarshallerShapes))] - internal class StatefulAllShapesType - { - } - - internal unsafe struct StatefulAllShapesNative - { - } - - [CustomMarshaller(typeof(StatefulAllShapesType), MarshalMode.Default, typeof(AllStatefulMarshallerShapes))] - internal unsafe ref struct AllStatefulMarshallerShapes - { - public static ref nint GetPinnableReference(StatefulAllShapesType managed) => throw new NotImplementedException(); - public ref nint GetPinnableReference() => throw new NotImplementedException("This is not currently used anywhere"); - public static int BufferSize => sizeof(StatefulAllShapesNative); - public void FromManaged(StatefulAllShapesType managed, Span buffer) => throw new NotImplementedException(); - public void FromManaged(StatefulAllShapesType managed) => throw new NotImplementedException(); - public StatefulAllShapesNative* ToUnmanaged() => throw new NotImplementedException(); - public void FromUnmanaged(StatefulAllShapesNative* unmanaged) => throw new NotImplementedException(); - public StatefulAllShapesType ToManaged() => throw new NotImplementedException(); - public StatefulAllShapesType ToManagedFinally() => throw new NotImplementedException(); - public void Free() => throw new NotImplementedException(); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs deleted file mode 100644 index a585a5456b4039..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCallerAllocatedBuffer.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] - internal partial interface IStatefulCallerAllocatedBufferMarshalling - { - void Method(StatefulCallerAllocatedBufferType param); - void MethodIn(in StatefulCallerAllocatedBufferType param); - void MethodOut(out StatefulCallerAllocatedBufferType param); - void MethodRef(ref StatefulCallerAllocatedBufferType param); - StatefulCallerAllocatedBufferType Return(); - [PreserveSig] - StatefulCallerAllocatedBufferType ReturnPreserveSig(); - } - - [NativeMarshalling(typeof(StatefulCallerAllocatedBufferTypeMarshaller))] - internal class StatefulCallerAllocatedBufferType - { - } - - [CustomMarshaller(typeof(StatefulCallerAllocatedBufferType), MarshalMode.Default, typeof(StatefulCallerAllocatedBufferTypeMarshaller))] - internal struct StatefulCallerAllocatedBufferTypeMarshaller - { - public static int BufferSize => 64; - - public void FromManaged(StatefulCallerAllocatedBufferType managed, Span buffer) - { - throw new NotImplementedException(); - } - - public nint ToUnmanaged() - { - throw new NotImplementedException(); - } - - public void FromUnmanaged(nint unmanaged) - { - throw new NotImplementedException(); - } - - public StatefulCallerAllocatedBufferType ToManaged() - { - throw new NotImplementedException(); - } - - public void Free() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs deleted file mode 100644 index 481ca0e78296db..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionAllShapes.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] - internal partial interface IStatefulCollectionAllShapes - { - void Method([MarshalUsing(CountElementName = nameof(size))] StatefulCollectionAllShapes param, int size); - void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatefulCollectionAllShapes param, int size); - void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatefulCollectionAllShapes param, out int size); - void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatefulCollectionAllShapes param, int size); - [return: MarshalUsing(CountElementName = nameof(size))] - StatefulCollectionAllShapes Return(out int size); - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatefulCollectionAllShapes ReturnPreserveSig(out int size); - } - - [NativeMarshalling(typeof(StatefulCollectionAllShapesMarshaller<,>))] - internal class StatefulCollectionAllShapes - { - } - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatefulCollectionAllShapes<>), MarshalMode.Default, typeof(StatefulCollectionAllShapesMarshaller<,>))] - internal unsafe struct StatefulCollectionAllShapesMarshaller where TUnmanagedElement : unmanaged - { - public StatefulCollectionAllShapesMarshaller() { } - public void OnInvoked() { } - - // ManagedToUnmanaged - public void FromManaged(StatefulCollectionAllShapes collection) => throw new NotImplementedException(); - - public ReadOnlySpan GetManagedValuesSource() => throw new NotImplementedException(); - - public Span GetUnmanagedValuesDestination() => throw new NotImplementedException(); - - public ref nint GetPinnableReference() => throw new NotImplementedException(); - - public TUnmanagedElement* ToUnmanaged() => throw new NotImplementedException(); - - public static ref TUnmanagedElement* GetPinnableReference(StatefulCollectionAllShapes collection) => throw new NotImplementedException(); - - - // ManagedToUnmanaged with Caller Allocated Buffer - public static int BufferSize { get; } - - public void FromManaged(StatefulCollectionAllShapes collection, Span buffer) => throw new NotImplementedException(); - - - // UnmanagedToManaged - public void FromUnmanaged(TUnmanagedElement* value) => throw new NotImplementedException(); // Should not throw exceptions. - - public ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw new NotImplementedException(); // Can throw exceptions. - - public Span GetManagedValuesDestination(int numElements) => throw new NotImplementedException(); // Can throw exceptions. - - public StatefulCollectionAllShapes ToManaged() => throw new NotImplementedException(); // Can throw exceptions - - public void Free() { } - - - // UnmanagedToManaged with guaranteed unmarshalling - public StatefulCollectionAllShapes ToManagedFinally() => throw new NotImplementedException(); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs deleted file mode 100644 index e4df4476cfb7cc..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionBlittableElement.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] - partial interface IStatefulCollectionBlittableElement - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatefulCollection p, - int size); - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, - in int size); - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, - int size); - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, - out int size); - [return: MarshalUsing(CountElementName = nameof(size))] - StatefulCollection Return(int size); - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatefulCollection ReturnPreserveSig(int size); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs deleted file mode 100644 index f417e7bc314cca..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionPinnableReference.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(), Guid("0A52BA7C-E08B-42A4-A1F4-1A2BF2C07E60")] - internal partial interface IStatefulCollectionPinnableReference - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatefulCollectionPinnableReference p, - int size); - - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatefulCollectionPinnableReference pIn, - in int size); - - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollectionPinnableReference pRef, - int size); - - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatefulCollectionPinnableReference pOut, - out int size); - - [return: MarshalUsing(CountElementName = nameof(size))] - StatefulCollectionPinnableReference Return(int size); - } - - [NativeMarshalling(typeof(StatefulCollectionPinnableReferenceMarshaller<,>))] - internal class StatefulCollectionPinnableReference - { - } - - internal struct StatefulCollectionPinnableReferenceNative - { - public static unsafe explicit operator void*(StatefulCollectionPinnableReferenceNative _) => throw new NotImplementedException(); - public static unsafe explicit operator StatefulCollectionPinnableReferenceNative(void* _) => throw new NotImplementedException(); - } - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedRef, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedIn, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatefulCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedOut, typeof(StatefulCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] - internal static class StatefulCollectionPinnableReferenceMarshaller where TUnmanaged : unmanaged - { - public struct Bidirectional - { - public void FromManaged(StatefulCollectionPinnableReference managed) => throw new NotImplementedException(); - public StatefulCollectionPinnableReferenceNative ToUnmanaged() => throw new NotImplementedException(); - public ReadOnlySpan GetManagedValuesSource() => throw new NotImplementedException(); - public Span GetUnmanagedValuesDestination() => throw new NotImplementedException(); - - public void FromUnmanaged(StatefulCollectionPinnableReferenceNative unmanaged) => throw new NotImplementedException(); - public StatefulCollectionPinnableReference ToManaged() => throw new NotImplementedException(); - public ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw new NotImplementedException(); - public Span GetManagedValuesDestination(int numElements) => throw new NotImplementedException(); - - public void Free() => throw new NotImplementedException(); - } - - public struct UnmanagedToManaged - { - public void Free() => throw new NotImplementedException(); - public void FromUnmanaged(StatefulCollectionPinnableReferenceNative unmanaged) => throw new NotImplementedException(); - public StatefulCollectionPinnableReference ToManaged() => throw new NotImplementedException(); - public ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw new NotImplementedException(); - public Span GetManagedValuesDestination(int numElements) => throw new NotImplementedException(); - } - - public struct ManagedToUnmanaged - { - public void FromManaged(StatefulCollectionPinnableReference managed) => throw new NotImplementedException(); - public StatefulCollectionPinnableReferenceNative ToUnmanaged() => throw new NotImplementedException(); - public ReadOnlySpan GetManagedValuesSource() => throw new NotImplementedException(); - public Span GetUnmanagedValuesDestination() => throw new NotImplementedException(); - public void Free() => throw new NotImplementedException(); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs deleted file mode 100644 index d31c70856b6ebc..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulCollectionStatelessElement.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] - partial interface IStatefulCollectionStatelessElement - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatefulCollection p, - int size); - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatefulCollection pIn, - in int size); - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatefulCollection pRef, - int size); - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatefulCollection pOut, - out int size); - [return: MarshalUsing(CountElementName = nameof(size))] - StatefulCollection Return(int size); - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatefulCollection ReturnPreserveSig(int size); - } - - [NativeMarshalling(typeof(StatefulCollectionMarshaller<,>))] - internal class StatefulCollection - { - } - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatefulCollection<>), MarshalMode.Default, typeof(StatefulCollectionMarshaller<,>.Default))] - static unsafe class StatefulCollectionMarshaller where TUnmanagedElement : unmanaged - { - public ref struct Default - { - public byte* ToUnmanaged() => throw null; - public System.ReadOnlySpan GetManagedValuesSource() => throw null; - public System.Span GetUnmanagedValuesDestination() => throw null; - - public void FromUnmanaged(byte* value) => throw null; - public System.Span GetManagedValuesDestination(int numElements) => throw null; - public System.ReadOnlySpan GetUnmanagedValuesSource(int numElements) => throw null; - - public void Free() => throw null; - - public void FromManaged(StatefulCollection managed) => throw null; - - public StatefulCollection ToManaged() => throw null; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs deleted file mode 100644 index 3c9951c03fafe8..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulFinallyMarshalling.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] - internal partial interface IStatefulFinallyMarshalling - { - void Method(StatefulFinallyType param); - void MethodIn(in StatefulFinallyType param); - void MethodOut(out StatefulFinallyType param); - void MethodRef(ref StatefulFinallyType param); - StatefulFinallyType Return(); - [PreserveSig] - StatefulFinallyType ReturnPreserveSig(); - } - - [GeneratedComClass] - internal partial class StatefulFinallyMarshalling : IStatefulFinallyMarshalling - { - public void Method(StatefulFinallyType param) - { - _ = param.i; - } - public void MethodIn(in StatefulFinallyType param) - { - _ = param.i; - } - public void MethodOut(out StatefulFinallyType param) - { - param = new StatefulFinallyType() { i = 42 }; - } - public void MethodRef(ref StatefulFinallyType param) - { - _ = param.i; - param = new StatefulFinallyType() { i = 99 }; - } - public StatefulFinallyType Return() - => new StatefulFinallyType() { i = 8 }; - public StatefulFinallyType ReturnPreserveSig() - => new StatefulFinallyType() { i = 3 }; - } - - [NativeMarshalling(typeof(StatefulFinallyTypeMarshaller))] - internal class StatefulFinallyType - { - public int i; - } - - internal struct StatefulFinallyNative - { - public int i; - } - - [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatefulFinallyType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] - internal struct StatefulFinallyTypeMarshaller - { - internal struct Bidirectional - { - int managed_i; - int unmanaged_i; - - public void FromManaged(StatefulFinallyType managed) - { - managed_i = managed.i; - } - - public StatefulFinallyNative ToUnmanaged() - { - return new StatefulFinallyNative() { i = this.managed_i }; - } - - public void FromUnmanaged(StatefulFinallyNative unmanaged) - { - unmanaged_i = unmanaged.i; - } - - public StatefulFinallyType ToManagedFinally() - { - return new StatefulFinallyType() { i = unmanaged_i }; - } - - public void Free() { } - - public void OnInvoked() { } - } - - internal struct ManagedToUnmanaged - { - int managed_i; - - public void FromManaged(StatefulFinallyType managed) - { - managed_i = managed.i; - } - - public StatefulFinallyNative ToUnmanaged() - { - return new StatefulFinallyNative() { i = this.managed_i }; - } - - public void Free() { } - - public void OnInvoked() { } - } - - internal struct UnmanagedToManaged - { - int unmanaged_i; - - public void FromUnmanaged(StatefulFinallyNative unmanaged) - { - unmanaged_i = unmanaged.i; - } - public StatefulFinallyType ToManagedFinally() - { - return new StatefulFinallyType() { i = unmanaged_i }; - } - - public void Free() { } - - public void OnInvoked() { } - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs deleted file mode 100644 index 47b6cc916c3384..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulMarshalling.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] - internal partial interface IStatefulMarshalling - { - void Method(StatefulType param); - void MethodIn(in StatefulType param); - void MethodOut(out StatefulType param); - void MethodRef(ref StatefulType param); - StatefulType Return(); - [PreserveSig] - StatefulType ReturnPreserveSig(); - } - - [GeneratedComClass] - internal partial class StatefulMarshalling : IStatefulMarshalling - { - public void Method(StatefulType param) => param.i++; - public void MethodIn(in StatefulType param) => param.i++; - public void MethodOut(out StatefulType param) => param = new StatefulType() { i = 1 }; - public void MethodRef(ref StatefulType param) { } - public StatefulType Return() => new StatefulType() { i = 1 }; - public StatefulType ReturnPreserveSig() => new StatefulType() { i = 1 }; - } - - [NativeMarshalling(typeof(StatefulTypeMarshaller))] - internal class StatefulType - { - public int i; - } - - internal struct StatefulNative - { - public int i; - } - - [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatefulType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatefulType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] - internal struct StatefulTypeMarshaller - { - public static int FreeCount => Bidirectional.FreeCount + ManagedToUnmanaged.FreeCount + UnmanagedToManaged.FreeCount; - internal struct Bidirectional - { - public static int FreeCount { get; private set; } - StatefulType? _managed; - bool _hasManaged; - StatefulNative _unmanaged; - bool _hasUnmanaged; - - public void FromManaged(StatefulType managed) - { - _hasManaged = true; - _managed = managed; - } - - public StatefulNative ToUnmanaged() - { - if (!_hasManaged) throw new InvalidOperationException(); - return new StatefulNative() { i = _managed.i }; - } - - public void FromUnmanaged(StatefulNative unmanaged) - { - _hasUnmanaged = true; - _unmanaged = unmanaged; - } - - public StatefulType ToManaged() - { - if (!_hasUnmanaged) - { - throw new InvalidOperationException(); - } - if (_hasManaged && _managed.i == _unmanaged.i) - { - return _managed; - } - return new StatefulType() { i = _unmanaged.i }; - } - - public void Free() - { - FreeCount++; - } - - public void OnInvoked() { } - } - - internal struct ManagedToUnmanaged - { - public static int FreeCount { get; private set; } - StatefulType? _managed; - bool _hasManaged; - public void FromManaged(StatefulType managed) - { - _hasManaged = true; - _managed = managed; - } - - public StatefulNative ToUnmanaged() - { - if (!_hasManaged) throw new InvalidOperationException(); - return new StatefulNative() { i = _managed.i }; - } - - public void Free() - { - FreeCount++; - } - - public void OnInvoked() { } - } - - internal struct UnmanagedToManaged - { - public static int FreeCount { get; private set; } - StatefulNative _unmanaged; - bool _hasUnmanaged; - - public void FromUnmanaged(StatefulNative unmanaged) - { - _hasUnmanaged = true; - _unmanaged = unmanaged; - } - - public StatefulType ToManaged() - { - if (!_hasUnmanaged) - { - throw new InvalidOperationException(); - } - return new StatefulType() { i = _unmanaged.i }; - } - - public void Free() - { - FreeCount++; - } - - public void OnInvoked() { } - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs deleted file mode 100644 index 1347a2858e3725..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs +++ /dev/null @@ -1,288 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] - internal partial interface IStatefulPinnedMarshalling - { - void Method(StatefulPinnedType param); - void MethodIn(in StatefulPinnedType param); - void MethodOut(out StatefulPinnedType param); - void MethodRef(ref StatefulPinnedType param); - StatefulPinnedType Return(); - } - - [GeneratedComClass] - internal partial class StatefulPinnedMarshalling : IStatefulPinnedMarshalling - { - public void Method(StatefulPinnedType param) { param.I = 100; } - public void MethodIn(in StatefulPinnedType param) { param.I = 101; } - public void MethodOut(out StatefulPinnedType param) => param = new StatefulPinnedType() { I = 102 }; - public void MethodRef(ref StatefulPinnedType param) { param = new StatefulPinnedType() { I = 103 }; } - public StatefulPinnedType Return() => new StatefulPinnedType() { I = 104 }; - } - - [NativeMarshalling(typeof(StatefulPinnedTypeMarshaller))] - internal class StatefulPinnedType - { - public int I; - } - - internal unsafe struct StatefulPinnedNative - { - public int I; - } - - [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] - [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.UnmanagedToManagedOut, typeof(UnmanagedToManagedOut))] - [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManagedIn))] - [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.ManagedToUnmanagedOut, typeof(ManagedToUnmanagedOut))] - [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.ManagedToUnmanagedRef, typeof(ManagedToUnmanagedRef))] - [CustomMarshaller(typeof(StatefulPinnedType), MarshalMode.UnmanagedToManagedRef, typeof(UnmanagedToManagedRef))] - internal unsafe static class StatefulPinnedTypeMarshaller - { - public ref struct ManagedToUnmanagedIn - { - static bool s_mustPin; - public static void DisableNonPinnedPath() => s_mustPin = true; - public static void EnableNonPinnedPath() => s_mustPin = false; - - StatefulPinnedType _managed; - bool _hasManaged; - Span buffer; - nint _ptr; - bool _canFree; - bool _isPinned; - ref StatefulPinnedNative _refNativeStruct; - - public void FromManaged(StatefulPinnedType managed) - { - _hasManaged = true; - _managed = managed; - } - - public ref StatefulPinnedNative GetPinnableReference() - { - if (!_hasManaged) - throw new InvalidOperationException(); - buffer = new byte[sizeof(StatefulPinnedNative)]; - _isPinned = true; - _refNativeStruct = ref MemoryMarshal.AsRef(buffer); - return ref _refNativeStruct; - } - - public StatefulPinnedNative* ToUnmanaged() - { - if (!_hasManaged) - throw new InvalidOperationException(); - - _canFree = true; - if (_isPinned) - { - // Unsafe.AsPointer is safe, because the result from GetPinnableReference is pinned - _refNativeStruct = new StatefulPinnedNative() { I = _managed.I }; - return (StatefulPinnedNative*)Unsafe.AsPointer(ref _refNativeStruct); - } - - if (s_mustPin) - throw new InvalidOperationException("Expected to pin, but is instead converting with default ToUnmanaged."); - - _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); - *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; - return (StatefulPinnedNative*)_ptr; - } - - public void Free() - { - if (!_canFree) - throw new InvalidOperationException(); - - if (!_isPinned && _ptr != 0) - { - Marshal.FreeHGlobal(_ptr); - } - } - } - - public struct ManagedToUnmanagedOut - { - StatefulPinnedNative* _unmanaged; - bool _hasUnmanaged; - - public void FromUnmanaged(StatefulPinnedNative* unmanaged) - { - _unmanaged = unmanaged; - _hasUnmanaged = true; - } - - public StatefulPinnedType ToManaged() - { - if (!_hasUnmanaged) - throw new InvalidOperationException(); - return new StatefulPinnedType() { I = _unmanaged->I }; - } - - public void Free() - { - if (!_hasUnmanaged) - throw new InvalidOperationException(); - var ptr = (nint)_unmanaged; - if (ptr != 0) - Marshal.FreeHGlobal(ptr); - } - } - - public struct UnmanagedToManagedIn - { - StatefulPinnedNative* _unmanaged; - bool _hasUnmanaged; - public void FromUnmanaged(StatefulPinnedNative* unmanaged) - { - _unmanaged = unmanaged; - _hasUnmanaged = true; - } - - public StatefulPinnedType ToManaged() - { - if (!_hasUnmanaged) - throw new InvalidOperationException(); - return new StatefulPinnedType() { I = _unmanaged->I }; - } - - public void Free() - { - } - } - - public struct UnmanagedToManagedOut - { - StatefulPinnedType _managed; - bool _hasManaged; - nint _ptr; - - public void FromManaged(StatefulPinnedType managed) - { - _hasManaged = true; - _managed = managed; - } - - public StatefulPinnedNative* ToUnmanaged() - { - if (!_hasManaged) - throw new InvalidOperationException(); - _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); - *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; - return (StatefulPinnedNative*)_ptr; - } - - public void Free() - { - } - } - - - public struct ManagedToUnmanagedRef - { - StatefulPinnedNative* _unmanaged; - bool _hasUnmanaged; - public void FromUnmanaged(StatefulPinnedNative* unmanaged) - { - _unmanaged = unmanaged; - _hasUnmanaged = true; - } - - public StatefulPinnedType ToManaged() - { - if (!_hasUnmanaged) - throw new InvalidOperationException(); - return new StatefulPinnedType() { I = _unmanaged->I }; - } - - StatefulPinnedType _managed; - bool _hasManaged; - nint _ptr; - bool _hasAllocated; - - public void FromManaged(StatefulPinnedType managed) - { - _hasManaged = true; - _managed = managed; - } - - public StatefulPinnedNative* ToUnmanaged() - { - if (!_hasManaged) - throw new InvalidOperationException(); - _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); - _hasAllocated = true; - *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; - return (StatefulPinnedNative*)_ptr; - } - - public void Free() - { - if (_hasUnmanaged) - { - Marshal.FreeHGlobal((nint)_unmanaged); - } - else if (_hasAllocated) - { - Marshal.FreeHGlobal(_ptr); - } - } - } - public struct UnmanagedToManagedRef - { - StatefulPinnedNative* _unmanaged; - bool _hasUnmanaged; - public void FromUnmanaged(StatefulPinnedNative* unmanaged) - { - _unmanaged = unmanaged; - _hasUnmanaged = true; - } - - public StatefulPinnedType ToManaged() - { - if (!_hasUnmanaged) - throw new InvalidOperationException(); - return new StatefulPinnedType() { I = _unmanaged->I }; - } - - StatefulPinnedType _managed; - bool _hasManaged; - nint _ptr; - bool _hasAllocated; - - public void FromManaged(StatefulPinnedType managed) - { - _hasManaged = true; - _managed = managed; - } - - public StatefulPinnedNative* ToUnmanaged() - { - if (!_hasManaged) - throw new InvalidOperationException(); - _ptr = Marshal.AllocHGlobal(sizeof(StatefulPinnedNative)); - _hasAllocated = true; - *(StatefulPinnedNative*)_ptr = new StatefulPinnedNative() { I = _managed.I }; - return (StatefulPinnedNative*)_ptr; - } - - public void Free() - { - if (_hasAllocated && _hasUnmanaged) - { - Marshal.FreeHGlobal((nint)_unmanaged); - } - } - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs deleted file mode 100644 index 42d1486a690b16..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessAllShapes.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - /// - /// This type uses a stateless marshaller with all methods from every marshaller shape present - /// - [GeneratedComInterface] - [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] - internal partial interface IStatelessAllShapes - { - void Method(StatelessAllShapesType param); - void MethodIn(in StatelessAllShapesType param); - void MethodOut(out StatelessAllShapesType param); - void MethodRef(ref StatelessAllShapesType param); - StatelessAllShapesType Return(); - [PreserveSig] - StatelessAllShapesType ReturnPreserveSig(); - void Arrays( - int size, - [MarshalUsing(CountElementName = nameof(size))] - StatelessAllShapesType[] param, - [MarshalUsing(CountElementName = nameof(size))] - in StatelessAllShapesType[] paramIn, - [MarshalUsing(CountElementName = nameof(size))] - out StatelessAllShapesType[] paramOut, - [MarshalUsing(CountElementName = nameof(size))] - ref StatelessAllShapesType[] paramRef, - [MarshalUsing(CountElementName = nameof(size))] - [In] StatelessAllShapesType[] paramContentsIn, - [MarshalUsing(CountElementName = nameof(size))] - [Out] StatelessAllShapesType[] paramContentsOut, - [MarshalUsing(CountElementName = nameof(size))] - [In, Out] StatelessAllShapesType[] paramContentsInOut); - - } - - [NativeMarshalling(typeof(AllStatelessMarshallerShapes))] - internal class StatelessAllShapesType - { - } - - internal struct StatelessAllShapesNative - { - } - - [CustomMarshaller(typeof(StatelessAllShapesType), MarshalMode.Default, typeof(AllStatelessMarshallerShapes))] - internal unsafe static class AllStatelessMarshallerShapes - { - public static ref nint GetPinnableReference(StatelessAllShapesType managed) => throw new NotImplementedException(); - public static int BufferSize => 32; - public static StatelessAllShapesNative* ConvertToUnmanaged(StatelessAllShapesType managed, Span buffer) => throw new NotImplementedException(); - public static StatelessAllShapesNative* ConvertToUnmanaged(StatelessAllShapesType managed) => throw new NotImplementedException(); - public static StatelessAllShapesType ConvertToManaged(StatelessAllShapesNative* unmanaged) => throw new NotImplementedException(); - public static StatelessAllShapesType ConvertToManagedFinally(StatelessAllShapesNative* unmanaged) => throw new NotImplementedException(); - public static void Free(StatelessAllShapesNative* unmanaged) { } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs deleted file mode 100644 index 6388ab2990113b..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] - internal partial interface IStatelessCallerAllocatedBufferMarshalling - { - void Method(StatelessCallerAllocatedBufferType param); - void MethodIn(in StatelessCallerAllocatedBufferType param); - void MethodOut(out StatelessCallerAllocatedBufferType param); - void MethodRef(ref StatelessCallerAllocatedBufferType param); - StatelessCallerAllocatedBufferType Return(); - [PreserveSig] - StatelessCallerAllocatedBufferType ReturnPreserveSig(); - } - - [GeneratedComClass] - internal partial class StatelessCallerAllocatedBufferMarshalling : IStatelessCallerAllocatedBufferMarshalling - { - public void Method(StatelessCallerAllocatedBufferType param) { } - public void MethodIn(in StatelessCallerAllocatedBufferType param) { } - public void MethodOut(out StatelessCallerAllocatedBufferType param) { param = new StatelessCallerAllocatedBufferType { I = 20 }; } - public void MethodRef(ref StatelessCallerAllocatedBufferType param) { param = new StatelessCallerAllocatedBufferType { I = 200 }; } - public StatelessCallerAllocatedBufferType Return() => new StatelessCallerAllocatedBufferType() { I = 201 }; - public StatelessCallerAllocatedBufferType ReturnPreserveSig() => new StatelessCallerAllocatedBufferType() { I = 202 }; - } - - [NativeMarshalling(typeof(StatelessCallerAllocatedBufferTypeMarshaller))] - internal class StatelessCallerAllocatedBufferType - { - public int I; - } - - internal struct StatelessCallerAllocatedBufferNative - { - public int I; - } - - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ElementIn, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ElementOut, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ElementRef, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanagedIn))] - [CustomMarshaller(typeof(StatelessCallerAllocatedBufferType), MarshalMode.UnmanagedToManagedOut, typeof(UnmanagedToManagedOut))] - internal static unsafe class StatelessCallerAllocatedBufferTypeMarshaller - { - static bool _canAllocate = true; - public static void DisableAllocations() => _canAllocate = false; - public static void EnableAllocations() => _canAllocate = true; - public static void AssertAllPointersFreed() - { - if (_ptrs.Any()) throw new InvalidOperationException(); - } - - static HashSet _ptrs = new(); - - public static int FreeCount { get; private set; } - - public static class UnmanagedToManaged - { - public static StatelessCallerAllocatedBufferType ConvertToManaged(StatelessCallerAllocatedBufferNative* unmanaged) - { - return new StatelessCallerAllocatedBufferType() { I = unmanaged->I }; - } - - public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) - { - FreeCount++; - if (_ptrs.Contains((nint)unmanaged)) - { - Marshal.FreeHGlobal((nint)unmanaged); - _ptrs.Remove((nint)unmanaged); - } - } - } - - public static class ManagedToUnmanagedIn - { - public static int BufferSize => sizeof(StatelessCallerAllocatedBufferNative); - - public static StatelessCallerAllocatedBufferNative* ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed, Span buffer) - { - var unmanaged = new StatelessCallerAllocatedBufferNative() { I = managed.I }; - MemoryMarshal.Write(buffer, in unmanaged); - // Unsafe.AsPointer is safe since buffer is pinned - return (StatelessCallerAllocatedBufferNative*)Unsafe.AsPointer(ref MemoryMarshal.AsRef(buffer)); - } - - public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) - { - FreeCount++; - if (_ptrs.Contains((nint)unmanaged)) - { - Marshal.FreeHGlobal((nint)unmanaged); - _ptrs.Remove((nint)unmanaged); - } - } - } - - public static class UnmanagedToManagedOut - { - public static StatelessCallerAllocatedBufferNative* ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed) - { - if (!_canAllocate) - throw new InvalidOperationException("Marshalling used default ConverToUnmanaged when CallerAllocatedBuffer was expected"); - nint ptr = Marshal.AllocHGlobal(sizeof(StatelessCallerAllocatedBufferNative)); - _ptrs.Add(ptr); - var structPtr = (StatelessCallerAllocatedBufferNative*)ptr; - structPtr->I = managed.I; - return structPtr; - } - - public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) - { - FreeCount++; - if (_ptrs.Contains((nint)unmanaged)) - { - Marshal.FreeHGlobal((nint)unmanaged); - _ptrs.Remove((nint)unmanaged); - } - } - } - - public static class Bidirectional - { - public static StatelessCallerAllocatedBufferNative* ConvertToUnmanaged(StatelessCallerAllocatedBufferType managed) - { - if (!_canAllocate) - throw new InvalidOperationException("Marshalling used default ConverToUnmanaged when CallerAllocatedBuffer was expected"); - nint ptr = Marshal.AllocHGlobal(sizeof(StatelessCallerAllocatedBufferNative)); - _ptrs.Add(ptr); - var structPtr = (StatelessCallerAllocatedBufferNative*)ptr; - structPtr->I = managed.I; - return structPtr; - } - - public static StatelessCallerAllocatedBufferType ConvertToManaged(StatelessCallerAllocatedBufferNative* unmanaged) - { - return new StatelessCallerAllocatedBufferType() { I = unmanaged->I }; - } - - public static void Free(StatelessCallerAllocatedBufferNative* unmanaged) - { - FreeCount++; - if (_ptrs.Contains((nint)unmanaged)) - { - Marshal.FreeHGlobal((nint)unmanaged); - _ptrs.Remove((nint)unmanaged); - } - } - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs deleted file mode 100644 index b9aeecd69d10a3..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionAllShapes.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4731FA5D-C103-4A22-87A1-58DCEDD4A9B3")] - internal partial interface IStatelessCollectionAllShapes - { - void Method([MarshalUsing(CountElementName = nameof(size))] StatelessCollectionAllShapes param, int size); - void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessCollectionAllShapes param, int size); - void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessCollectionAllShapes param, out int size); - void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessCollectionAllShapes param, int size); - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollectionAllShapes Return(out int size); - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollectionAllShapes ReturnPreserveSig(out int size); - } - - [NativeMarshalling(typeof(StatelessCollectionAllShapesMarshaller<,>))] - internal class StatelessCollectionAllShapes - { - } - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatelessCollectionAllShapes<>), MarshalMode.Default, typeof(StatelessCollectionAllShapesMarshaller<,>))] - internal unsafe static class StatelessCollectionAllShapesMarshaller where TUnmanagedElement : unmanaged - { - public static void Free(TUnmanagedElement* unmanaged) { } - - // ToUnmanaged - public static TUnmanagedElement* AllocateContainerForUnmanagedElements(StatelessCollectionAllShapes managed, out int numElements) - => throw new NotImplementedException(); - - public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionAllShapes managed) // Can throw exceptions - => throw new NotImplementedException(); - - public static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements) // Can throw exceptions - => throw new NotImplementedException(); - - public static ref TUnmanagedElement* GetPinnableReference(StatelessCollectionAllShapes managed) - => throw new NotImplementedException(); - - - - // Caller Allocated buffer ToUnmanaged - public static int BufferSize { get; } - public static TUnmanagedElement* AllocateContainerForUnmanagedElements(StatelessCollectionAllShapes managed, Span buffer, out int numElements) - => throw new NotImplementedException(); - - - // ToManaged - public static StatelessCollectionAllShapes AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) - => throw new NotImplementedException(); - - public static Span GetManagedValuesDestination(StatelessCollectionAllShapes managed) - => throw new NotImplementedException(); - - public static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements) - => throw new NotImplementedException(); - - - //ToManaged Guaranteed marshalling - public static StatelessCollectionAllShapes AllocateContainerForManagedElementsFinally(TUnmanagedElement* unmanaged, int numElements) - => throw new NotImplementedException(); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs deleted file mode 100644 index 684352435e6269..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionBlittableElement.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] - partial interface IStatelessCollectionBlittableElement - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatelessCollection p, - int size); - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, - in int size); - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, - int size); - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, - out int size); - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollection Return(int size); - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollection ReturnPreserveSig(int size); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs deleted file mode 100644 index c13caee66fa472..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionCallerAllocatedBuffer.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] - internal partial interface IStatelessCollectionCallerAllocatedBuffer - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatelessCollectionCallerAllocatedBuffer p, - int size); - - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatelessCollectionCallerAllocatedBuffer pIn, - in int size); - - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollectionCallerAllocatedBuffer pRef, - int size); - - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatelessCollectionCallerAllocatedBuffer pOut, - out int size); - - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollectionCallerAllocatedBuffer Return(int size); - - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollectionCallerAllocatedBuffer ReturnPreserveSig(int size); - } - - [NativeMarshalling(typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>))] - internal class StatelessCollectionCallerAllocatedBuffer - { - } - - internal struct StatelessCollectionCallerAllocatedBufferNative - { - - } - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollectionCallerAllocatedBuffer<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessCollectionCallerAllocatedBufferMarshaller<,>.Bidirectional))] - internal static unsafe class StatelessCollectionCallerAllocatedBufferMarshaller where TUnmanagedElement : unmanaged - { - internal static class Bidirectional - { - public static int BufferSize => throw new NotImplementedException(); - public static StatelessCollectionCallerAllocatedBufferNative AllocateContainerForUnmanagedElements(StatelessCollectionCallerAllocatedBuffer managed, Span buffer, out int numElements) - { - throw new NotImplementedException(); - } - - // Bidirectional requires non-buffer version of this method - public static StatelessCollectionCallerAllocatedBufferNative AllocateContainerForUnmanagedElements(StatelessCollectionCallerAllocatedBuffer managed, out int numElements) - { - throw new NotImplementedException(); - } - - public static StatelessCollectionCallerAllocatedBuffer AllocateContainerForManagedElements(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionCallerAllocatedBuffer managed) - { - throw new NotImplementedException(); - } - - public static Span GetUnmanagedValuesDestination(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static Span GetManagedValuesDestination(StatelessCollectionCallerAllocatedBuffer managed) - { - throw new NotImplementedException(); - } - - public static void Free(StatelessCollectionCallerAllocatedBufferNative unmanaged) { } - } - - internal static class ManagedToUnmanaged - { - public static int BufferSize => throw new NotImplementedException(); - public static StatelessCollectionCallerAllocatedBufferNative AllocateContainerForUnmanagedElements(StatelessCollectionCallerAllocatedBuffer managed, Span buffer, out int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionCallerAllocatedBuffer managed) - { - throw new NotImplementedException(); - } - - public static Span GetUnmanagedValuesDestination(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static void Free(StatelessCollectionCallerAllocatedBufferNative unmanaged) => throw new NotImplementedException(); - } - - internal static class UnmanagedToManaged - { - public static StatelessCollectionCallerAllocatedBuffer AllocateContainerForManagedElements(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionCallerAllocatedBufferNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static Span GetManagedValuesDestination(StatelessCollectionCallerAllocatedBuffer managed) - { - throw new NotImplementedException(); - } - - public static void Free(StatelessCollectionCallerAllocatedBufferNative unmanaged) => throw new NotImplementedException(); - - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs deleted file mode 100644 index 33d478c597b9c6..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionPinnableReference.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(), Guid("0A52BA7C-E08B-42A4-A1F4-1A2BF2C07E60")] - internal partial interface IStatelessCollectionPinnableReference - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatelessCollectionPinnableReference p, - int size); - - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatelessCollectionPinnableReference pIn, - in int size); - - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollectionPinnableReference pRef, - int size); - - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatelessCollectionPinnableReference pOut, - out int size); - - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollectionPinnableReference Return(int size); - } - - [NativeMarshalling(typeof(StatelessCollectionPinnableReferenceMarshaller<,>))] - internal class StatelessCollectionPinnableReference - { - } - - internal struct StatelessCollectionPinnableReferenceNative - { - public static unsafe explicit operator void*(StatelessCollectionPinnableReferenceNative _) => throw new NotImplementedException(); - public static unsafe explicit operator StatelessCollectionPinnableReferenceNative(void* _) => throw new NotImplementedException(); - } - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ElementIn, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ElementOut, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ElementRef, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessCollectionPinnableReference<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessCollectionPinnableReferenceMarshaller<,>.ManagedToUnmanaged))] - internal static class StatelessCollectionPinnableReferenceMarshaller where TUnmanaged : unmanaged - { - public static class Bidirectional - { - public static ref StatelessCollectionPinnableReferenceNative GetPinnableReference(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); - public static StatelessCollectionPinnableReferenceNative AllocateContainerForUnmanagedElements(StatelessCollectionPinnableReference managed, out int numElements) => throw new NotImplementedException(); - public static StatelessCollectionPinnableReference AllocateContainerForManagedElements(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); - public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); - public static Span GetUnmanagedValuesDestination(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); - public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); - public static Span GetManagedValuesDestination(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); - public static void Free(StatelessCollectionPinnableReferenceNative native) { } - } - - public static class UnmanagedToManaged - { - public static StatelessCollectionPinnableReference AllocateContainerForManagedElements(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); - public static ReadOnlySpan GetUnmanagedValuesSource(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); - public static Span GetManagedValuesDestination(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); - public static void Free(StatelessCollectionPinnableReferenceNative native) { } - } - - public static class ManagedToUnmanaged - { - public static ref StatelessCollectionPinnableReferenceNative GetPinnableReference(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); - public static StatelessCollectionPinnableReferenceNative AllocateContainerForUnmanagedElements(StatelessCollectionPinnableReference managed, out int numElements) => throw new NotImplementedException(); - public static ReadOnlySpan GetManagedValuesSource(StatelessCollectionPinnableReference managed) => throw new NotImplementedException(); - public static Span GetUnmanagedValuesDestination(StatelessCollectionPinnableReferenceNative unmanaged, int numElements) => throw new NotImplementedException(); - public static void Free(StatelessCollectionPinnableReferenceNative native) { } - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs deleted file mode 100644 index 68ad9f409b3b84..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCollectionStatelessElement.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(), Guid("0A52B77C-E08B-4274-A1F4-1A2BF2C07E60")] - internal partial interface IStatelessCollectionStatelessElement - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatelessCollection p, - int size); - - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatelessCollection pIn, - in int size); - - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatelessCollection pRef, - int size); - - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatelessCollection pOut, - out int size); - - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollection Return(int size); - - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessCollection ReturnPreserveSig(int size); - } - - [NativeMarshalling(typeof(StatelessCollectionMarshaller<,>))] - internal class StatelessCollection - { - } - - internal struct NativeCollection - { - - } - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessCollectionMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessCollectionMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessCollectionMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessCollectionMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementIn, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementOut, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessCollection<>), MarshalMode.ElementRef, typeof(StatelessCollectionMarshaller<,>.Bidirectional))] - internal static unsafe class StatelessCollectionMarshaller where TUnmanagedElement : unmanaged - { - internal static class Bidirectional - { - public static NativeCollection AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) - { - throw new NotImplementedException(); - } - - public static StatelessCollection AllocateContainerForManagedElements(NativeCollection unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) - { - throw new NotImplementedException(); - } - - public static Span GetUnmanagedValuesDestination(NativeCollection unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetUnmanagedValuesSource(NativeCollection unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static Span GetManagedValuesDestination(StatelessCollection managed) - { - throw new NotImplementedException(); - } - - public static void Free(NativeCollection unmanaged) { } - } - - internal static class ManagedToUnmanaged - { - public static NativeCollection AllocateContainerForUnmanagedElements(StatelessCollection managed, out int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetManagedValuesSource(StatelessCollection managed) - { - throw new NotImplementedException(); - } - - public static Span GetUnmanagedValuesDestination(NativeCollection unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static void Free(NativeCollection unmanaged) => throw new NotImplementedException(); - } - - internal static class UnmanagedToManaged - { - public static StatelessCollection AllocateContainerForManagedElements(NativeCollection unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetUnmanagedValuesSource(NativeCollection unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static Span GetManagedValuesDestination(StatelessCollection managed) - { - throw new NotImplementedException(); - } - - public static void Free(NativeCollection unmanaged) => throw new NotImplementedException(); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs deleted file mode 100644 index be7e1a9893a0b7..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessFinallyMarshalling.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4732FA5D-C105-4A26-87A7-58DCEDD4A9B3")] - internal partial interface IStatelessFinallyMarshalling - { - void Method(StatelessFinallyType param); - void MethodIn(in StatelessFinallyType param); - void MethodOut(out StatelessFinallyType param); - void MethodRef(ref StatelessFinallyType param); - StatelessFinallyType Return(); - [PreserveSig] - StatelessFinallyType ReturnPreserveSig(); - } - - [GeneratedComClass] - internal partial class StatelessFinallyMarshalling : IStatelessFinallyMarshalling - { - public void Method(StatelessFinallyType param) { _ = param.I; } - public void MethodIn(in StatelessFinallyType param) { _ = param.I; } - public void MethodOut(out StatelessFinallyType param) { param = new StatelessFinallyType { I = 42 }; } - public void MethodRef(ref StatelessFinallyType param) { _ = param.I; param = new StatelessFinallyType { I = 200 }; } - public StatelessFinallyType Return() => new StatelessFinallyType { I = 200 }; - public StatelessFinallyType ReturnPreserveSig() => new StatelessFinallyType { I = 200 }; - } - - [NativeMarshalling(typeof(StatelessFinallyTypeMarshaller))] - internal class StatelessFinallyType - { - public int I; - } - - internal struct StatelessFinallyNative - { - public int i; - } - - [CustomMarshaller(typeof(StatelessFinallyType), MarshalMode.Default, typeof(StatelessFinallyTypeMarshaller))] - internal static class StatelessFinallyTypeMarshaller - { - public static int FreeCount { get; private set; } - public static StatelessFinallyNative ConvertToUnmanaged(StatelessFinallyType managed) => new StatelessFinallyNative() { i = managed.I }; - - public static StatelessFinallyType ConvertToManagedFinally(StatelessFinallyNative unmanaged) => new StatelessFinallyType { I = unmanaged.i }; - - public static void Free(StatelessFinallyNative unmanaged) => FreeCount++; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs deleted file mode 100644 index e063e77258cc6a..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessMarshalling.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] - internal partial interface IStatelessMarshalling - { - void Method([MarshalUsing(CountElementName = nameof(size))] StatelessType param, int size); - void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessType param, int size); - void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessType param, int size); - void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessType param, int size); - StatelessType Return(); - [PreserveSig] - StatelessType ReturnPreserveSig(); - } - - [GeneratedComClass] - internal partial class StatelessMarshalling : IStatelessMarshalling - { - public void Method(StatelessType param, int size) { } - public void MethodIn(in StatelessType param, int size) { } - public void MethodOut(out StatelessType param, int size) { param = new StatelessType { I = 42 }; } - public void MethodRef(ref StatelessType param, int size) { param = new StatelessType { I = 200 }; } - public StatelessType Return() => throw new NotImplementedException(); - public StatelessType ReturnPreserveSig() => throw new NotImplementedException(); - } - - [NativeMarshalling(typeof(StatelessTypeMarshaller))] - internal class StatelessType - { - public int I; - } - - [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedOut, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedIn, typeof(UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementOut, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementIn, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.ElementRef, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))] - [CustomMarshaller(typeof(StatelessType), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))] - internal static class StatelessTypeMarshaller - { - public static int AllFreeCount => Bidirectional.FreeCount + UnmanagedToManaged.FreeCount + ManagedToUnmanaged.FreeCount; - - internal static class Bidirectional - { - public static int FreeCount { get; private set; } - public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; - - public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; - - public static void Free(nint unmanaged) => FreeCount++; - } - - internal static class ManagedToUnmanaged - { - public static int FreeCount { get; private set; } - public static void Free(nint unmanaged) => FreeCount++; - public static nint ConvertToUnmanaged(StatelessType managed) => managed.I; - } - - internal static class UnmanagedToManaged - { - public static int FreeCount { get; private set; } - public static void Free(nint unmanaged) => FreeCount++; - public static StatelessType ConvertToManaged(nint unmanaged) => new StatelessType { I = (int)unmanaged }; - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs deleted file mode 100644 index f625af2d27e3b1..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnableCollectionBlittableElements.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("3BBB0C99-7D6C-4AD1-BE4C-ACB4C2127F02")] - internal partial interface IStatelessPinnableCollectionBlittableElements - { - void Method( - [MarshalUsing(CountElementName = nameof(size))] StatelessPinnableCollection p, - int size); - - void MethodIn( - [MarshalUsing(CountElementName = nameof(size))] in StatelessPinnableCollection pIn, - in int size); - - void MethodRef( - [MarshalUsing(CountElementName = nameof(size))] ref StatelessPinnableCollection pRef, - int size); - - void MethodOut( - [MarshalUsing(CountElementName = nameof(size))] out StatelessPinnableCollection pOut, - out int size); - - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessPinnableCollection Return(int size); - - [PreserveSig] - [return: MarshalUsing(CountElementName = nameof(size))] - StatelessPinnableCollection ReturnPreserveSig(int size); - } - - [NativeMarshalling(typeof(StatelessPinnableCollectionMarshaller<,>))] - internal class StatelessPinnableCollection where T : unmanaged - { - } - - internal unsafe struct StatelessPinnableCollectionNative where T : unmanaged - { - public static explicit operator StatelessPinnableCollectionNative(void* ptr) => new StatelessPinnableCollectionNative(); - public static explicit operator void*(StatelessPinnableCollectionNative ptr) => (void*)null; - } - - - [ContiguousCollectionMarshaller] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ManagedToUnmanagedIn, typeof(StatelessPinnableCollectionMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.UnmanagedToManagedOut, typeof(StatelessPinnableCollectionMarshaller<,>.ManagedToUnmanaged))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ManagedToUnmanagedOut, typeof(StatelessPinnableCollectionMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.UnmanagedToManagedIn, typeof(StatelessPinnableCollectionMarshaller<,>.UnmanagedToManaged))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.UnmanagedToManagedRef, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ManagedToUnmanagedRef, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ElementIn, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ElementOut, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] - [CustomMarshaller(typeof(StatelessPinnableCollection<>), MarshalMode.ElementRef, typeof(StatelessPinnableCollectionMarshaller<,>.Bidirectional))] - internal static unsafe class StatelessPinnableCollectionMarshaller - where T : unmanaged - where TUnmanagedElement : unmanaged - { - internal static class Bidirectional - { - public static StatelessPinnableCollectionNative AllocateContainerForUnmanagedElements(StatelessPinnableCollection managed, out int numElements) - { - throw new NotImplementedException(); - } - - public static StatelessPinnableCollection AllocateContainerForManagedElements(StatelessPinnableCollectionNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetManagedValuesSource(StatelessPinnableCollection managed) - { - throw new NotImplementedException(); - } - - public static Span GetUnmanagedValuesDestination(StatelessPinnableCollectionNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetUnmanagedValuesSource(StatelessPinnableCollectionNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static Span GetManagedValuesDestination(StatelessPinnableCollection managed) - { - throw new NotImplementedException(); - } - - public static ref StatelessPinnableCollectionNative GetPinnableReference(StatelessPinnableCollection managed) - { - throw new NotImplementedException(); - } - - public static void Free(StatelessPinnableCollectionNative unmanaged) { } - } - - internal static class ManagedToUnmanaged - { - public static StatelessPinnableCollectionNative AllocateContainerForUnmanagedElements(StatelessPinnableCollection managed, out int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetManagedValuesSource(StatelessPinnableCollection managed) - { - throw new NotImplementedException(); - } - - public static Span GetUnmanagedValuesDestination(StatelessPinnableCollectionNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ref StatelessPinnableCollectionNative GetPinnableReference(StatelessPinnableCollection managed) - { - throw new NotImplementedException(); - } - - public static void Free(StatelessPinnableCollectionNative unmanaged) => throw new NotImplementedException(); - } - - internal static class UnmanagedToManaged - { - public static StatelessPinnableCollection AllocateContainerForManagedElements(StatelessPinnableCollectionNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static ReadOnlySpan GetUnmanagedValuesSource(StatelessPinnableCollectionNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - // Should be removed: https://github.com/dotnet/runtime/issues/89885 - public static Span GetUnmanagedValuesDestination(StatelessPinnableCollectionNative unmanaged, int numElements) - { - throw new NotImplementedException(); - } - - public static Span GetManagedValuesDestination(StatelessPinnableCollection managed) - { - throw new NotImplementedException(); - } - - public static void Free(StatelessPinnableCollectionNative unmanaged) => throw new NotImplementedException(); - - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs deleted file mode 100644 index 4419fc52c077cf..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessPinnedMarshalling.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface] - [Guid("4732FA5D-C105-4A23-87A7-58DCEDD4A9B3")] - internal partial interface IStatelessPinnedMarshalling - { - void Method([MarshalUsing(CountElementName = nameof(size))] StatelessPinnedType param, int size); - void MethodIn([MarshalUsing(CountElementName = nameof(size))] in StatelessPinnedType param, int size); - void MethodOut([MarshalUsing(CountElementName = nameof(size))] out StatelessPinnedType param, int size); - void MethodRef([MarshalUsing(CountElementName = nameof(size))] ref StatelessPinnedType param, int size); - StatelessPinnedType Return(); - [PreserveSig] - StatelessPinnedType ReturnPreserveSig(); - } - - [GeneratedComClass] - internal partial class StatelessPinnedMarshalling : IStatelessPinnedMarshalling - { - public void Method([MarshalUsing(CountElementName = "size")] StatelessPinnedType param, int size) { } - public void MethodIn([MarshalUsing(CountElementName = "size")] in StatelessPinnedType param, int size) { } - public void MethodOut([MarshalUsing(CountElementName = "size")] out StatelessPinnedType param, int size) { param = new StatelessPinnedType { I = 42 }; } - public void MethodRef([MarshalUsing(CountElementName = "size")] ref StatelessPinnedType param, int size) { param = new StatelessPinnedType { I = 200 }; } - public StatelessPinnedType Return() => throw new NotImplementedException(); - public StatelessPinnedType ReturnPreserveSig() => throw new NotImplementedException(); - } - - [NativeMarshalling(typeof(StatelessPinnedTypeMarshaller))] - internal class StatelessPinnedType - { - public int I; - } - - internal struct StatelessPinnedStruct - { - public int I; - } - - [CustomMarshaller(typeof(StatelessPinnedType), MarshalMode.Default, typeof(StatelessPinnedTypeMarshaller))] - internal static class StatelessPinnedTypeMarshaller - { - public static int FreeCount { get; private set; } - public static nint ConvertToUnmanaged(StatelessPinnedType managed) => managed.I; - - public static StatelessPinnedType ConvertToManaged(nint unmanaged) => new StatelessPinnedType { I = (int)unmanaged }; - - static StatelessPinnedStruct _field; - public static ref StatelessPinnedStruct GetPinnableReference(StatelessPinnedType unmanaged) - { - _field = new StatelessPinnedStruct() { I = unmanaged.I }; - return ref _field; - } - - public static void Free(nint unmanaged) => FreeCount++; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs deleted file mode 100644 index df956009fce8be..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverride.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(StringMarshalling = System.Runtime.InteropServices.StringMarshalling.Utf8)] - [Guid(IID)] - internal partial interface IStringMarshallingOverride - { - public const string IID = "5146B7DB-0588-469B-B8E5-B38090A2FC15"; - string StringMarshallingUtf8(string input); - - [return: MarshalAs(UnmanagedType.LPWStr)] - string MarshalAsLPWString([MarshalAs(UnmanagedType.LPWStr)] string input); - - [return: MarshalUsing(typeof(Utf16StringMarshaller))] - string MarshalUsingUtf16([MarshalUsing(typeof(Utf16StringMarshaller))] string input); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs deleted file mode 100644 index 5022d42d13930f..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStringMarshallingOverrideDerived.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] - [Guid(IID)] - internal partial interface IStringMarshallingOverrideDerived : IStringMarshallingOverride - { - public new const string IID = "3AFFE3FD-D11E-4195-8250-0C73321977A0"; - string StringMarshallingUtf8_2(string input); - - [return: MarshalAs(UnmanagedType.LPWStr)] - string MarshalAsLPWString_2([MarshalAs(UnmanagedType.LPWStr)] string input); - - [return: MarshalUsing(typeof(Utf16StringMarshaller))] - string MarshalUsingUtf16_2([MarshalUsing(typeof(Utf16StringMarshaller))] string input); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs deleted file mode 100644 index 0e4584c0982587..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ISystem.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] - [Guid(IID)] - internal partial interface ISystem - { - // Make sure method names System and Microsoft don't interfere with framework type / method references - void Microsoft(int p); - void System(int p); - public const string IID = "3BFFE3FD-D11E-4195-8250-0C73321977A0"; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs deleted file mode 100644 index 1c123065a2ec1c..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF16Marshalling.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [Guid(IID)] - [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)] - internal partial interface IUTF16Marshalling - { - public string GetString(); - - public void SetString(string value); - - public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs deleted file mode 100644 index 08dbbdd5e68924..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IUTF8Marshalling.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces -{ - [Guid(IID)] - [GeneratedComInterface(StringMarshalling = StringMarshalling.Utf8)] - internal partial interface IUTF8Marshalling - { - public string GetString(); - - public void SetString(string value); - - public const string IID = "E11D5F3E-DD57-41A6-A59E-7D110551A760"; - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs deleted file mode 100644 index de5aea6228a144..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/ManagedComMethodFailureException.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace SharedTypes.ComInterfaces -{ - internal class ManagedComMethodFailureException : Exception - { - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs deleted file mode 100644 index 0d331441e7dd9d..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ICollectionMarshallingFails.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces.MarshallingFails -{ - [GeneratedComInterface] - [Guid("A4857395-06FB-4A6E-81DB-35461BE999C5")] - internal partial interface ICollectionMarshallingFails - { - [return: MarshalUsing(ConstantElementCount = 10)] - [return: MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] - public int[] GetConstSize(); - - [return: MarshalUsing(CountElementName = nameof(size))] - [return: MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] - public int[] Get(out int size); - - public void Set( - [MarshalUsing(CountElementName = nameof(size))] - [MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] - int[] value, int size); - } - - [GeneratedComClass] - internal partial class ICollectionMarshallingFailsImpl : ICollectionMarshallingFails, ISupportErrorInfo - { - int[] _data = new[] { 1, 2, 3, 4, 5, 6, 7, 8 }; - public int[] Get(out int size) - { - size = _data.Length; - return _data; - } - - [return: MarshalUsing(ConstantElementCount = 10), MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 1)] - public int[] GetConstSize() => new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - public void Set(int[] value, int size) - { - _data = new int[size]; - value.CopyTo(_data, 0); - } - - int ISupportErrorInfo.InterfaceSupportsErrorInfo(in Guid riid) - { - if (riid == typeof(ICollectionMarshallingFails).GUID) - { - return 0; // S_OK - } - return 1; // S_FALSE - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs deleted file mode 100644 index 1a4eda0dca222f..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IJaggedIntArrayMarshallingFails.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces.MarshallingFails -{ - [GeneratedComInterface] - [Guid("9FA4A8A9-3D8F-48A8-B6FB-B45B5F1B9FB6")] - internal partial interface IJaggedIntArrayMarshallingFails - { - [return: MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), - MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - int[][] Get( - [MarshalUsing(CountElementName = nameof(length))] - out int[] widths, - out int length); - - int Get2( - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), - MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - out int[][] array, - [MarshalUsing(CountElementName = MarshalUsingAttribute.ReturnsCountValue)] - out int[] widths); - - [return: MarshalUsing(ConstantElementCount = 10), - MarshalUsing(ElementIndirectionDepth = 1, ConstantElementCount = 10), - MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - int[][] GetConstSize(); - - void Set( - [MarshalUsing(CountElementName = nameof(length)), - MarshalUsing(ElementIndirectionDepth = 1, CountElementName = nameof(widths)), - MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - int[][] array, - [MarshalUsing(CountElementName = nameof(length))] - int[] widths, - int length); - } - - [GeneratedComClass] - internal partial class IJaggedIntArrayMarshallingFailsImpl : IJaggedIntArrayMarshallingFails, ISupportErrorInfo - { - int[][] _data = new int[][] { new int[] { 1, 2, 3 }, new int[] { 4, 5 }, new int[] { 6, 7, 8, 9 } }; - int[] _widths = new int[] { 3, 2, 4 }; - public int[][] Get(out int[] widths, out int length) - { - widths = _widths; - length = _data.Length; - return _data; - } - public int Get2(out int[][] array, out int[] widths) - { - array = _data; - widths = _widths; - return array.Length; - } - - [return: MarshalUsing(ConstantElementCount = 10), MarshalUsing(ElementIndirectionDepth = 1, ConstantElementCount = 10), MarshalUsing(typeof(ThrowOn4thElementMarshalled), ElementIndirectionDepth = 2)] - public int[][] GetConstSize() - { - int[][] values = new int[10][]; - for (int i = 0; i < 10; i++) - { - values[i] = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - } - return values; - } - - public void Set(int[][] array, int[] widths, int length) - { - _data = array; - _widths = widths; - } - - int ISupportErrorInfo.InterfaceSupportsErrorInfo(in Guid riid) - { - if (riid == typeof(IJaggedIntArrayMarshallingFails).GUID) - { - return 0; // S_OK - } - return 1; // S_FALSE - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs deleted file mode 100644 index 5492f147b137e2..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/IStringArrayMarshallingFails.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces.MarshallingFails -{ - /// - /// Has methods that marshal a string array in different ways - /// - [GeneratedComInterface(StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshallingFails))] - [Guid("BE11C211-76D5-496F-A117-82F5D13208F7")] - internal partial interface IStringArrayMarshallingFails - { - public void Param([MarshalUsing(ConstantElementCount = 10)] string[] value); - public void InParam([MarshalUsing(ConstantElementCount = 10)] in string[] value); - public void RefParam([MarshalUsing(ConstantElementCount = 10)] ref string[] value); - public void OutParam([MarshalUsing(ConstantElementCount = 10)] out string[] value); - public void ByValueOutParam([MarshalUsing(ConstantElementCount = 10)][Out] string[] value); - public void ByValueInOutParam([MarshalUsing(ConstantElementCount = 10)][In, Out] string[] value); - [return: MarshalUsing(ConstantElementCount = 10)] - public string[] ReturnValue(); - } - - /// - /// Implements IStringArrayMarshallingFails. - /// - [GeneratedComClass] - internal partial class IStringArrayMarshallingFailsImpl : IStringArrayMarshallingFails, ISupportErrorInfo - { - public static string[] StartingStrings { get; } = new string[] { "Hello", "World", "Lorem", "Ipsum", "Dolor", "Sample", "Text", ".Net", "Interop", "string" }; - private string[] _strings = StartingStrings; - public void ByValueInOutParam([In, MarshalUsing(ConstantElementCount = 10), Out] string[] value) => value[0] = _strings[0]; - public void ByValueOutParam([MarshalUsing(ConstantElementCount = 10), Out] string[] value) => value = _strings; - public void InParam([MarshalUsing(ConstantElementCount = 10)] in string[] value) => value[0] = _strings[0]; - public void OutParam([MarshalUsing(ConstantElementCount = 10)] out string[] value) => value = _strings; - public void Param([MarshalUsing(ConstantElementCount = 10)] string[] value) => value[0] = _strings[0]; - public void RefParam([MarshalUsing(ConstantElementCount = 10)] ref string[] value) => value[0] = _strings[0]; - [return: MarshalUsing(ConstantElementCount = 10)] - public string[] ReturnValue() => _strings; - - int ISupportErrorInfo.InterfaceSupportsErrorInfo(in Guid riid) - { - if (riid == typeof(IStringArrayMarshallingFails).GUID) - { - return 0; // S_OK - } - return 1; // S_FALSE - } - } - - /// - /// Marshals and unmarshals elements of string arrays, throwing an exception instead of marshalling the element number - /// - [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshallingFails))] - [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringMarshallingFails))] - [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringMarshallingFails))] - internal static unsafe class StringMarshallingFails - { - static int _marshalledCount = 0; - const int ThrowOnNthMarshalledElement = 4; - public static nint ConvertToUnmanaged(string managed) - { - if (++_marshalledCount == ThrowOnNthMarshalledElement) - { - _marshalledCount = 0; - throw new MarshallingFailureException("This marshaller throws on the Nth element marshalled where N = " + ThrowOnNthMarshalledElement); - } - return (nint)Utf8StringMarshaller.ConvertToUnmanaged(managed); - } - - public static string ConvertToManaged(nint unmanaged) - { - return Utf8StringMarshaller.ConvertToManaged((byte*)unmanaged); - } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs deleted file mode 100644 index b34a824ab394b3..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ISupportErrorInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces.MarshallingFails -{ - [GeneratedComInterface] - [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] - internal partial interface ISupportErrorInfo - { - [PreserveSig] - int InterfaceSupportsErrorInfo(in Guid riid); - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs deleted file mode 100644 index b832266e409a46..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/MarshallingFailureException.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace SharedTypes.ComInterfaces.MarshallingFails -{ - internal class MarshallingFailureException : Exception - { - public MarshallingFailureException() : base() { } - public MarshallingFailureException(string message) : base(message) { } - } -} diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs deleted file mode 100644 index 0bfb7e052d74a3..00000000000000 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/MarshallingFails/ThrowOn4thElementMarshalled.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices.Marshalling; - -namespace SharedTypes.ComInterfaces.MarshallingFails -{ - [CustomMarshaller(typeof(int), MarshalMode.ElementIn, typeof(ThrowOn4thElementMarshalled))] - [CustomMarshaller(typeof(int), MarshalMode.ElementOut, typeof(ThrowOn4thElementMarshalled))] - internal static class ThrowOn4thElementMarshalled - { - static int _marshalledCount = 0; - static int _unmarshalledCount = 0; - public static int FreeCount { get; private set; } - public static nint ConvertToUnmanaged(int managed) - { - if (_marshalledCount++ == 3) - { - _marshalledCount = 0; - throw new MarshallingFailureException("The element was the 4th element (with 0-based index 3)"); - } - return managed; - } - - public static int ConvertToManaged(nint unmanaged) - { - if (_unmarshalledCount++ == 3) - { - _unmarshalledCount = 0; - throw new MarshallingFailureException("The element was the 4th element (with 0-based index 3)"); - } - return (int)unmanaged; - } - public static void Free(nint unmanaged) => ++FreeCount; - } - -} diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj deleted file mode 100644 index a07418861d50d8..00000000000000 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/Canonicalization/System.Runtime.Serialization.Xml.Canonicalization.csproj +++ /dev/null @@ -1,72 +0,0 @@ - - - true - $(NetCoreAppCurrent) - - - - - - - - - - - - - - - - - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - - - - \ No newline at end of file diff --git a/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs b/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs deleted file mode 100644 index e5b3bb8a4129dd..00000000000000 --- a/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netcoreapp.cs +++ /dev/null @@ -1,222 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ------------------------------------------------------------------------------ -// Changes to this file must follow the https://aka.ms/api-review process. -// ------------------------------------------------------------------------------ - -namespace System.Security.Cryptography.Pkcs -{ - public sealed partial class CmsRecipient - { - public CmsRecipient(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, System.Security.Cryptography.RSAEncryptionPadding rsaEncryptionPadding) { } - public CmsRecipient(System.Security.Cryptography.Pkcs.SubjectIdentifierType recipientIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, System.Security.Cryptography.RSAEncryptionPadding rsaEncryptionPadding) { } - public System.Security.Cryptography.RSAEncryptionPadding? RSAEncryptionPadding { get { throw null; } } - } - public sealed partial class CmsSigner - { - public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.AsymmetricAlgorithm? privateKey) { } - [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] - public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.SlhDsa? privateKey) { } - public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.RSA? privateKey, System.Security.Cryptography.RSASignaturePadding? signaturePadding) { } - public System.Security.Cryptography.AsymmetricAlgorithm? PrivateKey { get { throw null; } set { } } - public System.Security.Cryptography.RSASignaturePadding? SignaturePadding { get { throw null; } set { } } - } - public sealed partial class ContentInfo - { - public static System.Security.Cryptography.Oid GetContentType(ReadOnlySpan encodedMessage) { throw null; } - } - public sealed partial class EnvelopedCms - { - public void Decode(System.ReadOnlySpan encodedMessage) { } - public void Decrypt(System.Security.Cryptography.Pkcs.RecipientInfo recipientInfo, System.Security.Cryptography.AsymmetricAlgorithm? privateKey) { } - } - public sealed partial class Pkcs12Builder - { - public Pkcs12Builder() { } - public bool IsSealed { get { throw null; } } - public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, byte[]? passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { } - public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { } - public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters) { } - public void AddSafeContentsEncrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents, string? password, System.Security.Cryptography.PbeParameters pbeParameters) { } - public void AddSafeContentsUnencrypted(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents) { } - public byte[] Encode() { throw null; } - public void SealWithMac(System.ReadOnlySpan password, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int iterationCount) { } - public void SealWithMac(string? password, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, int iterationCount) { } - public void SealWithoutIntegrity() { } - public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } - } - public sealed partial class Pkcs12CertBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag - { - public Pkcs12CertBag(System.Security.Cryptography.Oid certificateType, System.ReadOnlyMemory encodedCertificate) : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } - public System.ReadOnlyMemory EncodedCertificate { get { throw null; } } - public bool IsX509Certificate { get { throw null; } } - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] - public System.Security.Cryptography.X509Certificates.X509Certificate2 GetCertificate() { throw null; } - public System.Security.Cryptography.Oid GetCertificateType() { throw null; } - } - public enum Pkcs12ConfidentialityMode - { - None = 1, - Password = 2, - PublicKey = 3, - Unknown = 0, - } - public sealed partial class Pkcs12Info - { - internal Pkcs12Info() { } - public System.Collections.ObjectModel.ReadOnlyCollection AuthenticatedSafe { get { throw null; } } - public System.Security.Cryptography.Pkcs.Pkcs12IntegrityMode IntegrityMode { get { throw null; } } - public static System.Security.Cryptography.Pkcs.Pkcs12Info Decode(System.ReadOnlyMemory encodedBytes, out int bytesConsumed, bool skipCopy = false) { throw null; } - public bool VerifyMac(System.ReadOnlySpan password) { throw null; } - public bool VerifyMac(string? password) { throw null; } - } - public enum Pkcs12IntegrityMode - { - None = 1, - Password = 2, - PublicKey = 3, - Unknown = 0, - } - public sealed partial class Pkcs12KeyBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag - { - public Pkcs12KeyBag(System.ReadOnlyMemory pkcs8PrivateKey, bool skipCopy = false) : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } - public System.ReadOnlyMemory Pkcs8PrivateKey { get { throw null; } } - } - public abstract partial class Pkcs12SafeBag - { - protected Pkcs12SafeBag(string bagIdValue, System.ReadOnlyMemory encodedBagValue, bool skipCopy = false) { } - public System.Security.Cryptography.CryptographicAttributeObjectCollection Attributes { get { throw null; } } - public System.ReadOnlyMemory EncodedBagValue { get { throw null; } } - public byte[] Encode() { throw null; } - public System.Security.Cryptography.Oid GetBagId() { throw null; } - public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } - } - public sealed partial class Pkcs12SafeContents - { - public Pkcs12SafeContents() { } - public System.Security.Cryptography.Pkcs.Pkcs12ConfidentialityMode ConfidentialityMode { get { throw null; } } - public bool IsReadOnly { get { throw null; } } - public System.Security.Cryptography.Pkcs.Pkcs12CertBag AddCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { throw null; } - public System.Security.Cryptography.Pkcs.Pkcs12KeyBag AddKeyUnencrypted(System.Security.Cryptography.AsymmetricAlgorithm key) { throw null; } - public System.Security.Cryptography.Pkcs.Pkcs12SafeContentsBag AddNestedContents(System.Security.Cryptography.Pkcs.Pkcs12SafeContents safeContents) { throw null; } - public void AddSafeBag(System.Security.Cryptography.Pkcs.Pkcs12SafeBag safeBag) { } - public System.Security.Cryptography.Pkcs.Pkcs12SecretBag AddSecret(System.Security.Cryptography.Oid secretType, System.ReadOnlyMemory secretValue) { throw null; } - public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, byte[]? passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } - public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } - public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } - public System.Security.Cryptography.Pkcs.Pkcs12ShroudedKeyBag AddShroudedKey(System.Security.Cryptography.AsymmetricAlgorithm key, string? password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } - public void Decrypt(byte[]? passwordBytes) { } - public void Decrypt(System.ReadOnlySpan passwordBytes) { } - public void Decrypt(System.ReadOnlySpan password) { } - public void Decrypt(string? password) { } - public System.Collections.Generic.IEnumerable GetBags() { throw null; } - } - public sealed partial class Pkcs12SafeContentsBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag - { - internal Pkcs12SafeContentsBag() : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } - public System.Security.Cryptography.Pkcs.Pkcs12SafeContents? SafeContents { get { throw null; } } - } - public sealed partial class Pkcs12SecretBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag - { - internal Pkcs12SecretBag() : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } - public System.ReadOnlyMemory SecretValue { get { throw null; } } - public System.Security.Cryptography.Oid GetSecretType() { throw null; } - } - public sealed partial class Pkcs12ShroudedKeyBag : System.Security.Cryptography.Pkcs.Pkcs12SafeBag - { - public Pkcs12ShroudedKeyBag(System.ReadOnlyMemory encryptedPkcs8PrivateKey, bool skipCopy = false) : base (default(string), default(System.ReadOnlyMemory), default(bool)) { } - public System.ReadOnlyMemory EncryptedPkcs8PrivateKey { get { throw null; } } - } - public sealed partial class Pkcs8PrivateKeyInfo - { - public Pkcs8PrivateKeyInfo(System.Security.Cryptography.Oid algorithmId, System.ReadOnlyMemory? algorithmParameters, System.ReadOnlyMemory privateKey, bool skipCopies = false) { } - public System.Security.Cryptography.Oid AlgorithmId { get { throw null; } } - public System.ReadOnlyMemory? AlgorithmParameters { get { throw null; } } - public System.Security.Cryptography.CryptographicAttributeObjectCollection Attributes { get { throw null; } } - public System.ReadOnlyMemory PrivateKeyBytes { get { throw null; } } - public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo Create(System.Security.Cryptography.AsymmetricAlgorithm privateKey) { throw null; } - public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo Decode(System.ReadOnlyMemory source, out int bytesRead, bool skipCopy = false) { throw null; } - public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo DecryptAndDecode(System.ReadOnlySpan passwordBytes, System.ReadOnlyMemory source, out int bytesRead) { throw null; } - public static System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo DecryptAndDecode(System.ReadOnlySpan password, System.ReadOnlyMemory source, out int bytesRead) { throw null; } - public byte[] Encode() { throw null; } - public byte[] Encrypt(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } - public byte[] Encrypt(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters) { throw null; } - public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } - public bool TryEncrypt(System.ReadOnlySpan passwordBytes, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } - public bool TryEncrypt(System.ReadOnlySpan password, System.Security.Cryptography.PbeParameters pbeParameters, System.Span destination, out int bytesWritten) { throw null; } - } - public sealed partial class Pkcs9LocalKeyId : System.Security.Cryptography.Pkcs.Pkcs9AttributeObject - { - public Pkcs9LocalKeyId() { } - public Pkcs9LocalKeyId(byte[] keyId) { } - public Pkcs9LocalKeyId(System.ReadOnlySpan keyId) { } - public System.ReadOnlyMemory KeyId { get { throw null; } } - } - public sealed partial class Rfc3161TimestampRequest - { - internal Rfc3161TimestampRequest() { } - public bool HasExtensions { get { throw null; } } - public System.Security.Cryptography.Oid HashAlgorithmId { get { throw null; } } - public System.Security.Cryptography.Oid? RequestedPolicyId { get { throw null; } } - public bool RequestSignerCertificate { get { throw null; } } - public int Version { get { throw null; } } - public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromData(System.ReadOnlySpan data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } - public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromHash(System.ReadOnlyMemory hash, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } - public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromHash(System.ReadOnlyMemory hash, System.Security.Cryptography.Oid hashAlgorithmId, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } - public static System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest CreateFromSignerInfo(System.Security.Cryptography.Pkcs.SignerInfo signerInfo, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.Oid? requestedPolicyId = null, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), bool requestSignerCertificates = false, System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { throw null; } - public byte[] Encode() { throw null; } - public System.Security.Cryptography.X509Certificates.X509ExtensionCollection GetExtensions() { throw null; } - public System.ReadOnlyMemory GetMessageHash() { throw null; } - public System.ReadOnlyMemory? GetNonce() { throw null; } - public System.Security.Cryptography.Pkcs.Rfc3161TimestampToken ProcessResponse(System.ReadOnlyMemory responseBytes, out int bytesConsumed) { throw null; } - public static bool TryDecode(System.ReadOnlyMemory encodedBytes, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest? request, out int bytesConsumed) { throw null; } - public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } - } - public sealed partial class Rfc3161TimestampToken - { - internal Rfc3161TimestampToken() { } - public System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo TokenInfo { get { throw null; } } - public System.Security.Cryptography.Pkcs.SignedCms AsSignedCms() { throw null; } - public static bool TryDecode(System.ReadOnlyMemory encodedBytes, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.Pkcs.Rfc3161TimestampToken? token, out int bytesConsumed) { throw null; } - public bool VerifySignatureForData(System.ReadOnlySpan data, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } - public bool VerifySignatureForHash(System.ReadOnlySpan hash, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } - public bool VerifySignatureForHash(System.ReadOnlySpan hash, System.Security.Cryptography.Oid hashAlgorithmId, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } - public bool VerifySignatureForSignerInfo(System.Security.Cryptography.Pkcs.SignerInfo signerInfo, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.X509Certificates.X509Certificate2? signerCertificate, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? extraCandidates = null) { throw null; } - } - public sealed partial class Rfc3161TimestampTokenInfo - { - public Rfc3161TimestampTokenInfo(System.Security.Cryptography.Oid policyId, System.Security.Cryptography.Oid hashAlgorithmId, System.ReadOnlyMemory messageHash, System.ReadOnlyMemory serialNumber, System.DateTimeOffset timestamp, long? accuracyInMicroseconds = default(long?), bool isOrdering = false, System.ReadOnlyMemory? nonce = default(System.ReadOnlyMemory?), System.ReadOnlyMemory? timestampAuthorityName = default(System.ReadOnlyMemory?), System.Security.Cryptography.X509Certificates.X509ExtensionCollection? extensions = null) { } - public long? AccuracyInMicroseconds { get { throw null; } } - public bool HasExtensions { get { throw null; } } - public System.Security.Cryptography.Oid HashAlgorithmId { get { throw null; } } - public bool IsOrdering { get { throw null; } } - public System.Security.Cryptography.Oid PolicyId { get { throw null; } } - public System.DateTimeOffset Timestamp { get { throw null; } } - public int Version { get { throw null; } } - public byte[] Encode() { throw null; } - public System.Security.Cryptography.X509Certificates.X509ExtensionCollection GetExtensions() { throw null; } - public System.ReadOnlyMemory GetMessageHash() { throw null; } - public System.ReadOnlyMemory? GetNonce() { throw null; } - public System.ReadOnlyMemory GetSerialNumber() { throw null; } - public System.ReadOnlyMemory? GetTimestampAuthorityName() { throw null; } - public static bool TryDecode(System.ReadOnlyMemory encodedBytes, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Security.Cryptography.Pkcs.Rfc3161TimestampTokenInfo? timestampTokenInfo, out int bytesConsumed) { throw null; } - public bool TryEncode(System.Span destination, out int bytesWritten) { throw null; } - } - public sealed partial class SignedCms - { - public void AddCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } - public void Decode(System.ReadOnlySpan encodedMessage) { } - public void RemoveCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } - } - public sealed partial class SignerInfo - { - public System.Security.Cryptography.Oid SignatureAlgorithm { get { throw null; } } - public byte[] GetSignature() { throw null; } - public void AddUnsignedAttribute(System.Security.Cryptography.AsnEncodedData unsignedAttribute) { } - public void RemoveUnsignedAttribute(System.Security.Cryptography.AsnEncodedData unsignedAttribute) { } - } - public sealed partial class SubjectIdentifier - { - public bool MatchesCertificate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { throw null; } - } -} diff --git a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs b/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs deleted file mode 100644 index 03c846a15a7706..00000000000000 --- a/src/libraries/System.Threading.Channels/src/System/Threading/Channels/ChannelClosedException.netcoreapp.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#if NET8_0_OR_GREATER -using System.ComponentModel; -#endif -using System.Runtime.Serialization; - -namespace System.Threading.Channels -{ - /// Exception thrown when a channel is used after it's been closed. - [Serializable] - public partial class ChannelClosedException : InvalidOperationException - { - /// Initializes a new instance of the class with serialized data. - /// The object that holds the serialized object data. - /// The contextual information about the source or destination. -#if NET8_0_OR_GREATER - [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] - [EditorBrowsable(EditorBrowsableState.Never)] -#endif - protected ChannelClosedException(SerializationInfo info, StreamingContext context) : - base(info, context) - { - } - } -} diff --git a/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs b/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs deleted file mode 100644 index 8dcdd9ef11af4a..00000000000000 --- a/src/libraries/System.Threading.Tasks.Dataflow/ref/System.Threading.Tasks.Dataflow.netcoreapp.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ------------------------------------------------------------------------------ -// Changes to this file must follow the https://aka.ms/api-review process. -// ------------------------------------------------------------------------------ - -namespace System.Threading.Tasks.Dataflow -{ - public static partial class DataflowBlock - { - public static System.Collections.Generic.IAsyncEnumerable ReceiveAllAsync(this System.Threading.Tasks.Dataflow.IReceivableSourceBlock source, System.Threading.CancellationToken cancellationToken = default) { throw null; } - } - - public sealed partial class TransformManyBlock : System.Threading.Tasks.Dataflow.IDataflowBlock, System.Threading.Tasks.Dataflow.IPropagatorBlock, System.Threading.Tasks.Dataflow.IReceivableSourceBlock, System.Threading.Tasks.Dataflow.ISourceBlock, System.Threading.Tasks.Dataflow.ITargetBlock - { - public TransformManyBlock(System.Func> transform) { } - public TransformManyBlock(System.Func> transform, System.Threading.Tasks.Dataflow.ExecutionDataflowBlockOptions dataflowBlockOptions) { } - } -} diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs deleted file mode 100644 index 07dcd2a41e3a9b..00000000000000 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Base/DataflowBlock.IAsyncEnumerable.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace System.Threading.Tasks.Dataflow -{ - public static partial class DataflowBlock - { - /// Creates an that enables receiving all of the data from the source. - /// Specifies the type of data contained in the source. - /// The source from which to asynchronously receive. - /// The which may be used to cancel the receive operation. - /// The created async enumerable. - /// The is null (Nothing in Visual Basic). - public static IAsyncEnumerable ReceiveAllAsync(this IReceivableSourceBlock source, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(source); - - return Core(source, cancellationToken); - - static async IAsyncEnumerable Core(IReceivableSourceBlock source, [EnumeratorCancellation] CancellationToken cancellationToken) - { - while (await source.OutputAvailableAsync(cancellationToken).ConfigureAwait(false)) - { - while (source.TryReceive(out TOutput? item)) - { - yield return item; - } - } - } - } - } -} diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs b/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs deleted file mode 100644 index 393b7f2fb8e8c9..00000000000000 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/Blocks/TransformManyBlock.IAsyncEnumerable.cs +++ /dev/null @@ -1,267 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks.Dataflow.Internal; - -namespace System.Threading.Tasks.Dataflow -{ - public partial class TransformManyBlock - { - /// Initializes the with the specified function. - /// - /// The function to invoke with each data element received. All of the data from the returned - /// will be made available as output from this . - /// - /// The is . - public TransformManyBlock(Func> transform) : - this(transform, ExecutionDataflowBlockOptions.Default) - { - } - - /// Initializes the with the specified function and . - /// - /// The function to invoke with each data element received. All of the data from the returned - /// will be made available as output from this . - /// - /// The options with which to configure this . - /// The or is . - public TransformManyBlock(Func> transform, ExecutionDataflowBlockOptions dataflowBlockOptions) - { - ArgumentNullException.ThrowIfNull(transform); - - Initialize(messageWithId => - { - Task t = ProcessMessageAsync(transform, messageWithId); -#if DEBUG - // Task returned from ProcessMessageAsync is explicitly ignored. - // That function handles all exceptions. - t.ContinueWith(static t => Debug.Assert(t.IsCompletedSuccessfully), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); -#endif - }, dataflowBlockOptions, ref _source, ref _target, ref _reorderingBuffer, TargetCoreOptions.UsesAsyncCompletion); - } - - // Note: - // Enumerating the IAsyncEnumerable is done with ConfigureAwait(true), using the default behavior of - // paying attention to the current context/scheduler. This makes it so that the enumerable code runs on the target scheduler. - // For this to work correctly, there can't be any ConfigureAwait(false) in the same method prior to - // these await foreach loops, nor in the call chain prior to the method invocation. - - /// Processes the message with a user-provided transform function that returns an async enumerable. - /// The transform function to use to process the message. - /// The message to be processed. - private async Task ProcessMessageAsync(Func> transformFunction, KeyValuePair messageWithId) - { - try - { - // Run the user transform and store the results. - IAsyncEnumerable outputItems = transformFunction(messageWithId.Key); - await StoreOutputItemsAsync(messageWithId, outputItems).ConfigureAwait(false); - } - catch (Exception exc) - { - // Enumerating the user's collection failed. If this exception represents cancellation, - // swallow it rather than shutting down the block. - if (!Common.IsCooperativeCancellation(exc)) - { - // The exception was not for cancellation. We must add the exception before declining - // and signaling completion, as the exception is part of the operation, and the completion - // conditions depend on this. - Common.StoreDataflowMessageValueIntoExceptionData(exc, messageWithId.Key); - _target.Complete(exc, dropPendingMessages: true, storeExceptionEvenIfAlreadyCompleting: true, unwrapInnerExceptions: false); - } - } - finally - { - // Let the target know that one of the asynchronous operations it launched has completed. - _target.SignalOneAsyncMessageCompleted(); - } - } - - /// - /// Stores the output items, either into the reordering buffer or into the source half. - /// Ensures that the bounding count is correctly updated. - /// - /// The message with id. - /// The output items to be persisted. - private async Task StoreOutputItemsAsync( - KeyValuePair messageWithId, IAsyncEnumerable? outputItems) - { - // If there's a reordering buffer, pass the data along to it. - // The reordering buffer will handle all details, including bounding. - if (_reorderingBuffer is not null) - { - await StoreOutputItemsReorderedAsync(messageWithId.Value, outputItems).ConfigureAwait(false); - } - // Otherwise, output the data directly. - else if (outputItems is not null) - { - await StoreOutputItemsNonReorderedWithIterationAsync(outputItems).ConfigureAwait(false); - } - else if (_target.IsBounded) - { - // outputItems is null and there's no reordering buffer - // and we're bounding, so decrement the bounding count to - // signify that the input element we already accounted for - // produced no output - _target.ChangeBoundingCount(count: -1); - } - // else there's no reordering buffer, there are no output items, and we're not bounded, - // so there's nothing more to be done. - } - - /// Stores the next item using the reordering buffer. - /// The ID of the item. - /// The async enumerable. - private async Task StoreOutputItemsReorderedAsync(long id, IAsyncEnumerable? item) - { - Debug.Assert(_reorderingBuffer is not null, "Expected a reordering buffer"); - Debug.Assert(id != Common.INVALID_REORDERING_ID, "This ID should never have been handed out."); - - // Grab info about the transform - TargetCore target = _target; - bool isBounded = target.IsBounded; - - // Handle invalid items (null enumerables) by delegating to the base - if (item is null) - { - _reorderingBuffer.AddItem(id, null, false); - if (isBounded) - { - target.ChangeBoundingCount(count: -1); - } - return; - } - - // By this point, either we're not the next item, in which case we need to make a copy of the - // data and store it, or we are the next item and can store it immediately but we need to enumerate - // the items and store them individually because we don't want to enumerate while holding a lock. - List? itemCopy = null; - try - { - // If this is the next item, we can output it now. - if (_reorderingBuffer.IsNext(id)) - { - await StoreOutputItemsNonReorderedWithIterationAsync(item).ConfigureAwait(false); - // here itemCopy remains null, so that base.AddItem will finish our interactions with the reordering buffer - } - else - { - // We're not the next item, and we're not trusted, so copy the data into a list. - // We need to enumerate outside of the lock in the base class. - int itemCount = 0; - try - { - itemCopy = new List(); - await foreach (TOutput element in item.ConfigureAwait(true)) - { - itemCopy.Add(element); - } - itemCount = itemCopy.Count; - } - finally - { - // If we're here successfully, then itemCount is the number of output items - // we actually received, and we should update the bounding count with it. - // If we're here because ToList threw an exception, then itemCount will be 0, - // and we still need to update the bounding count with this in order to counteract - // the increased bounding count for the corresponding input. - if (isBounded) - { - UpdateBoundingCountWithOutputCount(count: itemCount); - } - } - } - // else if the item isn't valid, the finally block will see itemCopy as null and output invalid - } - finally - { - // Tell the base reordering buffer that we're done. If we already output - // all of the data, itemCopy will be null, and we just pass down the invalid item. - // If we haven't, pass down the real thing. We do this even in the case of an exception, - // in which case this will be a dummy element. - _reorderingBuffer.AddItem(id, itemCopy, itemIsValid: itemCopy is not null); - } - } - - /// - /// Stores the untrusted async enumerable into the source core. - /// This method does not go through the reordering buffer. - /// - /// The untrusted enumerable. - private async Task StoreOutputItemsNonReorderedWithIterationAsync(IAsyncEnumerable outputItems) - { - // The _source we're adding to isn't thread-safe, so we need to determine - // whether we need to lock. If the block is configured with a max degree - // of parallelism of 1, then only one transform can run at a time, and so - // we don't need to lock. Similarly, if there's a reordering buffer, then - // it guarantees that we're invoked serially, and we don't need to lock. - bool isSerial = - _target.DataflowBlockOptions.MaxDegreeOfParallelism == 1 || - _reorderingBuffer is not null; - - // If we're bounding, we need to increment the bounded count - // for each individual item as we enumerate it. - if (_target.IsBounded) - { - // When the input item that generated this - // output was loaded, we incremented the bounding count. If it only - // output a single a item, then we don't need to touch the bounding count. - // Otherwise, we need to adjust the bounding count accordingly. - bool outputFirstItem = false; - try - { - await foreach (TOutput item in outputItems.ConfigureAwait(true)) - { - if (outputFirstItem) - { - _target.ChangeBoundingCount(count: 1); - } - outputFirstItem = true; - - if (isSerial) - { - _source.AddMessage(item); - } - else - { - lock (ParallelSourceLock) // don't hold lock while enumerating - { - _source.AddMessage(item); - } - } - } - } - finally - { - if (!outputFirstItem) - { - _target.ChangeBoundingCount(count: -1); - } - } - } - // If we're not bounding, just output each individual item. - else - { - if (isSerial) - { - await foreach (TOutput item in outputItems.ConfigureAwait(true)) - { - _source.AddMessage(item); - } - } - else - { - await foreach (TOutput item in outputItems.ConfigureAwait(true)) - { - lock (ParallelSourceLock) // don't hold lock while enumerating - { - _source.AddMessage(item); - } - } - } - } - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs deleted file mode 100644 index cc3f606fe6273d..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Browser.Threads.Mono.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Threading; - -internal sealed partial class PortableThreadPool -{ - private static partial class WorkerThread - { - private static bool IsIOPending => false; - } - - private struct CpuUtilizationReader - { -#pragma warning disable CA1822 - public double CurrentUtilization => 0.0; // FIXME: can we do better -#pragma warning restore CA1822 - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs deleted file mode 100644 index 7933e49db422b9..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Browser.Threads.Mono.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Threading -{ - public static partial class ThreadPool - { - // Indicates that the threadpool should yield the thread from the dispatch loop to the - // runtime periodically. We use this to return back to the JS event loop so that the JS - // event queue can be drained - internal static bool YieldFromDispatchLoop => true; - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs deleted file mode 100644 index 0d082fd863cb95..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPool.Wasi.Mono.cs +++ /dev/null @@ -1,138 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Diagnostics.CodeAnalysis; -using Microsoft.Win32.SafeHandles; - -namespace System.Threading -{ -#if FEATURE_WASM_MANAGED_THREADS -#error when compiled with FEATURE_WASM_MANAGED_THREADS -#endif - [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] - public sealed class RegisteredWaitHandle : MarshalByRefObject - { - internal RegisteredWaitHandle() - { - } - -#pragma warning disable CA1822 // Mark members as static - internal bool Repeating => false; -#pragma warning restore CA1822 - - public bool Unregister(WaitHandle? waitObject) - { - throw new PlatformNotSupportedException(); - } - } - - public static partial class ThreadPool - { - // Indicates whether the thread pool should yield the thread from the dispatch loop to the runtime periodically so that - // the runtime may use the thread for processing other work - internal static bool YieldFromDispatchLoop => true; - - private const bool IsWorkerTrackingEnabledInConfig = false; - - public static bool SetMaxThreads(int workerThreads, int completionPortThreads) - { - if (workerThreads == 1 && completionPortThreads == 1) - return true; - return false; - } - - public static void GetMaxThreads(out int workerThreads, out int completionPortThreads) - { - workerThreads = 1; - completionPortThreads = 1; - } - - public static bool SetMinThreads(int workerThreads, int completionPortThreads) - { - if (workerThreads == 1 && completionPortThreads == 1) - return true; - return false; - } - - public static void GetMinThreads(out int workerThreads, out int completionPortThreads) - { - workerThreads = 1; - completionPortThreads = 1; - } - - public static void GetAvailableThreads(out int workerThreads, out int completionPortThreads) - { - workerThreads = 1; - completionPortThreads = 1; - } - - public static int ThreadCount => 1; - - public static long CompletedWorkItemCount => 0; - - internal static unsafe void RequestWorkerThread() - { - } - - internal static void NotifyWorkItemProgress() - { - } - - internal static bool NotifyThreadBlocked() => false; - - internal static void NotifyThreadUnblocked() - { - } - - internal static object? GetOrCreateThreadLocalCompletionCountObject() => null; - - internal static bool NotifyWorkItemComplete(object? _1, int _2) => true; - - private static RegisteredWaitHandle RegisterWaitForSingleObject( - WaitHandle? waitObject, - WaitOrTimerCallback? callBack, - object? state, - uint millisecondsTimeOutInterval, - bool executeOnlyOnce, - bool flowExecutionContext) - { - throw new PlatformNotSupportedException(); - } - - private static unsafe void NativeOverlappedCallback(nint overlappedPtr) => - IOCompletionCallbackHelper.PerformSingleIOCompletionCallback(0, 0, (NativeOverlapped*)overlappedPtr); - - [CLSCompliant(false)] - [SupportedOSPlatform("windows")] - public static unsafe bool UnsafeQueueNativeOverlapped(NativeOverlapped* overlapped) - { - throw new PlatformNotSupportedException(); - } - - [Obsolete("ThreadPool.BindHandle(IntPtr) has been deprecated. Use ThreadPool.BindHandle(SafeHandle) instead.")] - [SupportedOSPlatform("windows")] - public static bool BindHandle(IntPtr osHandle) - { - throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle - } - - [SupportedOSPlatform("windows")] - public static bool BindHandle(SafeHandle osHandle) - { - throw new PlatformNotSupportedException(SR.Arg_PlatformNotSupported); // Replaced by ThreadPoolBoundHandle.BindHandle - } - -#pragma warning disable IDE0060 - [Conditional("unnecessary")] - internal static void ReportThreadStatus(bool isWorking) - { - - } -#pragma warning restore IDE0060 - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs deleted file mode 100644 index c1bcfa75b8c2f0..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/ThreadPoolBoundHandle.Browser.Threads.Mono.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; - -namespace System.Threading -{ - public sealed partial class ThreadPoolBoundHandle : IDisposable - { - private static ThreadPoolBoundHandle BindHandleCore(SafeHandle handle) - { - throw new PlatformNotSupportedException(SR.PlatformNotSupported_OverlappedIO); - } - } -} diff --git a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs deleted file mode 100644 index d5af01ca588caf..00000000000000 --- a/src/mono/System.Private.CoreLib/src/System/Threading/TimerQueue.Wasi.Mono.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; -using WasiPollWorld.wit.imports.wasi.clocks.v0_2_0; - -namespace System.Threading -{ -#if FEATURE_WASM_MANAGED_THREADS -#error when compiled with FEATURE_WASM_MANAGED_THREADS, we will use TimerQueue.Portable.cs -#endif - // - // Wasi implementation of Timer, single-threaded, on top of pollable and wasi:clocks - // Based on TimerQueue.Portable.cs - // Not thread safe - // - internal sealed partial class TimerQueue - { - private static long TickCount64 => Environment.TickCount64; - private static List? s_scheduledTimers; - private static List? s_scheduledTimersToFire; - private static long s_shortestDueTimeMs = long.MaxValue; - - // this means that it's in the s_scheduledTimers collection, not that it's the one which would run on the next TimeoutCallback - private bool _isScheduled; - private long _scheduledDueTimeMs; - private TimerQueue(int _) - { - } - - private static void TimerHandler(object _) - { - try - { - s_shortestDueTimeMs = long.MaxValue; - - long currentTimeMs = TickCount64; - SetNextTimer(PumpTimerQueue(currentTimeMs), currentTimeMs); - } - catch (Exception e) - { - Environment.FailFast("TimerQueue.TimerHandler failed", e); - } - } - - // this is called with shortest of timers scheduled on the particular TimerQueue - private bool SetTimer(uint actualDuration) - { - Debug.Assert((int)actualDuration >= 0); - long currentTimeMs = TickCount64; - if (!_isScheduled) - { - s_scheduledTimers ??= new List(Instances.Length); - s_scheduledTimersToFire ??= new List(Instances.Length); - s_scheduledTimers.Add(this); - _isScheduled = true; - } - - _scheduledDueTimeMs = currentTimeMs + (int)actualDuration; - - SetNextTimer(ShortestDueTime(), currentTimeMs); - - return true; - } - - // shortest time of all TimerQueues - private static unsafe void SetNextTimer(long shortestDueTimeMs, long currentTimeMs) - { - if (shortestDueTimeMs == long.MaxValue) - { - return; - } - - // this also covers s_shortestDueTimeMs = long.MaxValue when none is scheduled - if (s_shortestDueTimeMs > shortestDueTimeMs) - { - s_shortestDueTimeMs = shortestDueTimeMs; - ulong shortestWaitMs = (ulong)Math.Max((long)(shortestDueTimeMs - currentTimeMs), 0); - - // `SubscribeDuration` expects nanoseconds: - var pollable = MonotonicClockInterop.SubscribeDuration(shortestWaitMs * 1000 * 1000); - Task task = WasiEventLoop.RegisterWasiPollable(pollable, true, CancellationToken.None); - task.ContinueWith(TimerHandler, TaskScheduler.Default); - } - } - - private static long ShortestDueTime() - { - if (s_scheduledTimers == null) - { - return long.MaxValue; - } - - long shortestDueTimeMs = long.MaxValue; - var timers = s_scheduledTimers!; - for (int i = timers.Count - 1; i >= 0; --i) - { - TimerQueue timer = timers[i]; - if (timer._scheduledDueTimeMs < shortestDueTimeMs) - { - shortestDueTimeMs = timer._scheduledDueTimeMs; - } - } - - return shortestDueTimeMs; - } - - private static long PumpTimerQueue(long currentTimeMs) - { - if (s_scheduledTimersToFire == null) - { - return ShortestDueTime(); - } - - List timersToFire = s_scheduledTimersToFire!; - List timers; - timers = s_scheduledTimers!; - long shortestDueTimeMs = long.MaxValue; - for (int i = timers.Count - 1; i >= 0; --i) - { - TimerQueue timer = timers[i]; - long waitDurationMs = timer._scheduledDueTimeMs - currentTimeMs; - if (waitDurationMs <= 0) - { - timer._isScheduled = false; - timersToFire.Add(timer); - - int lastIndex = timers.Count - 1; - if (i != lastIndex) - { - timers[i] = timers[lastIndex]; - } - timers.RemoveAt(lastIndex); - continue; - } - - if (timer._scheduledDueTimeMs < shortestDueTimeMs) - { - shortestDueTimeMs = timer._scheduledDueTimeMs; - } - } - - if (timersToFire.Count > 0) - { - foreach (TimerQueue timerToFire in timersToFire) - { - timerToFire.FireNextTimers(); - } - timersToFire.Clear(); - } - - return shortestDueTimeMs; - } - } -} From 8adc8c025793510ac85cc4f8ce2157a4175d21f6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 11:46:38 -0700 Subject: [PATCH 30/33] Remove export that no longer exists --- src/coreclr/dlls/mscordac/mscordac_unixexports.src | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 99c760416274b9..3c464ce4cb36b9 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -104,7 +104,6 @@ nativeStringResourceTable_mscorrc #RaiseException #RaiseFailFastException #ReadFile -#ReleaseMutex #ReleaseSemaphore #ResetEvent #ResumeThread From 95a1ed3f5382312a7c86990a7362292d78ffcc69 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 21:19:47 +0000 Subject: [PATCH 31/33] Interrupt the wait subsystem when Interrupt is called. --- .../src/System/Threading/Thread.CoreCLR.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 0a0eed3748dbef..18be6b915084b7 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -410,6 +410,9 @@ public void DisableComObjectEagerCleanup() { } /// public void Interrupt() { +#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI + WaitSubsystem.Interrupt(this); +#endif Interrupt(GetNativeHandle()); GC.KeepAlive(this); } From 6595959ee58c8d87ca082c15369ce4226be2c69c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 23:21:47 +0000 Subject: [PATCH 32/33] Handle the standard .NET-owned thread case (apparently the "detached" logic is only for externally-attached threads) --- .../src/System/Threading/Thread.CoreCLR.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 18be6b915084b7..0680b416e21f43 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -110,6 +110,11 @@ private void StartCallback() _startHelper = null; startHelper.Run(); + + // When this thread is about to exit, inform any subsystems that need to know. + // For external threads that have been attached to the runtime, we'll call this + // after the thread has been detached as it won't come through this path. + OnThreadExiting(); } // Max iterations to be done in SpinWait without switching GC modes. From a563d38fd6ab2c8778b95191dbb1969fa77968cb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 30 Oct 2025 23:43:49 +0000 Subject: [PATCH 33/33] Fix invalidnames test now that the implementations are unified. --- src/libraries/System.Threading/tests/MutexTests.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libraries/System.Threading/tests/MutexTests.cs b/src/libraries/System.Threading/tests/MutexTests.cs index 7ce7d878d09005..a87522ec3211bd 100644 --- a/src/libraries/System.Threading/tests/MutexTests.cs +++ b/src/libraries/System.Threading/tests/MutexTests.cs @@ -232,13 +232,6 @@ public void Ctor_InvalidNames_Unix() { Assert.Throws(() => new Mutex("Foo/Bar", options: default)); Assert.Throws(() => new Mutex("Global\\Foo/Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); - if (PlatformDetection.IsCoreCLR) - { - AssertExtensions.Throws("name", null, () => new Mutex(new string('a', 1000), options: default)); - AssertExtensions.Throws("name", null, () => new Mutex("Foo\\Bar", options: default)); - AssertExtensions.Throws("name", null, () => new Mutex("Foo\\Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); - Assert.Throws(() => new Mutex("Global\\Foo\\Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); - } } [Theory]