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 c12d6c854ac0c0..b089e0b436671b 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 @@ - @@ -290,8 +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/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..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 @@ -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 @@ -108,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; @@ -298,6 +294,28 @@ public ThreadState ThreadState [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")] private static partial int GetThreadState(ThreadHandle t); + 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); + /// /// An unstarted thread can be marked to indicate that it will host a /// single-threaded or multi-threaded apartment. @@ -347,6 +365,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; @@ -364,6 +385,8 @@ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwO return true; } + + internal const bool ReentrantWaitsEnabled = false; #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT #if FEATURE_COMINTEROP @@ -513,6 +536,45 @@ 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 + +#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 + // the thread. + _waitInfo?.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 { @@ -521,12 +583,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.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.cs deleted file mode 100644 index a63096dc84941f..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/WaitHandle.CoreCLR.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/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..7400c2582c83ad --- /dev/null +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.cpp @@ -0,0 +1,341 @@ +// 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 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/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/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/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index a9f862c684ec5b..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)] @@ -441,19 +431,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/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/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..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 @@ -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() { @@ -526,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/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 766d4cdc9f7c8c..0b9810fe932bdc 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -742,65 +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); - -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 -ReleaseMutex( - IN HANDLE hMutex); - PALIMPORT DWORD PALAPI @@ -906,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) @@ -920,13 +859,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/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index 1395013eb59601..f2ff28312ecdb1 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -198,10 +198,8 @@ 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 synchmgr/synchcontrollers.cpp synchmgr/synchmanager.cpp synchmgr/wait.cpp 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 7e606980dd3434..8e57767be3a07a 100644 --- a/src/coreclr/pal/src/include/pal/corunix.hpp +++ b/src/coreclr/pal/src/include/pal/corunix.hpp @@ -159,8 +159,6 @@ namespace CorUnix { otiAutoResetEvent = 0, otiManualResetEvent, - otiMutex, - otiNamedMutex, otiSemaphore, otiFile, otiFileMapping, @@ -206,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 { @@ -234,13 +228,6 @@ namespace CorUnix ThreadReleaseNotApplicable }; - enum OwnershipSemantics - { - OwnershipTracked, - NoOwner, - OwnershipNotApplicable - }; - private: // @@ -261,7 +248,6 @@ namespace CorUnix SynchronizationSupport m_eSynchronizationSupport; SignalingSemantics m_eSignalingSemantics; ThreadReleaseSemantics m_eThreadReleaseSemantics; - OwnershipSemantics m_eOwnershipSemantics; public: @@ -275,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), @@ -288,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; }; @@ -400,14 +384,6 @@ namespace CorUnix { return m_eThreadReleaseSemantics; }; - - OwnershipSemantics - GetOwnershipSemantics( - void - ) - { - return m_eOwnershipSemantics; - }; }; class CAllowedObjectTypes @@ -531,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( @@ -606,8 +552,7 @@ namespace CorUnix virtual PAL_ERROR CanThreadWaitWithoutBlocking( - bool *pfCanWaitWithoutBlocking, // OUT - bool *pfAbandoned + bool *pfCanWaitWithoutBlocking // OUT ) = 0; virtual @@ -626,8 +571,7 @@ namespace CorUnix RegisterWaitingThread( WaitType eWaitType, DWORD dwIndex, - bool fAltertable, - bool fPrioritize + bool fAltertable ) = 0; // @@ -917,7 +861,6 @@ namespace CorUnix { WaitSucceeded, Alerted, - MutexAbandoned, WaitTimeout, WaitFailed }; @@ -948,13 +891,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/mutex.hpp b/src/coreclr/pal/src/include/pal/mutex.hpp deleted file mode 100644 index 9ff179ca43d180..00000000000000 --- a/src/coreclr/pal/src/include/pal/mutex.hpp +++ /dev/null @@ -1,262 +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 "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 - ); - - PAL_ERROR - InternalReleaseMutex( - CPalThread *pThread, - HANDLE hMutex - ); - - PAL_ERROR - InternalOpenMutex( - SharedMemorySystemCallErrors *errors, - CPalThread *pThread, - LPCSTR lpName, - BOOL bCurrentUserOnly, - HANDLE *phMutex - ); - -} - -#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); - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// 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 9c2824c1c366b7..00000000000000 --- a/src/coreclr/pal/src/include/pal/sharedmemory.h +++ /dev/null @@ -1,313 +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 - -// 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(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..407df2b52b510c 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 @@ -38,8 +37,7 @@ namespace CorUnix CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds, - BOOL bAlertable, - BOOL bPrioritize = FALSE); + BOOL bAlertable); DWORD InternalSignalObjectAndWait( CPalThread *thread, @@ -68,7 +66,6 @@ namespace CorUnix class CSynchData; typedef struct _WaitingThreadsListNode * PWaitingThreadsListNode; - typedef struct _OwnedObjectsListNode * POwnedObjectsListNode; typedef struct _ThreadApcInfoNode * PThreadApcInfoNode; typedef struct _ThreadWaitInfo @@ -113,8 +110,6 @@ namespace CorUnix Volatile m_lLocalSynchLockCount; LIST_ENTRY m_leOwnedObjsList; - NamedMutexProcessData *m_ownedNamedMutexListHead; - ThreadNativeWaitData m_tnwdNativeData; ThreadWaitInfo m_twiWaitInfo; @@ -160,20 +155,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); - - 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/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/init/pal.cpp b/src/coreclr/pal/src/init/pal.cpp index d06cd1a4963fe2..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(STRING_LENGTH(TEMP_DIRECTORY_PATH) + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH); -} 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/sharedmemory/sharedmemory.cpp b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp deleted file mode 100644 index 71873fbec82579..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(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/synchcontrollers.cpp b/src/coreclr/pal/src/synchmgr/synchcontrollers.cpp index 614944198d46bd..945268b31f49a6 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()) { @@ -249,8 +247,7 @@ namespace CorUnix PAL_ERROR CSynchWaitController::RegisterWaitingThread( WaitType wtWaitType, DWORD dwIndex, - bool fAlertable, - bool fPrioritize) + bool fAlertable) { VALIDATEOBJECT(m_psdSynchData); @@ -368,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++; @@ -561,117 +558,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 +672,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 +681,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); - - if (CObjectType::OwnershipTracked == osOwnershipSemantics && - 0 < GetOwnershipCount()) - { - // We are rentering an object with ownership: we need to skip - // the object unsignaling - fReenteringObjWithOwnership = true; - } + "[SignalingSemantics=%u " + "ThreadReleaseSemantics=%u]\n", + ssSignalingSemantics, + trsThreadReleaseSemantics); - 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 +721,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,16 +772,6 @@ namespace CorUnix } } - _ASSERT_MSG(CObjectType::OwnershipTracked != - GetObjectType()->GetOwnershipSemantics() || - 0 == GetOwnershipCount() || 0 == GetSignalCount(), - "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; } @@ -1042,31 +846,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) { @@ -1093,7 +872,7 @@ namespace CorUnix palErr = CPalSynchronizationManager::WakeUpLocalThread( pthrCurrent, ptwiWaitInfo->pthrOwner, - fAbandoned ? MutexAbandoned : WaitSucceeded, + WaitSucceeded, dwObjIdx); if (NO_ERROR != palErr) @@ -1176,26 +955,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) { @@ -1222,7 +981,7 @@ namespace CorUnix palErr = CPalSynchronizationManager::WakeUpLocalThread( pthrCurrent, ptwiWaitInfo->pthrOwner, - fAbandoned ? MutexAbandoned : WaitSucceeded, + WaitSucceeded, dwObjIdx); if (NO_ERROR != palErr) @@ -1267,7 +1026,7 @@ namespace CorUnix WaitCompletionState CSynchData::IsRestOfWaitAllSatisfied( WaitingThreadsListNode * pwtlnNode) { - int iSignaledOrOwnedObjCount = 0; + int iSignaledObjCount = 0; int iTgtCount = 0; int i; WaitCompletionState wcsWaitCompletionState = WaitIsNotSatisfied; @@ -1294,7 +1053,6 @@ namespace CorUnix { WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i]; bool fRetVal; - bool fIsAbandoned; VALIDATEOBJECT(pwtlnItem); @@ -1311,17 +1069,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 { @@ -1329,7 +1086,7 @@ namespace CorUnix } } - if (iSignaledOrOwnedObjCount < iTgtCount) + if (iSignaledObjCount < iTgtCount) { wcsWaitCompletionState = WaitIsNotSatisfied; } @@ -1343,145 +1100,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 @@ -1494,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.cpp b/src/coreclr/pal/src/synchmgr/synchmanager.cpp index 0665df179caa04..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,128 +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(); - - // 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); - - // 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 (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 - // 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); - - if (abandonNamedMutexes) - { - SharedMemoryManager::ReleaseCreationDeletionProcessLock(); - } - - DiscardAllPendingAPCs(pthrCurrent, pthrTarget); - - return palErr; - } - /*++ Method: CPalSynchronizationManager::GetSynchWaitControllersForObjects @@ -2989,8 +2865,7 @@ namespace CorUnix CThreadSynchronizationInfo::CThreadSynchronizationInfo() : m_tsThreadState(TS_IDLE), m_shridWaitAwakened(NULL), - m_lLocalSynchLockCount(0), - m_ownedNamedMutexListHead(nullptr) + m_lLocalSynchLockCount(0) { InitializeListHead(&m_leOwnedObjsList); @@ -3190,133 +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; - } - - 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/synchmanager.hpp b/src/coreclr/pal/src/synchmgr/synchmanager.hpp index 7d86ab6fb12d4d..1b884ab478f722 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,14 +173,13 @@ namespace CorUnix LONG Release(CPalThread * pthrCurrent); bool CanWaiterWaitWithoutBlocking( - CPalThread * pWaiterThread, - bool * pfAbandoned); + CPalThread * pWaiterThread); PAL_ERROR ReleaseWaiterWithoutBlocking( CPalThread * pthrCurrent, CPalThread * pthrTarget); - void WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode, bool fPrioritize); + void WaiterEnqueue(WaitingThreadsListNode * pwtlnNewNode); // Object Type accessor methods CObjectType * GetObjectType(void) @@ -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,16 +359,14 @@ namespace CorUnix // ISynchWaitController methods // virtual PAL_ERROR CanThreadWaitWithoutBlocking( - bool * pfCanWaitWithoutBlocking, - bool * pfAbandoned); + bool * pfCanWaitWithoutBlocking); virtual PAL_ERROR ReleaseWaitingThreadWithoutBlocking(void); virtual PAL_ERROR RegisterWaitingThread( WaitType wtWaitType, DWORD dwIndex, - bool fAlertable, - bool fPrioritize); + bool fAlertable); virtual void ReleaseController(void); @@ -452,8 +390,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 +406,6 @@ namespace CorUnix typedef CSynchCache CWaitingThreadsListNodeCache; typedef CSHRSynchCache CSHRWaitingThreadsListNodeCache; typedef CSynchCache CThreadApcInfoNodeCache; - typedef CSynchCache COwnedObjectsListNodeCache; private: // types @@ -510,7 +445,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 +482,6 @@ namespace CorUnix CWaitingThreadsListNodeCache m_cacheWTListNodes; CSHRWaitingThreadsListNodeCache m_cacheSHRWTListNodes; CThreadApcInfoNodeCache m_cacheThreadApcInfoNodes; - COwnedObjectsListNodeCache m_cacheOwnedObjectsListNodes; // static methods static PAL_ERROR Initialize(); @@ -715,19 +648,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 +659,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 495d86b1cd5cd3..d0907ee334629f 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,8 +38,6 @@ static PalObjectTypeId sg_rgWaitObjectsIds[] = { otiAutoResetEvent, otiManualResetEvent, - otiMutex, - otiNamedMutex, otiSemaphore, otiProcess, otiThread @@ -52,8 +49,6 @@ static PalObjectTypeId sg_rgSignalableObjectIds[] = { otiAutoResetEvent, otiManualResetEvent, - otiMutex, - otiNamedMutex, otiSemaphore }; static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, ARRAY_SIZE(sg_rgSignalableObjectIds)); @@ -85,36 +80,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 @@ -356,14 +321,12 @@ 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; int i, iSignaledObjCount, iSignaledObjIndex = -1; bool fWAll = (bool)bWaitAll, fNeedToBlock = false; - bool fAbandoned = false; WaitType wtWaitType; IPalObject * pIPalObjStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL }; @@ -417,55 +380,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) @@ -528,8 +442,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 " @@ -537,10 +451,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( pThread->SetLastError(ERROR_INTERNAL_ERROR); goto WFMOExIntReleaseControllers; } - if (fWaitObjectAbandoned) - { - fAbandoned = true; - } if (fValue) { iSignaledObjCount++; @@ -589,7 +499,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( } } - dwRet = (fAbandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0); + dwRet = WAIT_OBJECT_0; } else if (0 == dwMilliseconds) { @@ -605,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 " @@ -655,9 +564,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; @@ -679,14 +585,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) { @@ -760,11 +666,6 @@ DWORD CorUnix::InternalSignalObjectAndWait( palError = InternalSetEvent(thread, hObjectToSignal, true /* fSetEvent */); break; - case otiMutex: - case otiNamedMutex: - palError = InternalReleaseMutex(thread, hObjectToSignal); - break; - case otiSemaphore: palError = InternalReleaseSemaphore(thread, hObjectToSignal, 1 /* lReleaseCount */, nullptr /* lpPreviousCount */); break; @@ -873,9 +774,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/mutex.cpp b/src/coreclr/pal/src/synchobj/mutex.cpp deleted file mode 100644 index 4e5f7f8852106a..00000000000000 --- a/src/coreclr/pal/src/synchobj/mutex.cpp +++ /dev/null @@ -1,1730 +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); - -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( - IN BOOL bInitialOwner, - IN LPCWSTR lpName, - IN BOOL bCurrentUserOnly, - IN LPSTR lpSystemCallErrors, - IN DWORD dwSystemCallErrorsBufferSize) -{ - 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); - - 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, - 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 - // where last error is set to ERROR_ALREADY_EXISTS on - // entry to the function - // - - pthr->SetLastError(palError); - - LOGEXIT("PAL_CreateMutexW returns HANDLE %p\n", hMutex); - PERF_EXIT(PAL_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( - SharedMemorySystemCallErrors *errors, - CPalThread *pthr, - LPSECURITY_ATTRIBUTES lpMutexAttributes, - BOOL bInitialOwner, - LPCSTR lpName, - BOOL bCurrentUserOnly, - HANDLE *phMutex - ) -{ - CObjectAttributes oa(nullptr, lpMutexAttributes); - PAL_ERROR palError = NO_ERROR; - IPalObject *pobjMutex = NULL; - 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", - 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, - &oa, - &pobjMutex - ); - - if (NO_ERROR != palError) - { - 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); - } - - pssc->ReleaseController(); - - if (NO_ERROR != palError) - { - ASSERT("Unable to set initial mutex state (%d)\n", palError); - goto InternalCreateMutexExit; - } - } - else - { - SharedMemoryProcessDataHeader *processDataHeader; - try - { - processDataHeader = - NamedMutexProcessData::CreateOrOpen(errors, lpName, !!bCurrentUserOnly, !!bInitialOwner, &createdNamedMutex); - } - catch (SharedMemoryException ex) - { - palError = ex.GetErrorCode(); - goto InternalCreateMutexExit; - } - - SharedMemoryProcessDataHeader::PalObject_SetProcessDataHeader(pobjMutex, processDataHeader); - } - - palError = g_pObjectManager->RegisterObject( - pthr, - pobjMutex, - aot, - &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 InternalCreateMutexExit; - } - - pobjRegisteredMutex->ReleaseReference(pthr); - pobjRegisteredMutex = nullptr; - - *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); - _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, - &aotAnyMutex, - &pobjMutex - ); - - if (NO_ERROR != palError) - { - ERROR("Unable to obtain object for handle %p (error %d)!\n", hMutex, palError); - goto InternalReleaseMutexExit; - } - - objectTypeId = pobjMutex->GetObjectType()->GetId(); - if (objectTypeId == otiMutex) - { - 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; - } - } - else - { - _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; - } - } - -InternalReleaseMutexExit: - - if (NULL != pssc) - { - pssc->ReleaseController(); - } - - if (NULL != pobjMutex) - { - pobjMutex->ReleaseReference(pthr); - } - - LOGEXIT("InternalReleaseMutex returns %i\n", palError); - - 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) -{ - 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; - } - - if (lpSystemCallErrors != nullptr) - { - lpSystemCallErrors[0] = '\0'; - } - - { - 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 OpenMutexWExit; - } - - 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 - ) -{ - 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); -} - -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(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/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 2af7244fcaa8c1..da1378347c1803 100644 --- a/src/coreclr/pal/src/thread/process.cpp +++ b/src/coreclr/pal/src/thread/process.cpp @@ -130,8 +130,7 @@ CObjectType CorUnix::otProcess( NULL, // No process local data cleanup routine CObjectType::WaitableObject, CObjectType::SingleTransitionObject, - CObjectType::ThreadReleaseHasNoSideEffects, - CObjectType::NoOwner + CObjectType::ThreadReleaseHasNoSideEffects ); CAllowedObjectTypes aotProcess(otiProcess); @@ -174,7 +173,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/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp index ec6920f922cade..7acf3439735b6e 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" @@ -104,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); @@ -797,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/src/thread/threadsusp.cpp b/src/coreclr/pal/src/thread/threadsusp.cpp index 867f46b3fa38f2..ec0dc9c5b92594 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,8 +49,56 @@ 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; + +namespace +{ + LONG g_ssSuspensionLock = 0; +} +#endif + +#define SYNCSPINLOCK_F_ASYMMETRIC 1 + +#define SPINLOCKInit(lock) (*(lock) = 0) + +namespace +{ + /* 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. + } +} /*++ Function: diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index a6338791711233..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 @@ -415,13 +401,10 @@ 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 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 @@ -433,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 @@ -455,17 +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/WaitForMultipleObjectsEx/test6/child6.cpp - threading/WaitForMultipleObjectsEx/test6/test6.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 ee5ba036ce7a31..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 @@ -312,11 +305,9 @@ 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 -threading/OpenEventW/test4/paltest_openeventw_test4 threading/OpenEventW/test5/paltest_openeventw_test5 threading/OpenProcess/test1/paltest_openprocess_test1 threading/QueryThreadCycleTime/test1/paltest_querythreadcycletime_test1 @@ -327,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 @@ -350,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 b55a93735f16f2..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 @@ -269,7 +265,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 @@ -277,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 @@ -294,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 TestNamedMutex_CreateMutexW_ReleaseMutex_test2(const char *szMutexName); -DWORD NamedMutexThread_CreateMutexW_ReleaseMutex_test2(LPVOID lpParam); -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 ); - } - - - /* - * 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 - */ - - 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 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 - * - * 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/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/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/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/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/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 - -/*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;itrue customattribute.cpp @@ -415,7 +414,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 e98f7ec66de23a..206408a986ada1 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 @@ -601,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: @@ -831,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) { @@ -907,3 +922,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 = alertable ? WaitMode_Alertable : WaitMode_None; + retVal = pThread->DoReentrantWaitAny(count, handles, 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 5c78eb8be2039f..2582209d894d65 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -56,6 +56,10 @@ 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); +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); @@ -67,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/comwaithandle.cpp b/src/coreclr/vm/comwaithandle.cpp deleted file mode 100644 index e80675cc03febf..00000000000000 --- a/src/coreclr/vm/comwaithandle.cpp +++ /dev/null @@ -1,103 +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; -} - -#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 deleted file mode 100644 index ac605389129138..00000000000000 --- a/src/coreclr/vm/comwaithandle.h +++ /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. - - -/*============================================================ -** -** 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); - -#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/corelib.cpp b/src/coreclr/vm/corelib.cpp index 75f5dd62da82ea..efd5959bd3b072 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/corelib.h b/src/coreclr/vm/corelib.h index 0b979353253f72..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 @@ -969,6 +964,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/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp index e7f6b88b3bc37f..14f289452611e1 100644 --- a/src/coreclr/vm/finalizerthread.cpp +++ b/src/coreclr/vm/finalizerthread.cpp @@ -288,8 +288,6 @@ void FinalizerThread::WaitForFinalizerEvent (CLREvent *event) { case (WAIT_OBJECT_0): return; - case (WAIT_ABANDONED): - return; case (WAIT_TIMEOUT): break; } @@ -351,8 +349,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/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 7b0e21e2c4437d..8f0e9766654f9b 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1225,37 +1225,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 @@ -1315,6 +1293,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. @@ -1361,12 +1342,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/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e2dd498b8b483a..d570ae7f992324 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" @@ -289,6 +288,10 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_GetCurrentOSThreadId) DllImportEntry(ThreadNative_Initialize) 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) @@ -298,17 +301,10 @@ 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) #endif // FEATURE_COMINTEROP - 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) @@ -484,24 +480,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) diff --git a/src/coreclr/vm/synch.h b/src/coreclr/vm/synch.h index 72e19f1c33b602..58dbf2b2ca6866 100644 --- a/src/coreclr/vm/synch.h +++ b/src/coreclr/vm/synch.h @@ -12,8 +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. - 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 f047cc4b1bc55e..353aeafa4d49a4 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; @@ -2575,93 +2571,125 @@ void Thread::CoUninitialize() void Thread::CleanupDetachedThreads() { CONTRACTL { - NOTHROW; + THROWS; + MODE_COOPERATIVE; GC_TRIGGERS; } CONTRACTL_END; _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->IsDetached()) + { + STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - detaching thread 0x%p\n", 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 (!thread->IsGCSpecial()) + { + // 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); + } - // 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 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(pThread->GetExposedObject()); + CALL_MANAGED_METHOD_NORET(args); + + pThread->DecExternalCount(FALSE); + } } #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT @@ -3122,6 +3150,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, @@ -3177,18 +3223,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. @@ -3205,48 +3239,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 @@ -3263,8 +3255,6 @@ DWORD Thread::DoAppropriateWaitWorker(int countHandles, HANDLE *handles, BOOL wa DoAppropriateWaitWorkerAlertableHelper(mode); } - StateHolder OSAlertableWait(alertable); - ThreadStateHolder tsh(alertable, TS_Interruptible | TS_Interrupted); bool sendWaitEvents = @@ -3351,7 +3341,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); @@ -3384,14 +3373,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); @@ -3449,11 +3430,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) @@ -3476,178 +3459,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 - _ASSERTE(WAIT_OBJECT_0 == ret || - WAIT_ABANDONED == ret || - WAIT_TIMEOUT == ret || - WAIT_FAILED == ret || - ERROR_TOO_MANY_POSTS == ret); - - //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 - { - 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) { @@ -3897,82 +3708,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 da6f0a6ff2eba9..26d67fcd4edc8a 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, @@ -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. @@ -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(); @@ -1822,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. @@ -2166,15 +2145,12 @@ 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 +2633,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.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 a25bcdfc984f28..4c7a7a63b1b0aa 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 @@ -1778,6 +1778,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 @@ -2236,6 +2245,7 @@ + @@ -2274,15 +2284,18 @@ + + + @@ -2302,16 +2315,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 @@ -2324,12 +2333,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 @@ -2340,8 +2343,6 @@ Common\System\Memory\FixedBufferExtensions.cs - - @@ -2809,6 +2810,7 @@ + @@ -2849,7 +2851,7 @@ - + @@ -2860,16 +2862,13 @@ - + Common\Interop\Unix\System.Native\Interop.LowLevelCrossProcessMutex.cs - - - 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 78% 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..162d8e5a89c9c6 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 @@ -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 { @@ -11,8 +11,11 @@ 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))] private void Create(int maximumSignalCount) { _semaphore = WaitSubsystem.WaitableObject.NewSemaphore(0, maximumSignalCount); 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.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(); 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() { 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 { 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 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__