diff --git a/eng/native/tryrun.cmake b/eng/native/tryrun.cmake index 570e1e14221c23..4f84aa93668096 100644 --- a/eng/native/tryrun.cmake +++ b/eng/native/tryrun.cmake @@ -59,7 +59,6 @@ if(DARWIN AND NOT DEFINED ANDROID_BUILD) set_cache_value(HAVE_CLOCK_REALTIME_EXITCODE 0) set_cache_value(HAVE_CLOCK_THREAD_CPUTIME_EXITCODE 0) set_cache_value(HAVE_CLOCK_GETTIME_NSEC_NP_EXITCODE 0) - set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 1) set_cache_value(HAVE_MMAP_DEV_ZERO_EXITCODE 1) set_cache_value(HAVE_PROCFS_CTL_EXITCODE 1) set_cache_value(HAVE_PROCFS_STAT_EXITCODE 1) @@ -109,15 +108,11 @@ else() set_cache_value(HAVE_BROKEN_FIFO_KEVENT_EXITCODE 1) set_cache_value(HAVE_PROCFS_STAT 0) set_cache_value(HAVE_PROCFS_STATM 0) - set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0) elseif(ILLUMOS) set_cache_value(HAVE_PROCFS_CTL_EXITCODE 0) set_cache_value(COMPILER_SUPPORTS_W_CLASS_MEMACCESS 1) set_cache_value(HAVE_SET_MAX_VARIABLE 1) - set_cache_value(HAVE_FULLY_FEATURED_PTHREAD_MUTEXES 1) - set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0) elseif (TIZEN) - set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0) elseif(HAIKU) set_cache_value(HAVE_CLOCK_MONOTONIC_COARSE_EXITCODE 1) set_cache_value(HAVE_PROCFS_STAT_EXITCODE 1) @@ -126,7 +121,3 @@ else() set_cache_value(ONE_SHARED_MAPPING_PER_FILEREGION_PER_PROCESS 0) endif() endif() - -if(TARGET_ARCH_NAME MATCHES "^(x86|x64|s390x|armv6|loongarch64|riscv64|ppc64le)$") - set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 0) -endif() diff --git a/eng/native/tryrun_ios_tvos.cmake b/eng/native/tryrun_ios_tvos.cmake index 7a260f2af212fe..8dfecf22e12e69 100644 --- a/eng/native/tryrun_ios_tvos.cmake +++ b/eng/native/tryrun_ios_tvos.cmake @@ -16,7 +16,6 @@ set_cache_value(HAVE_BROKEN_FIFO_SELECT_EXITCODE 1) set_cache_value(HAVE_CLOCK_REALTIME_EXITCODE 0) set_cache_value(HAVE_CLOCK_THREAD_CPUTIME_EXITCODE 0) set_cache_value(HAVE_CLOCK_GETTIME_NSEC_NP_EXITCODE 0) -set_cache_value(HAVE_FUNCTIONAL_PTHREAD_ROBUST_MUTEXES_EXITCODE 1) set_cache_value(HAVE_MMAP_DEV_ZERO_EXITCODE 1) set_cache_value(HAVE_PROCFS_CTL_EXITCODE 1) set_cache_value(HAVE_PROCFS_STAT_EXITCODE 1) diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 4b8ad916f5c8e9..38fe476f4ab8f6 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -240,7 +240,6 @@ - @@ -292,8 +291,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..a1c54a6e15f5fa 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 @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; +using Microsoft.Win32.SafeHandles; namespace System.Threading { @@ -33,6 +34,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 @@ -106,15 +111,12 @@ private void StartCallback() _startHelper = null; 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); + // When this thread is about to exit, inform any subsystems that need to know. + // For external threads that have been attached to the runtime, we'll call this + // after the thread has been detached as it won't come through this path. + OnThreadExiting(); + } // Max iterations to be done in SpinWait without switching GC modes. private const int SpinWaitCoopThreshold = 1024; @@ -298,6 +300,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 +371,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 +391,8 @@ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwO return true; } + + internal const bool ReentrantWaitsEnabled = false; #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT #if FEATURE_COMINTEROP @@ -387,38 +416,35 @@ public void DisableComObjectEagerCleanup() { } /// public void Interrupt() { +#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI + WaitSubsystem.Interrupt(this); +#else Interrupt(GetNativeHandle()); GC.KeepAlive(this); +#endif } +#if TARGET_WINDOWS [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Interrupt")] private static partial void Interrupt(ThreadHandle t); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Join")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool Join(ObjectHandleOnStack thread, int millisecondsTimeout); - - /// - /// Waits for the thread to die or for timeout milliseconds to elapse. - /// - /// - /// Returns true if the thread died, or false if the wait timed out. If - /// -1 is given as the parameter, no timeout will occur. - /// - /// if timeout < -1 (Timeout.Infinite) - /// if the thread is interrupted while waiting - /// if the thread has not been started yet - public bool Join(int millisecondsTimeout) - { - // Validate the timeout - if (millisecondsTimeout < 0 && millisecondsTimeout != Timeout.Infinite) - { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1); - } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetOSHandle")] + private static partial SafeWaitHandle GetOSHandle(ThreadHandle t); - Thread _this = this; - return Join(ObjectHandleOnStack.Create(ref _this), millisecondsTimeout); + private SafeWaitHandle GetJoinHandle() + { + SafeWaitHandle handle = GetOSHandle(GetNativeHandle()); + GC.KeepAlive(this); + return handle; } +#else + private volatile ManualResetEvent? _joinEvent; + private SafeWaitHandle GetJoinHandle() + { + ManualResetEvent joinEvent = Interlocked.CompareExchange(ref _joinEvent, new ManualResetEvent(false), null)!; + return joinEvent.SafeWaitHandle; + } +#endif /// /// Max value to be passed into for optimal delaying. This value is normalized to be @@ -513,6 +539,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 +586,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 73f8ea35f95daa..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, [MarshalAs(UnmanagedType.Bool)] bool doNotSendWaitEvents); - - 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/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 8e613dd0a3a05a..3c464ce4cb36b9 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 @@ -106,7 +104,6 @@ nativeStringResourceTable_mscorrc #RaiseException #RaiseFailFastException #ReadFile -#ReleaseMutex #ReleaseSemaphore #ResetEvent #ResumeThread 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 2f46b37643aaf0..e40d3e1b8e0e4a 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 @@ -292,7 +292,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 1663dda8e088a8..4132a9abfde1ba 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/Threading/Thread.NativeAot.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Unix.cs index b30ffd62b08d9a..54e0e182f61d80 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Unix.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Unix.cs @@ -14,6 +14,8 @@ public sealed partial class Thread // Event signaling that the thread has stopped private ManualResetEvent _stopped; + private SafeWaitHandle GetJoinHandle() => _stopped.SafeWaitHandle; + private WaitSubsystem.ThreadWaitInfo _waitInfo; internal WaitSubsystem.ThreadWaitInfo WaitInfo => _waitInfo; @@ -55,38 +57,6 @@ private static void OnThreadExit() } } - private bool JoinInternal(int millisecondsTimeout) - { - // This method assumes the thread has been started - Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); - SafeWaitHandle waitHandle = _stopped.SafeWaitHandle; - - // If an OS thread is terminated and its Thread object is resurrected, waitHandle may be finalized and closed - if (waitHandle.IsClosed) - { - return true; - } - - // Prevent race condition with the finalizer - try - { - waitHandle.DangerousAddRef(); - } - catch (ObjectDisposedException) - { - return true; - } - - try - { - return _stopped.WaitOne(millisecondsTimeout); - } - finally - { - waitHandle.DangerousRelease(); - } - } - private unsafe bool CreateThread(GCHandle thisThreadHandle) { // Create the Stop event before starting the thread to make sure 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 fd5183d74a99a4..57b8b7ad2bdbb9 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 @@ -21,60 +21,9 @@ public sealed partial class Thread private ApartmentState _initialApartmentState = ApartmentState.Unknown; - 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; - } + private SafeWaitHandle GetJoinHandle() => _osHandle; - currentThread.ClearWaitSleepJoinState(); - } - } + partial void PlatformSpecificInitialize(); // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start private void PlatformSpecificInitializeExistingThread() @@ -180,88 +129,6 @@ private static void OnThreadExit() } } - private bool JoinInternal(int millisecondsTimeout) - { - // This method assumes the thread has been started - Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); - SafeWaitHandle waitHandle = _osHandle; - - // If an OS thread is terminated and its Thread object is resurrected, _osHandle may be finalized and closed - if (waitHandle.IsClosed) - { - return true; - } - - // Handle race condition with the finalizer - try - { - waitHandle.DangerousAddRef(); - } - catch (ObjectDisposedException) - { - return true; - } - - try - { - if (millisecondsTimeout == 0) - { - int result = (int)Interop.Kernel32.WaitForSingleObject(waitHandle.DangerousGetHandle(), 0); - return result == (int)Interop.Kernel32.WAIT_OBJECT_0; - } - else - { - Thread currentThread = CurrentThread; - currentThread.SetWaitSleepJoinState(); - uint result; - if (millisecondsTimeout == Timeout.Infinite) - { - // Infinite wait - while (true) - { - result = Interop.Kernel32.WaitForSingleObjectEx(waitHandle.DangerousGetHandle(), Timeout.UnsignedInfinite, Interop.BOOL.TRUE); - if (result != Interop.Kernel32.WAIT_IO_COMPLETION) - { - break; - } - // Check if this was our interrupt APC - CheckForPendingInterrupt(); - } - } - else - { - long startTime = Environment.TickCount64; - while (true) - { - result = Interop.Kernel32.WaitForSingleObjectEx(waitHandle.DangerousGetHandle(), (uint)millisecondsTimeout, Interop.BOOL.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) - { - result = Interop.Kernel32.WAIT_TIMEOUT; - break; - } - millisecondsTimeout -= (int)elapsed; - startTime = currentTime; - } - } - currentThread.ClearWaitSleepJoinState(); - return result == (int)Interop.Kernel32.WAIT_OBJECT_0; - } - } - finally - { - waitHandle.DangerousRelease(); - } - } - private unsafe bool CreateThread(GCHandle thisThreadHandle) { const int AllocationGranularity = 0x10000; // 64 KiB @@ -539,6 +406,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 => GetCurrentApartmentState() == ApartmentState.STA; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs index 34db94ad986451..fe933685bdfd81 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.cs @@ -320,16 +320,6 @@ private static int VerifyTimeoutMilliseconds(int millisecondsTimeout) return millisecondsTimeout; } - public bool Join(int millisecondsTimeout) - { - VerifyTimeoutMilliseconds(millisecondsTimeout); - if (GetThreadStateBit(ThreadState.Unstarted)) - { - throw new ThreadStateException(SR.ThreadState_NotStarted); - } - return JoinInternal(millisecondsTimeout); - } - /// /// Max value to be passed into for optimal delaying. Currently, the value comes from /// defaults in CoreCLR's Thread::InitializeYieldProcessorNormalized(). This value is supposed to be normalized to be diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index c3355e4323f78e..eb03288e0cbc77 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -672,65 +672,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 @@ -836,8 +777,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) @@ -850,13 +789,6 @@ WaitForSingleObject( IN HANDLE hHandle, IN DWORD dwMilliseconds); -PALIMPORT -DWORD -PALAPI -PAL_WaitForSingleObjectPrioritized( - IN HANDLE hHandle, - IN DWORD dwMilliseconds); - PALIMPORT DWORD PALAPI @@ -884,15 +816,6 @@ WaitForMultipleObjectsEx( IN DWORD dwMilliseconds, IN BOOL bAlertable); -PALIMPORT -DWORD -PALAPI -SignalObjectAndWait( - IN HANDLE hObjectToSignal, - IN HANDLE hObjectToWaitOn, - IN DWORD dwMilliseconds, - IN BOOL bAlertable); - #define DUPLICATE_CLOSE_SOURCE 0x00000001 #define DUPLICATE_SAME_ACCESS 0x00000002 @@ -968,14 +891,6 @@ ResumeThread( typedef VOID (PALAPI_NOEXPORT *PAPCFUNC)(ULONG_PTR dwParam); -PALIMPORT -DWORD -PALAPI -QueueUserAPC( - IN PAPCFUNC pfnAPC, - IN HANDLE hThread, - IN ULONG_PTR dwData); - #ifdef HOST_X86 // diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index ac22c0d1924263..219a14a29f97c8 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -197,10 +197,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 fab78b1299467b..7703b484300fa9 100644 --- a/src/coreclr/pal/src/file/file.cpp +++ b/src/coreclr/pal/src/file/file.cpp @@ -74,8 +74,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..f5d9ae6560a858 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 @@ -625,9 +570,7 @@ namespace CorUnix PAL_ERROR RegisterWaitingThread( WaitType eWaitType, - DWORD dwIndex, - bool fAltertable, - bool fPrioritize + DWORD dwIndex ) = 0; // @@ -916,8 +859,6 @@ namespace CorUnix enum ThreadWakeupReason { WaitSucceeded, - Alerted, - MutexAbandoned, WaitTimeout, WaitFailed }; @@ -942,39 +883,11 @@ namespace CorUnix BlockThread( CPalThread *pCurrentThread, DWORD dwTimeout, - bool fAlertable, bool fIsSleep, ThreadWakeupReason *peWakeupReason, // OUT DWORD *pdwSignaledObject // OUT ) = 0; - virtual - PAL_ERROR - AbandonObjectsOwnedByThread( - CPalThread *pCallingThread, - CPalThread *pTargetThread - ) = 0; - - virtual - PAL_ERROR - QueueUserAPC( - CPalThread *pThread, - CPalThread *pTargetThread, - PAPCFUNC pfnAPC, - ULONG_PTR dwData - ) = 0; - - virtual - bool - AreAPCsPending( - CPalThread *pThread - ) = 0; - - virtual - PAL_ERROR - DispatchPendingAPCs( - CPalThread *pThread - ) = 0; // // This routine is primarily meant for use by WaitForMultipleObjects[Ex]. 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..79105ef14b8515 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 @@ -37,21 +36,11 @@ namespace CorUnix DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, - DWORD dwMilliseconds, - BOOL bAlertable, - BOOL bPrioritize = FALSE); - - DWORD InternalSignalObjectAndWait( - CPalThread *thread, - HANDLE hObjectToSignal, - HANDLE hObjectToWaitOn, - DWORD dwMilliseconds, - BOOL bAlertable); + DWORD dwMilliseconds); PAL_ERROR InternalSleepEx( CPalThread * pthrCurrent, - DWORD dwMilliseconds, - BOOL bAlertable); + DWORD dwMilliseconds); enum THREAD_STATE { @@ -68,8 +57,6 @@ namespace CorUnix class CSynchData; typedef struct _WaitingThreadsListNode * PWaitingThreadsListNode; - typedef struct _OwnedObjectsListNode * POwnedObjectsListNode; - typedef struct _ThreadApcInfoNode * PThreadApcInfoNode; typedef struct _ThreadWaitInfo { @@ -113,8 +100,6 @@ namespace CorUnix Volatile m_lLocalSynchLockCount; LIST_ENTRY m_leOwnedObjsList; - NamedMutexProcessData *m_ownedNamedMutexListHead; - ThreadNativeWaitData m_tnwdNativeData; ThreadWaitInfo m_twiWaitInfo; @@ -160,20 +145,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) @@ -182,21 +153,6 @@ namespace CorUnix bool TryAcquireNativeWaitLock(void); }; - class CThreadApcInfo : public CThreadInfoInitializer - { - friend class CPalSynchronizationManager; - - PThreadApcInfoNode m_ptainHead; - PThreadApcInfoNode m_ptainTail; - - public: - CThreadApcInfo() : - m_ptainHead(NULL), - m_ptainTail(NULL) - { - } - }; - class CPalSynchMgrController { public: diff --git a/src/coreclr/pal/src/include/pal/thread.hpp b/src/coreclr/pal/src/include/pal/thread.hpp index 236bb6d49d9d84..9b1f92eb3c3ba9 100644 --- a/src/coreclr/pal/src/include/pal/thread.hpp +++ b/src/coreclr/pal/src/include/pal/thread.hpp @@ -300,7 +300,6 @@ namespace CorUnix CThreadSynchronizationInfo synchronizationInfo; CThreadSuspensionInfo suspensionInfo; - CThreadApcInfo apcInfo; CPalThread() : 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..8cbee65aef2540 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()) { @@ -248,9 +246,7 @@ namespace CorUnix --*/ PAL_ERROR CSynchWaitController::RegisterWaitingThread( WaitType wtWaitType, - DWORD dwIndex, - bool fAlertable, - bool fPrioritize) + DWORD dwIndex) { VALIDATEOBJECT(m_psdSynchData); @@ -339,7 +335,7 @@ namespace CorUnix DWORD dwWaitState; // Setting the thread in wait state - dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE: TWS_WAITING); + dwWaitState = TWS_WAITING; TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u \n", pdwWaitState, dwWaitState); @@ -368,7 +364,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 +557,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 +671,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 +680,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 +720,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 +771,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; } @@ -1029,44 +832,19 @@ namespace CorUnix // // Target wait is satisfied // - TRACE("Trying to switch wait state [%p] from WAIT/ALERTABLE " + TRACE("Trying to switch wait state [%p] from WAIT " "to ACTIVE for thread=%u\n", pdwWaitState, pwtlnItem->dwThreadId); - if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState, FALSE)) + if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState)) { - TRACE("Succeeded switching wait state [%p] from WAIT/ALERTABLE " + TRACE("Succeeded switching wait state [%p] from WAIT " "to TWS_ACTIVE for trhead=%u\n", pdwWaitState, pwtlnItem->dwThreadId); 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 +871,7 @@ namespace CorUnix palErr = CPalSynchronizationManager::WakeUpLocalThread( pthrCurrent, ptwiWaitInfo->pthrOwner, - fAbandoned ? MutexAbandoned : WaitSucceeded, + WaitSucceeded, dwObjIdx); if (NO_ERROR != palErr) @@ -1163,39 +941,19 @@ namespace CorUnix // // Target wait is satisfied // - TRACE("Trying to switch wait state [%p] from WAIT/ALERTABLE " + TRACE("Trying to switch wait state [%p] from WAIT " "to ACTIVE for thread=%u\n", pdwWaitState, pwtlnItem->dwThreadId); - if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState, FALSE)) + if (CPalSynchronizationManager::InterlockedAwaken(pdwWaitState)) { - TRACE("Succeeded switching wait state [%p] from WAIT/ALERTABLE " + TRACE("Succeeded switching wait state [%p] from WAIT " "to TWS_ACTIVE for trhead=%u\n", pdwWaitState, pwtlnItem->dwThreadId); 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 +980,7 @@ namespace CorUnix palErr = CPalSynchronizationManager::WakeUpLocalThread( pthrCurrent, ptwiWaitInfo->pthrOwner, - fAbandoned ? MutexAbandoned : WaitSucceeded, + WaitSucceeded, dwObjIdx); if (NO_ERROR != palErr) @@ -1267,7 +1025,7 @@ namespace CorUnix WaitCompletionState CSynchData::IsRestOfWaitAllSatisfied( WaitingThreadsListNode * pwtlnNode) { - int iSignaledOrOwnedObjCount = 0; + int iSignaledObjCount = 0; int iTgtCount = 0; int i; WaitCompletionState wcsWaitCompletionState = WaitIsNotSatisfied; @@ -1294,7 +1052,6 @@ namespace CorUnix { WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i]; bool fRetVal; - bool fIsAbandoned; VALIDATEOBJECT(pwtlnItem); @@ -1311,17 +1068,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 +1085,7 @@ namespace CorUnix } } - if (iSignaledOrOwnedObjCount < iTgtCount) + if (iSignaledObjCount < iTgtCount) { wcsWaitCompletionState = WaitIsNotSatisfied; } @@ -1343,145 +1099,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 +1111,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..b6c44754210805 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.cpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.cpp @@ -158,9 +158,7 @@ namespace CorUnix m_cacheSynchData(SynchDataCacheMaxSize), m_cacheSHRSynchData(SynchDataCacheMaxSize), m_cacheWTListNodes(WTListNodeCacheMaxSize), - m_cacheSHRWTListNodes(WTListNodeCacheMaxSize), - m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize), - m_cacheOwnedObjectsListNodes(OwnedObjectsListCacheMaxSize) + m_cacheSHRWTListNodes(WTListNodeCacheMaxSize) { #if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT m_iKQueue = -1; @@ -185,7 +183,6 @@ namespace CorUnix PAL_ERROR CPalSynchronizationManager::BlockThread( CPalThread *pthrCurrent, DWORD dwTimeout, - bool fAlertable, bool fIsSleep, ThreadWakeupReason *ptwrWakeupReason, DWORD * pdwSignaledObject) @@ -195,7 +192,6 @@ namespace CorUnix DWORD * pdwWaitState; DWORD dwWaitState = 0; DWORD dwSigObjIdx = 0; - bool fRaceAlerted = false; bool fEarlyDeath = false; pdwWaitState = SharedIDToTypePointer(DWORD, @@ -207,105 +203,58 @@ namespace CorUnix if (fIsSleep) { - // If fIsSleep is true we are being called by Sleep/SleepEx - // and we need to switch the wait state to TWS_WAITING or - // TWS_ALERTABLE (according to fAlertable) + // Setting the thread in wait state + dwWaitState = TWS_WAITING; - if (fAlertable) - { - // If we are in alertable mode we need to grab the lock to - // make sure that no APC is queued right before the - // InterlockedCompareExchange. - // If there are APCs queued at this time, no native wakeup - // will be posted, so we need to skip the native wait + TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u [current *pdwWaitState=%u]\n", + pdwWaitState, dwWaitState, *pdwWaitState); - // Lock - AcquireLocalSynchLock(pthrCurrent); + dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState, + dwWaitState, + TWS_ACTIVE); - if (AreAPCsPending(pthrCurrent)) - { - // APCs have been queued when the thread wait status was - // still TWS_ACTIVE, therefore the queueing thread will not - // post any native wakeup: we need to skip the actual - // native wait - fRaceAlerted = true; - } - } - - if (!fRaceAlerted) + if ((DWORD)TWS_ACTIVE != dwWaitState) { - // Setting the thread in wait state - dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING); - - TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u [current *pdwWaitState=%u]\n", - pdwWaitState, dwWaitState, *pdwWaitState); - - dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState, - dwWaitState, - TWS_ACTIVE); - - if ((DWORD)TWS_ACTIVE != dwWaitState) + if ((DWORD)TWS_EARLYDEATH == dwWaitState) { - if (fAlertable) - { - // Unlock - ReleaseLocalSynchLock(pthrCurrent); - } - - if ((DWORD)TWS_EARLYDEATH == dwWaitState) - { - // Process is terminating, this thread will soon be suspended (by SuspendOtherThreads). - WARN("Thread is about to get suspended by TerminateProcess\n"); - - fEarlyDeath = true; - palErr = WAIT_FAILED; - } - else - { - ASSERT("Unexpected thread wait state %u\n", dwWaitState); - palErr = ERROR_INTERNAL_ERROR; - } + // Process is terminating, this thread will soon be suspended (by SuspendOtherThreads). + WARN("Thread is about to get suspended by TerminateProcess\n"); - goto BT_exit; + fEarlyDeath = true; + palErr = WAIT_FAILED; + } + else + { + ASSERT("Unexpected thread wait state %u\n", dwWaitState); + palErr = ERROR_INTERNAL_ERROR; } - } - if (fAlertable) - { - // Unlock - ReleaseLocalSynchLock(pthrCurrent); + goto BT_exit; } } - if (fRaceAlerted) - { - twrWakeupReason = Alerted; - } - else - { - TRACE("Current thread is about to block for waiting\n"); + TRACE("Current thread is about to block for waiting\n"); - palErr = ThreadNativeWait( - &pthrCurrent->synchronizationInfo.m_tnwdNativeData, - dwTimeout, - &twrWakeupReason, - &dwSigObjIdx); + palErr = ThreadNativeWait( + &pthrCurrent->synchronizationInfo.m_tnwdNativeData, + dwTimeout, + &twrWakeupReason, + &dwSigObjIdx); - if (NO_ERROR != palErr) - { - ERROR("ThreadNativeWait() failed [palErr=%d]\n", palErr); - twrWakeupReason = WaitFailed; - goto BT_exit; - } - - TRACE("ThreadNativeWait returned {WakeupReason=%u " - "dwSigObjIdx=%u}\n", twrWakeupReason, dwSigObjIdx); + if (NO_ERROR != palErr) + { + ERROR("ThreadNativeWait() failed [palErr=%d]\n", palErr); + twrWakeupReason = WaitFailed; + goto BT_exit; } + TRACE("ThreadNativeWait returned {WakeupReason=%u " + "dwSigObjIdx=%u}\n", twrWakeupReason, dwSigObjIdx); + if (WaitTimeout == twrWakeupReason) { // timeout reached. set wait state back to 'active' - dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING); + dwWaitState = TWS_WAITING; TRACE("Current thread awakened for timeout: switching wait " "state [%p] from %u to TWS_ACTIVE [current *pdwWaitState=%u]\n", @@ -364,7 +313,6 @@ namespace CorUnix palErr = WAIT_FAILED; break; case TWS_WAITING: - case TWS_ALERTABLE: default: _ASSERT_MSG(dwOldWaitState == dwWaitState, "Unexpected wait status: actual=%u, expected=%u\n", @@ -396,11 +344,10 @@ namespace CorUnix break; } case WaitSucceeded: - case MutexAbandoned: *pdwSignaledObject = dwSigObjIdx; break; default: - // 'Alerted' and 'WaitFailed' go through this case + // 'WaitFailed' goes through this case break; } @@ -533,128 +480,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 @@ -1030,232 +855,6 @@ namespace CorUnix return NO_ERROR; } - /*++ - Method: - CPalSynchronizationManager::QueueUserAPC - - Internal implementation of QueueUserAPC - --*/ - PAL_ERROR CPalSynchronizationManager::QueueUserAPC(CPalThread * pthrCurrent, - CPalThread * pthrTarget, - PAPCFUNC pfnAPC, - ULONG_PTR uptrData) - { - PAL_ERROR palErr = NO_ERROR; - ThreadApcInfoNode * ptainNode = NULL; - DWORD dwWaitState; - DWORD * pdwWaitState; - ThreadWaitInfo * pTargetTWInfo = GetThreadWaitInfo(pthrTarget); - bool fLocalSynchLock = false; - bool fThreadLock = false; - - ptainNode = m_cacheThreadApcInfoNodes.Get(pthrCurrent); - if (NULL == ptainNode) - { - ERROR("No memory for new APCs linked list entry\n"); - palErr = ERROR_NOT_ENOUGH_MEMORY; - goto QUAPC_exit; - } - - ptainNode->pfnAPC = pfnAPC; - ptainNode->pAPCData = uptrData; - ptainNode->pNext = NULL; - - AcquireLocalSynchLock(pthrCurrent); - fLocalSynchLock = true; - - pthrTarget->Lock(pthrCurrent); - fThreadLock = true; - - if (TS_DONE == pthrTarget->synchronizationInfo.GetThreadState()) - { - ERROR("Thread %#x has terminated; can't queue an APC on it\n", - pthrTarget->GetThreadId()); - palErr = ERROR_INVALID_PARAMETER; - goto QUAPC_exit; - } - pdwWaitState = SharedIDToTypePointer(DWORD, - pthrTarget->synchronizationInfo.m_shridWaitAwakened); - if (TWS_EARLYDEATH == VolatileLoad(pdwWaitState)) - { - ERROR("Thread %#x is about to be suspended for process shutdwon, " - "can't queue an APC on it\n", pthrTarget->GetThreadId()); - palErr = ERROR_INVALID_PARAMETER; - goto QUAPC_exit; - } - - if (NULL == pthrTarget->apcInfo.m_ptainTail) - { - _ASSERT_MSG(NULL == pthrTarget->apcInfo.m_ptainHead, "Corrupted APC list\n"); - - pthrTarget->apcInfo.m_ptainHead = ptainNode; - pthrTarget->apcInfo.m_ptainTail = ptainNode; - } - else - { - pthrTarget->apcInfo.m_ptainTail->pNext = ptainNode; - pthrTarget->apcInfo.m_ptainTail = ptainNode; - } - - // Set ptainNode to NULL so it won't be readded to the cache - ptainNode = NULL; - - TRACE("APC %p with parameter %p added to APC queue\n", pfnAPC, uptrData); - - dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState, - (LONG)TWS_ACTIVE, - (LONG)TWS_ALERTABLE); - - // Release thread lock - pthrTarget->Unlock(pthrCurrent); - fThreadLock = false; - - if (TWS_ALERTABLE == dwWaitState) - { - // Unregister the wait - UnRegisterWait(pthrCurrent, pTargetTWInfo); - - // Wake up target thread - palErr = WakeUpLocalThread( - pthrCurrent, - pthrTarget, - Alerted, - 0); - - if (NO_ERROR != palErr) - { - ERROR("Failed to wakeup local thread %#x for dispatching APCs [err=%u]\n", - pthrTarget->GetThreadId(), palErr); - } - } - - QUAPC_exit: - if (fThreadLock) - { - pthrTarget->Unlock(pthrCurrent); - } - - if (fLocalSynchLock) - { - ReleaseLocalSynchLock(pthrCurrent); - } - - if (ptainNode) - { - m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode); - } - - return palErr; - } - - /*++ - Method: - CPalSynchronizationManager::AreAPCsPending - - Returns 'true' if there are APCs currently pending for the target - thread (normally the current one) - --*/ - bool CPalSynchronizationManager::AreAPCsPending( - CPalThread * pthrTarget) - { - // No need to lock here - return (NULL != pthrTarget->apcInfo.m_ptainHead); - } - - /*++ - Method: - CPalSynchronizationManager::DispatchPendingAPCs - - Executes any pending APC for the current thread - --*/ - PAL_ERROR CPalSynchronizationManager::DispatchPendingAPCs( - CPalThread * pthrCurrent) - { - ThreadApcInfoNode * ptainNode, * ptainLocalHead; - int iAPCsCalled = 0; - - while (TRUE) - { - // Lock - pthrCurrent->Lock(pthrCurrent); - ptainLocalHead = pthrCurrent->apcInfo.m_ptainHead; - if (ptainLocalHead) - { - pthrCurrent->apcInfo.m_ptainHead = NULL; - pthrCurrent->apcInfo.m_ptainTail = NULL; - } - - // Unlock - pthrCurrent->Unlock(pthrCurrent); - - if (NULL == ptainLocalHead) - { - break; - } - - while (ptainLocalHead) - { - ptainNode = ptainLocalHead; - ptainLocalHead = ptainNode->pNext; - -#if _ENABLE_DEBUG_MESSAGES_ - // reset ENTRY nesting level back to zero while - // inside the callback ... - int iOldLevel = DBG_change_entrylevel(0); -#endif /* _ENABLE_DEBUG_MESSAGES_ */ - - TRACE("Calling APC %p with parameter %#x\n", - ptainNode->pfnAPC, ptainNode->pfnAPC); - - // Actual APC call - ptainNode->pfnAPC(ptainNode->pAPCData); - -#if _ENABLE_DEBUG_MESSAGES_ - // ... and set nesting level back to what it was - DBG_change_entrylevel(iOldLevel); -#endif /* _ENABLE_DEBUG_MESSAGES_ */ - - iAPCsCalled++; - m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode); - } - } - - return (iAPCsCalled > 0) ? NO_ERROR : ERROR_NOT_FOUND; - } - - /*++ - Method: - CPalSynchronizationManager::DiscardAllPendingAPCs - - Discards any pending APC for the target pthrTarget thread - --*/ - void CPalSynchronizationManager::DiscardAllPendingAPCs( - CPalThread * pthrCurrent, - CPalThread * pthrTarget) - { - ThreadApcInfoNode * ptainNode, * ptainLocalHead; - - // Lock - pthrTarget->Lock(pthrCurrent); - ptainLocalHead = pthrTarget->apcInfo.m_ptainHead; - if (ptainLocalHead) - { - pthrTarget->apcInfo.m_ptainHead = NULL; - pthrTarget->apcInfo.m_ptainTail = NULL; - } - - // Unlock - pthrTarget->Unlock(pthrCurrent); - - while (ptainLocalHead) - { - ptainNode = ptainLocalHead; - ptainLocalHead = ptainNode->pNext; - - m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode); - } - } - /*++ Method: CPalSynchronizationManager::CreatePalSynchronizationManager @@ -2989,8 +2588,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 +2788,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 /*++ @@ -3508,31 +2979,9 @@ namespace CorUnix Tries to change the target wait status to 'active' in an interlocked fashion --*/ bool CPalSynchronizationManager::InterlockedAwaken( - DWORD *pWaitState, - bool fAlertOnly) + DWORD *pWaitState) { - DWORD dwPrevState; - - dwPrevState = InterlockedCompareExchange((LONG *)pWaitState, TWS_ACTIVE, TWS_ALERTABLE); - if (TWS_ALERTABLE != dwPrevState) - { - if (fAlertOnly) - { - return false; - } - - dwPrevState = InterlockedCompareExchange((LONG *)pWaitState, TWS_ACTIVE, TWS_WAITING); - if (TWS_WAITING == dwPrevState) - { - return true; - } - } - else - { - return true; - } - - return false; + return InterlockedCompareExchange((LONG *)pWaitState, TWS_ACTIVE, TWS_WAITING) == TWS_WAITING; } /*++ diff --git a/src/coreclr/pal/src/synchmgr/synchmanager.hpp b/src/coreclr/pal/src/synchmgr/synchmanager.hpp index 7d86ab6fb12d4d..4a577261a783b0 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.hpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.hpp @@ -59,7 +59,6 @@ namespace CorUnix { TWS_ACTIVE, TWS_WAITING, - TWS_ALERTABLE, TWS_EARLYDEATH, }; @@ -116,19 +115,6 @@ namespace CorUnix CPalThread * pthrTarget; } DeferredSignalingListNode; - typedef struct _OwnedObjectsListNode - { - LIST_ENTRY Link; - CSynchData * pPalObjSynchData; - } OwnedObjectsListNode; - - typedef struct _ThreadApcInfoNode - { - struct _ThreadApcInfoNode * pNext; - PAPCFUNC pfnAPC; - ULONG_PTR pAPCData; - } ThreadApcInfoNode; - class CPalSynchronizationManager; // fwd declaration class CProcProcessLocalData; // fwd declaration @@ -147,15 +133,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 +141,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 +165,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 +222,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 +351,13 @@ 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); + DWORD dwIndex); virtual void ReleaseController(void); @@ -452,8 +381,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); }; @@ -469,8 +396,6 @@ namespace CorUnix typedef CSHRSynchCache CSHRSynchDataCache; typedef CSynchCache CWaitingThreadsListNodeCache; typedef CSHRSynchCache CSHRWaitingThreadsListNodeCache; - typedef CSynchCache CThreadApcInfoNodeCache; - typedef CSynchCache COwnedObjectsListNodeCache; private: // types @@ -509,8 +434,6 @@ namespace CorUnix static const int CtrlrsCacheMaxSize = 256; 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 @@ -547,8 +470,6 @@ namespace CorUnix CSHRSynchDataCache m_cacheSHRSynchData; CWaitingThreadsListNodeCache m_cacheWTListNodes; CSHRWaitingThreadsListNodeCache m_cacheSHRWTListNodes; - CThreadApcInfoNodeCache m_cacheThreadApcInfoNodes; - COwnedObjectsListNodeCache m_cacheOwnedObjectsListNodes; // static methods static PAL_ERROR Initialize(); @@ -704,45 +625,16 @@ namespace CorUnix m_cacheSHRWTListNodes.Add(pthrCurrent, shridWTLNode); } - ThreadApcInfoNode * CacheGetApcInfoNodes(CPalThread * pthrCurrent) - { - return m_cacheThreadApcInfoNodes.Get(pthrCurrent); - } - void CacheAddApcInfoNodes( - CPalThread * pthrCurrent, - ThreadApcInfoNode * pNode) - { - 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 // virtual PAL_ERROR BlockThread( CPalThread *pthrCurrent, DWORD dwTimeout, - bool fAlertable, bool fIsSleep, ThreadWakeupReason *ptwrWakeupReason, DWORD *pdwSignaledObject); - virtual PAL_ERROR AbandonObjectsOwnedByThread( - CPalThread *pthrCurrent, - CPalThread *pthrTarget); - virtual PAL_ERROR GetSynchWaitControllersForObjects( CPalThread *pthrCurrent, IPalObject *rgObjects[], @@ -775,16 +667,6 @@ namespace CorUnix VOID *pvSynchData, ISynchWaitController **ppWaitController); - virtual PAL_ERROR QueueUserAPC( - CPalThread * pthrCurrent, - CPalThread *pthrTarget, - PAPCFUNC pfnAPC, - ULONG_PTR uptrData); - - virtual bool AreAPCsPending(CPalThread * pthrTarget); - - virtual PAL_ERROR DispatchPendingAPCs(CPalThread * pthrCurrent); - virtual void AcquireProcessLock(CPalThread *pthrCurrent); virtual void ReleaseProcessLock(CPalThread *pthrCurrent); @@ -851,10 +733,6 @@ namespace CorUnix PAL_ERROR WakeUpLocalWorkerThread( SynchWorkerCmd swcWorkerCmd); - void DiscardAllPendingAPCs( - CPalThread * pthrCurrent, - CPalThread * pthrTarget); - int ReadBytesFromProcessPipe( int iTimeout, BYTE * pRecvBuf, @@ -893,8 +771,7 @@ namespace CorUnix bool * pfIsActualExitCode); static bool InterlockedAwaken( - DWORD *pWaitState, - bool fAlertOnly); + DWORD *pWaitState); static PAL_ERROR GetAbsoluteTimeout( DWORD dwTimeout, diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp index 495d86b1cd5cd3..0ea8091f6e2352 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)); @@ -78,43 +73,13 @@ WaitForSingleObject(IN HANDLE hHandle, CPalThread * pThread = InternalGetCurrentThread(); dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE, - dwMilliseconds, FALSE); + dwMilliseconds); LOGEXIT("WaitForSingleObject returns DWORD %u\n", dwRet); PERF_EXIT(WaitForSingleObject); 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 @@ -136,7 +101,7 @@ WaitForSingleObjectEx(IN HANDLE hHandle, CPalThread * pThread = InternalGetCurrentThread(); dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE, - dwMilliseconds, bAlertable); + dwMilliseconds); LOGEXIT("WaitForSingleObjectEx returns DWORD %u\n", dwRet); PERF_EXIT(WaitForSingleObjectEx); @@ -168,7 +133,7 @@ WaitForMultipleObjects(IN DWORD nCount, CPalThread * pThread = InternalGetCurrentThread(); dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles, - bWaitAll, dwMilliseconds, FALSE); + bWaitAll, dwMilliseconds); LOGEXIT("WaitForMultipleObjects returns DWORD %u\n", dwRet); PERF_EXIT(WaitForMultipleObjects); @@ -199,43 +164,13 @@ WaitForMultipleObjectsEx(IN DWORD nCount, CPalThread * pThread = InternalGetCurrentThread(); dwRet = InternalWaitForMultipleObjectsEx(pThread, nCount, lpHandles, bWaitAll, - dwMilliseconds, bAlertable); + dwMilliseconds); LOGEXIT("WaitForMultipleObjectsEx returns DWORD %u\n", dwRet); PERF_EXIT(WaitForMultipleObjectsEx); return dwRet; } -/*++ -Function: - SignalObjectAndWait - -See MSDN doc for info about this function. ---*/ -DWORD -PALAPI -SignalObjectAndWait( - IN HANDLE hObjectToSignal, - IN HANDLE hObjectToWaitOn, - IN DWORD dwMilliseconds, - IN BOOL bAlertable) -{ - PERF_ENTRY(SignalObjectAndWait); - ENTRY( - "SignalObjectAndWait(hObjectToSignal=%p, hObjectToWaitOn=%p, dwMilliseconds=%u, bAlertable=%s)\n", - hObjectToSignal, - hObjectToWaitOn, - dwMilliseconds, - bAlertable ? "TRUE" : "FALSE"); - - CPalThread *thread = InternalGetCurrentThread(); - DWORD result = InternalSignalObjectAndWait(thread, hObjectToSignal, hObjectToWaitOn, dwMilliseconds, bAlertable); - - LOGEXIT("SignalObjectAndWait returns DWORD %u\n", result); - PERF_EXIT(SignalObjectAndWait); - return result; -} - /*++ Function: Sleep @@ -251,7 +186,7 @@ Sleep(IN DWORD dwMilliseconds) CPalThread * pThread = InternalGetCurrentThread(); - DWORD internalSleepRet = InternalSleepEx(pThread, dwMilliseconds, FALSE); + DWORD internalSleepRet = InternalSleepEx(pThread, dwMilliseconds); if (internalSleepRet != 0) { @@ -282,7 +217,7 @@ SleepEx(IN DWORD dwMilliseconds, CPalThread * pThread = InternalGetCurrentThread(); - dwRet = InternalSleepEx(pThread, dwMilliseconds, bAlertable); + dwRet = InternalSleepEx(pThread, dwMilliseconds); LOGEXIT("SleepEx returns DWORD %u\n", dwRet); PERF_EXIT(SleepEx); @@ -290,80 +225,17 @@ SleepEx(IN DWORD dwMilliseconds, return dwRet; } -/*++ -Function: - QueueUserAPC - -See MSDN doc. ---*/ -DWORD -PALAPI -QueueUserAPC( - PAPCFUNC pfnAPC, - HANDLE hThread, - ULONG_PTR dwData) -{ - CPalThread * pCurrentThread = NULL; - CPalThread * pTargetThread = NULL; - IPalObject * pTargetThreadObject = NULL; - PAL_ERROR palErr; - DWORD dwRet; - - PERF_ENTRY(QueueUserAPC); - ENTRY("QueueUserAPC(pfnAPC=%p, hThread=%p, dwData=%#x)\n", - pfnAPC, hThread, dwData); - - /* NOTE: Windows does not check the validity of pfnAPC, even if it is - NULL. It just does an access violation later on when the APC call - is attempted */ - - pCurrentThread = InternalGetCurrentThread(); - - palErr = InternalGetThreadDataFromHandle( - pCurrentThread, - hThread, - &pTargetThread, - &pTargetThreadObject - ); - - if (NO_ERROR != palErr) - { - ERROR("Unable to obtain thread data for handle %p (error %x)!\n", - hThread, palErr); - goto QueueUserAPC_exit; - } - - - palErr = g_pSynchronizationManager->QueueUserAPC(pCurrentThread, pTargetThread, - pfnAPC, dwData); - -QueueUserAPC_exit: - if (pTargetThreadObject) - { - pTargetThreadObject->ReleaseReference(pCurrentThread); - } - - dwRet = (NO_ERROR == palErr) ? 1 : 0; - - LOGEXIT("QueueUserAPC returns DWORD %d\n", dwRet); - PERF_EXIT(QueueUserAPC); - return dwRet; -} - DWORD CorUnix::InternalWaitForMultipleObjectsEx( CPalThread * pThread, DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, - DWORD dwMilliseconds, - BOOL bAlertable, - BOOL bPrioritize) + DWORD dwMilliseconds) { 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 +289,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) @@ -496,40 +319,12 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( goto WFMOExIntCleanup; } - if (bAlertable) - { - // First check for pending APC. We need to do that while holding the global - // synch lock implicitely grabbed by GetSynchWaitControllersForObjects - if (g_pSynchronizationManager->AreAPCsPending(pThread)) - { - // If there is any pending APC we need to release the - // implicit global synch lock before calling into it - for (i = 0; (i < (int)nCount) && (NULL != ppISyncWaitCtrlrs[i]); i++) - { - ppISyncWaitCtrlrs[i]->ReleaseController(); - ppISyncWaitCtrlrs[i] = NULL; - } - palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); - if (NO_ERROR == palErr) - { - dwRet = WAIT_IO_COMPLETION; - } - else - { - ASSERT("Awakened for APC, but no APC is pending\n"); - pThread->SetLastError(ERROR_INTERNAL_ERROR); - dwRet = WAIT_FAILED; - } - goto WFMOExIntCleanup; - } - } - iSignaledObjCount = 0; 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 +332,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( pThread->SetLastError(ERROR_INTERNAL_ERROR); goto WFMOExIntReleaseControllers; } - if (fWaitObjectAbandoned) - { - fAbandoned = true; - } if (fValue) { iSignaledObjCount++; @@ -589,7 +380,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( } } - dwRet = (fAbandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0); + dwRet = WAIT_OBJECT_0; } else if (0 == dwMilliseconds) { @@ -604,9 +395,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( { palErr = ppISyncWaitCtrlrs[i]->RegisterWaitingThread( wtWaitType, - i, - (TRUE == bAlertable), - bPrioritize != FALSE); + i); if (NO_ERROR != palErr) { ERROR("RegisterWaitingThread() failed for %d-th object " @@ -636,7 +425,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( // palErr = g_pSynchronizationManager->BlockThread(pThread, dwMilliseconds, - (TRUE == bAlertable), false, &twrWakeupReason, (DWORD *)&iSignaledObjIndex); @@ -655,22 +443,9 @@ 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; - case Alerted: - _ASSERT_MSG(bAlertable, - "Awakened for APC from a non-alertable wait\n"); - - dwRet = WAIT_IO_COMPLETION; - palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); - - _ASSERT_MSG(NO_ERROR == palErr, - "Awakened for APC, but no APC is pending\n"); - break; case WaitFailed: default: ERROR("Thread %p awakened with some failure\n", pThread); @@ -679,14 +454,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) { @@ -714,142 +489,21 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx( return dwRet; } -DWORD CorUnix::InternalSignalObjectAndWait( - CPalThread *thread, - HANDLE hObjectToSignal, - HANDLE hObjectToWaitOn, - DWORD dwMilliseconds, - BOOL bAlertable) -{ - DWORD result = WAIT_FAILED; - PAL_ERROR palError = NO_ERROR; - IPalObject *objectToSignal = nullptr; - IPalObject *objectToWaitOn = nullptr; - - // Validate and add a reference to the object to signal - palError = - g_pObjectManager->ReferenceObjectByHandle( - thread, - hObjectToSignal, - &sg_aotSignalableObject, - &objectToSignal); - if (palError != NO_ERROR) - { - ERROR("Unable to obtain object for handle %p (error %u)!\n", hObjectToSignal, palError); - goto InternalSignalObjectAndWait_Error; - } - - // Validate and add a reference to the object to wait on. Error checking is done before signaling. - palError = - g_pObjectManager->ReferenceObjectByHandle( - thread, - hObjectToWaitOn, - &sg_aotWaitObject, - &objectToWaitOn); - if (palError != NO_ERROR) - { - ERROR("Unable to obtain object for handle %p (error %u)!\n", hObjectToWaitOn, palError); - goto InternalSignalObjectAndWait_Error; - } - - // Signal - switch (objectToSignal->GetObjectType()->GetId()) - { - case otiAutoResetEvent: - case otiManualResetEvent: - 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; - - default: - palError = ERROR_INVALID_HANDLE; - break; - } - if (palError != NO_ERROR) - { - ERROR("Unable to signal object for handle %p (error %u)!\n", hObjectToSignal, palError); - goto InternalSignalObjectAndWait_Error; - } - objectToSignal->ReleaseReference(thread); - objectToSignal = nullptr; - - // Wait - result = - InternalWaitForMultipleObjectsEx( - thread, - 1 /* nCount */, - &hObjectToWaitOn, - false /* bWaitAll */, - dwMilliseconds, - bAlertable); - if (result == WAIT_FAILED) - { - ERROR("Unable to wait on object for handle %p (error %u)!\n", hObjectToWaitOn, palError); - goto InternalSignalObjectAndWait_Error; - } - objectToWaitOn->ReleaseReference(thread); - objectToWaitOn = nullptr; - - goto InternalSignalObjectAndWait_Exit; - -InternalSignalObjectAndWait_Error: - if (objectToSignal != nullptr) - { - objectToSignal->ReleaseReference(thread); - } - if (objectToWaitOn != nullptr) - { - objectToWaitOn->ReleaseReference(thread); - } - - if (palError != NO_ERROR) - { - _ASSERTE(result == WAIT_FAILED); - thread->SetLastError(palError); - } - -InternalSignalObjectAndWait_Exit: - LOGEXIT("InternalSignalObjectAndWait returns %u\n", result); - return result; -} - DWORD CorUnix::InternalSleepEx ( CPalThread * pThread, - DWORD dwMilliseconds, - BOOL bAlertable) + DWORD dwMilliseconds) { PAL_ERROR palErr = NO_ERROR; DWORD dwRet = WAIT_FAILED; int iSignaledObjIndex; - TRACE("Sleeping %u ms [bAlertable=%d]", dwMilliseconds, (int)bAlertable); - - if (bAlertable) - { - // In this case do not use AreAPCsPending. In fact, since we are - // not holding the synch lock(s) an APC posting may race with - // AreAPCsPending. - palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread); - if (NO_ERROR == palErr) - { - return WAIT_IO_COMPLETION; - } - } + TRACE("Sleeping %u ms", dwMilliseconds); if (dwMilliseconds > 0) { ThreadWakeupReason twrWakeupReason; palErr = g_pSynchronizationManager->BlockThread(pThread, dwMilliseconds, - (TRUE == bAlertable), true, &twrWakeupReason, (DWORD *)&iSignaledObjIndex); @@ -866,17 +520,7 @@ DWORD CorUnix::InternalSleepEx ( case WaitTimeout: dwRet = 0; break; - case Alerted: - _ASSERT_MSG(bAlertable, "Awakened for APC from a non-alertable wait\n"); - - dwRet = WAIT_IO_COMPLETION; - 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: ERROR("Thread %p awakened with some failure\n", pThread); @@ -889,7 +533,7 @@ DWORD CorUnix::InternalSleepEx ( dwRet = 0; } - TRACE("Done sleeping %u ms [bAlertable=%d]", dwMilliseconds, (int)bAlertable); + TRACE("Done sleeping %u ms", dwMilliseconds); return dwRet; } 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 12ba9ac544f849..72a810d54a2aa7 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 0af33b35734634..39c6cf86eefa97 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. @@ -1587,13 +1571,6 @@ CPalThread::ThreadEntry( ASSERT("Error %i attempting to suspend new thread\n", palError); goto fail; } - - // - // We need to run any APCs that have already been queued for - // this thread. - // - - (void) g_pSynchronizationManager->DispatchPendingAPCs(pThread); } else { @@ -2076,12 +2053,6 @@ CPalThread::RunPreCreateInitializers( goto RunPreCreateInitializersExit; } - palError = apcInfo.InitializePreCreate(); - if (NO_ERROR != palError) - { - goto RunPreCreateInitializersExit; - } - RunPreCreateInitializersExit: return palError; @@ -2168,12 +2139,6 @@ CPalThread::RunPostCreateInitializers( goto RunPostCreateInitializersExit; } - palError = apcInfo.InitializePostCreate(this, m_threadId, m_dwLwpId); - if (NO_ERROR != palError) - { - goto RunPostCreateInitializersExit; - } - palError = SEHEnable(this); if (NO_ERROR != palError) { 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 c61121186f831c..97ff9e790da8a5 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 @@ -374,11 +368,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 @@ -391,12 +380,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 @@ -413,25 +399,14 @@ 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 threading/QueryThreadCycleTime/test1/test1.cpp - threading/QueueUserAPC/test1/test1.cpp - threading/QueueUserAPC/test2/test2.cpp - threading/QueueUserAPC/test3/test3.cpp - threading/QueueUserAPC/test4/test4.cpp - 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 @@ -442,28 +417,17 @@ add_executable_clr(paltests threading/SetEvent/test2/test2.cpp threading/SetEvent/test3/test3.cpp threading/SetEvent/test4/test4.cpp - threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp threading/Sleep/test1/Sleep.cpp threading/Sleep/test2/sleep.cpp threading/SleepEx/test1/test1.cpp - threading/SleepEx/test2/test2.cpp threading/SwitchToThread/test1/test1.cpp threading/TerminateProcess/test1/TerminateProcess.cpp threading/ThreadPriority/test1/ThreadPriority.cpp 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 c9de088fd78892..8498d27b325587 100644 --- a/src/coreclr/pal/tests/palsuite/compilableTests.txt +++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt @@ -276,11 +276,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 @@ -291,11 +286,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 @@ -310,22 +303,12 @@ 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 -threading/QueueUserAPC/test1/paltest_queueuserapc_test1 -threading/QueueUserAPC/test2/paltest_queueuserapc_test2 -threading/QueueUserAPC/test3/paltest_queueuserapc_test3 -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 @@ -336,25 +319,18 @@ threading/SetEvent/test1/paltest_setevent_test1 threading/SetEvent/test2/paltest_setevent_test2 threading/SetEvent/test3/paltest_setevent_test3 threading/SetEvent/test4/paltest_setevent_test4 -threading/SignalObjectAndWait/paltest_signalobjectandwaittest threading/Sleep/test1/paltest_sleep_test1 threading/Sleep/test2/paltest_sleep_test2 threading/SleepEx/test1/paltest_sleepex_test1 -threading/SleepEx/test2/paltest_sleepex_test2 threading/SwitchToThread/test1/paltest_switchtothread_test1 threading/TerminateProcess/test1/paltest_terminateprocess_test1 threading/ThreadPriority/test1/paltest_threadpriority_test1 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 threading/WaitForSingleObject/WFSOProcessTest/paltest_waitforsingleobject_wfsoprocesstest threading/WaitForSingleObject/WFSOSemaphoreTest/paltest_waitforsingleobject_wfsosemaphoretest 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 035f8b33a14875..660307915e7bb6 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt @@ -246,9 +246,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 @@ -257,7 +254,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 @@ -267,15 +263,7 @@ 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 -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 @@ -290,13 +278,8 @@ threading/SwitchToThread/test1/paltest_switchtothread_test1 threading/ThreadPriority/test1/paltest_threadpriority_test1 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 threading/WaitForSingleObject/WFSOSemaphoreTest/paltest_waitforsingleobject_wfsosemaphoretest threading/WaitForSingleObject/WFSOThreadTest/paltest_waitforsingleobject_wfsothreadtest 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..e890e3cd5ee083 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,13 +82,10 @@ 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 threading/Sleep/test1/paltest_sleep_test1 threading/SleepEx/test1/paltest_sleepex_test1 -threading/SleepEx/test2/paltest_sleepex_test2 threading/TerminateProcess/test1/paltest_terminateprocess_test1 threading/WaitForMultipleObjectsEx/test5/paltest_waitformultipleobjectsex_test5 threading/WaitForMultipleObjectsEx/test6/paltest_waitformultipleobjectsex_test6 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/QueueUserAPC/test1/test1.cpp b/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.cpp deleted file mode 100644 index 8e5ff257d96357..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test1/test1.cpp +++ /dev/null @@ -1,312 +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: Tests that APCs sent to a thread in an alertable state via -** QueueUserAPC are executed in FIFO order. Also tests that the APC -** function is executed within the context of the correct thread and -** that the dwData parameter gets sent correctly. -** -** -**===================================================================*/ - -#include - -const int ChildThreadSleepTime = 2000; -const int InterruptTime = 1000; - -VOID PALAPI APCFuncA(ULONG_PTR dwParam); -VOID PALAPI APCFuncB(ULONG_PTR dwParam); -VOID PALAPI APCFuncC(ULONG_PTR dwParam); -VOID PALAPI APCFuncD(ULONG_PTR dwParam); -DWORD PALAPI SleeperProc_QueueUserAPC_test1(LPVOID lpParameter); - -const char *ExpectedResults_QueueUserAPC_test1 = "A0B0C0D0A1B1C1D1A2B2C2D2A3B3C3D3"; -char ResultBuffer_QueueUserAPC_test1[256]; -char *ResultPtr_QueueUserAPC_test1; -DWORD ChildThread_QueueUserAPC_test1; - -/* synchronization events */ -static HANDLE hSyncEvent1_QueueUserAPC_test1 = NULL; -static HANDLE hSyncEvent2_QueueUserAPC_test1 = NULL; - -/* thread result because we have no GetExitCodeThread() API */ -BOOL bThreadResult_QueueUserAPC_test1 = FAIL; - -PALTEST(threading_QueueUserAPC_test1_paltest_queueuserapc_test1, "threading/QueueUserAPC/test1/paltest_queueuserapc_test1") -{ - HANDLE hThread = NULL; - int ret; - int i,j; - BOOL bResult = FAIL; - - PAPCFUNC APCFuncs[] = - { - APCFuncA, - APCFuncB, - APCFuncC, - APCFuncD, - }; - - /* initialize the PAL */ - if (0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - ResultPtr_QueueUserAPC_test1 = ResultBuffer_QueueUserAPC_test1; - - /* create a pair of synchronization events to coordinate our threads */ - hSyncEvent1_QueueUserAPC_test1 = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( hSyncEvent1_QueueUserAPC_test1 == NULL ) - { - Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - goto cleanup; - } - - hSyncEvent2_QueueUserAPC_test1 = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( hSyncEvent2_QueueUserAPC_test1 == NULL ) - { - Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - goto cleanup; - } - - /* create a child thread which will call SleepEx */ - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)SleeperProc_QueueUserAPC_test1, - 0, - 0, - &ChildThread_QueueUserAPC_test1); - - if( hThread == NULL ) - { - Trace( "ERROR:%lu:CreateThread() call failed\n", - GetLastError()); - goto cleanup; - } - - - /* wait on our synchronization event to ensure the thread is running */ - ret = WaitForSingleObject( hSyncEvent1_QueueUserAPC_test1, 20000 ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - goto cleanup; - } - - - /* queue our user APC functions on the thread */ - for (i=0; i<4; i++) - { - for (j=0; j - -const int ChildThreadSleepTime = 2000; -const int InterruptTime = 1000; - -DWORD ChildThread; -BOOL InAPC; - -/* synchronization events */ -static HANDLE hSyncEvent1 = NULL; -static HANDLE hSyncEvent2 = NULL; - -/* thread result because we have no GetExitCodeThread() API */ -static BOOL bThreadResult = FAIL; - - -VOID PALAPI APCFunc_QueueUserAPC_test2(ULONG_PTR dwParam) -{ - InAPC = TRUE; -} - -DWORD PALAPI SleeperProc_QueueUserAPC_test2(LPVOID lpParameter) -{ - DWORD ret; - - /* signal the main thread that we're ready to proceed */ - if( ! SetEvent( hSyncEvent1 ) ) - { - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - bThreadResult = FAIL; - goto done; - } - - /* wait for notification from the main thread */ - ret = WaitForSingleObject( hSyncEvent2, 20000 ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - bThreadResult = FAIL; - goto done; - } - - /* call our sleep function */ - Sleep( ChildThreadSleepTime ); - - /* success if we reach here */ - bThreadResult = PASS; - - -done: - - /* signal the main thread that we're finished */ - if( ! SetEvent( hSyncEvent1 ) ) - { - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - bThreadResult = FAIL; - } - - /* return success or failure */ - return bThreadResult; -} - - -PALTEST(threading_QueueUserAPC_test2_paltest_queueuserapc_test2, "threading/QueueUserAPC/test2/paltest_queueuserapc_test2") -{ - /* local variables */ - HANDLE hThread = 0; - int ret; - BOOL bResult = FAIL; - - /* initialize the PAL */ - if (0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - InAPC = FALSE; - - /* create a pair of synchronization events to coordinate our threads */ - hSyncEvent1 = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( hSyncEvent1 == NULL ) - { - Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - goto cleanup; - } - - hSyncEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( hSyncEvent2 == NULL ) - { - Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - goto cleanup; - } - - /* create a child thread */ - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)SleeperProc_QueueUserAPC_test2, - 0, - 0, - &ChildThread); - - if (hThread == NULL) - { - Trace( "ERROR:%lu:CreateThread() call failed\n", - GetLastError()); - goto cleanup; - } - - - /* wait on our synchronization event to ensure the thread is running */ - ret = WaitForSingleObject( hSyncEvent1, 20000 ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - goto cleanup; - } - - /* queue a user APC on the child thread */ - ret = QueueUserAPC(APCFunc_QueueUserAPC_test2, hThread, 0); - if (ret == 0) - { - Trace( "ERROR:%lu:QueueUserAPC() call failed\n", - GetLastError()); - goto cleanup; - } - - /* signal the child thread to continue */ - if( ! SetEvent( hSyncEvent2 ) ) - { - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - goto cleanup; - } - - /* wait on our synchronization event to ensure the other thread is done */ - ret = WaitForSingleObject( hSyncEvent1, 20000 ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - goto cleanup; - } - - /* check that the thread executed successfully */ - if( bThreadResult == FAIL ) - { - goto cleanup; - } - - - /* check whether the APC function was executed */ - if( InAPC ) - { - Trace( "FAIL:APC function was executed but shouldn't have been\n" ); - goto cleanup; - } - - /* success if we reach here */ - bResult = PASS; - - -cleanup: - /* wait for the other thread to finish */ - if( hThread != NULL ) - { - ret = WaitForSingleObject( hThread, INFINITE ); - if (ret == WAIT_FAILED) - { - Trace( "ERROR:%lu:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - bResult = FAIL; - } - } - - /* close our synchronization handles */ - if( hSyncEvent1 != NULL ) - { - if( ! CloseHandle( hSyncEvent1 ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - bResult = FAIL; - } - } - - if( hSyncEvent2 != NULL ) - { - if( ! CloseHandle( hSyncEvent2 ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - bResult = FAIL; - } - } - - if( bResult == FAIL ) - { - Fail( "test failed\n" ); - } - - - /* terminate the PAL */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.cpp b/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.cpp deleted file mode 100644 index d1265191b55e6d..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test3/test3.cpp +++ /dev/null @@ -1,32 +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 how QueueUserAPC handles an invalid thread. -** -** -**===================================================================*/ - -#include - -PALTEST(threading_QueueUserAPC_test3_paltest_queueuserapc_test3, "threading/QueueUserAPC/test3/paltest_queueuserapc_test3") -{ - int ret; - - if (0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - ret = QueueUserAPC(NULL, NULL, 0); - if (ret != 0) - { - Fail("QueueUserAPC passed with an invalid thread!\n"); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.cpp b/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.cpp deleted file mode 100644 index 5a4add3a81176c..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test4/test4.cpp +++ /dev/null @@ -1,71 +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 -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** GetCurrentThread -** SleepEx -** -** Purpose: -** -** Test to ensure proper operation of the QueueUserAPC() -** API by trying to queue APC functions on the current -** thread. -** -** -**===========================================================================*/ -#include - - -static BOOL bAPCExecuted_QueueUserAPC_test4 = FALSE; - -VOID PALAPI APCFunc_QueueUserAPC_test4( ULONG_PTR dwParam ) -{ - bAPCExecuted_QueueUserAPC_test4 = TRUE; -} - -PALTEST(threading_QueueUserAPC_test4_paltest_queueuserapc_test4, "threading/QueueUserAPC/test4/paltest_queueuserapc_test4") - -{ - /* local variables */ - HANDLE hThread = NULL; - DWORD ret; - - /* PAL initialization */ - if( (PAL_Initialize(argc, argv)) != 0 ) - { - return( FAIL ); - } - - /* get the current thread */ - hThread = GetCurrentThread(); - ret = QueueUserAPC( APCFunc_QueueUserAPC_test4, hThread, 0 ); - if( ret == 0 ) - { - Fail( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() ); - } - - /* call SleepEx() to put the thread in an alertable state */ - ret = SleepEx( 2000, TRUE ); - if( ret != WAIT_IO_COMPLETION ) - { - Fail( "ERROR:Expected sleep to return WAIT_IO_COMPLETION, got %lu\n", - ret ); - } - - /* check that the APC function was executed */ - if( bAPCExecuted_QueueUserAPC_test4 == FALSE ) - { - Fail( "ERROR:APC function was not executed\n" ); - } - - /* PAL termination */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.cpp b/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.cpp deleted file mode 100644 index f31773947eb46e..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test5/test5.cpp +++ /dev/null @@ -1,199 +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: test5.c -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** CreateEvent -** SetEvent -** CreateThread -** ResumeThread -** WaitForSingleObject -** CloseHandle -** -** Purpose: -** -** Test to ensure proper operation of the QueueUserAPC() -** API by trying to queue and activate APC functions on -** a thread that was created suspended, prior to resuming -** it. We're verifying the following behavior: -** -** "If an application queues an APC before the thread begins -** running, the thread begins by calling the APC function. -** After the thread calls an APC function, it calls the APC -** functions for all APCs in its APC queue." -** -** -**===========================================================================*/ -#include - - -static HANDLE hEvent_QueueUserAPC_test5 = NULL; -static BOOL bAPCExecuted_QueueUserAPC_test5 = FALSE; - -VOID PALAPI APCFunc_QueueUserAPC_test5( ULONG_PTR dwParam ) -{ - bAPCExecuted_QueueUserAPC_test5 = TRUE; -} - -/** - * ThreadFunc - * - * Dummy thread function for APC queuing. - */ -DWORD PALAPI ThreadFunc_QueueUserAPC_test5( LPVOID param ) -{ - DWORD ret = 0; - - /* alertable wait until the global event is signalled */ - ret = WaitForSingleObject( hEvent_QueueUserAPC_test5, INFINITE ); - if( ret != WAIT_OBJECT_0 ) - { - Fail( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - } - - return 0; -} - - -PALTEST(threading_QueueUserAPC_test5_paltest_queueuserapc_test5, "threading/QueueUserAPC/test5/paltest_queueuserapc_test5") - -{ - /* local variables */ - HANDLE hThread = NULL; - DWORD IDThread; - DWORD ret; - BOOL bResult = FALSE; - - /* PAL initialization */ - if( (PAL_Initialize(argc, argv)) != 0 ) - { - return( FAIL ); - } - - /* create an event for the other thread to wait on */ - hEvent_QueueUserAPC_test5 = CreateEvent( NULL, TRUE, FALSE, NULL ); - if( hEvent_QueueUserAPC_test5 == NULL ) - { - Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - } - - /* run another dummy thread to cause notification of the library */ - hThread = CreateThread( NULL, /* no security attributes */ - 0, /* use default stack size */ - (LPTHREAD_START_ROUTINE) ThreadFunc_QueueUserAPC_test5, /* thread function */ - (LPVOID) NULL, /* pass thread index as */ - /* function argument */ - CREATE_SUSPENDED, /* create suspended */ - &IDThread ); /* returns thread id */ - - /* Check the return value for success. */ - if( hThread == NULL ) - { - /* error creating thread */ - Trace( "ERROR:%lu:CreateThread call failed\n", GetLastError() ); - if( ! CloseHandle( hEvent_QueueUserAPC_test5 ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "test failed\n" ); - } - - /* queue our APC on the suspended thread */ - ret = QueueUserAPC( APCFunc_QueueUserAPC_test5, hThread, 0 ); - if( ret == 0 ) - { - Fail( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() ); - } - - /* wait on the suspended thread */ - ret = WaitForSingleObject( hThread, 2000 ); - if( ret != WAIT_TIMEOUT ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_TIMEOUT\n", - ret ); - goto cleanup; - } - - /* verify that the APC function was not executed */ - if( bAPCExecuted_QueueUserAPC_test5 == TRUE ) - { - Trace( "ERROR:APC function was executed for a suspended thread\n" ); - goto cleanup; - } - - /* Resume the suspended thread */ - ResumeThread( hThread ); - - /* do another wait on the resumed thread */ - ret = WaitForSingleObject( hThread, 2000 ); - - /* verify that we got a WAIT_TIMEOUT result */ - if( ret != WAIT_TIMEOUT ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_TIMEOUT\n", - ret ); - goto cleanup; - } - - /* check that the APC function was actually executed */ - if( bAPCExecuted_QueueUserAPC_test5 == FALSE ) - { - Trace( "ERROR:APC function was not executed\n" ); - goto cleanup; - } - - /* set the success flag */ - bResult = PASS; - -cleanup: - /* signal the event so the other thread will exit */ - if( ! SetEvent( hEvent_QueueUserAPC_test5 ) ) - { - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - bResult = FAIL; - } - - /* close the global event handle */ - if( ! CloseHandle( hEvent_QueueUserAPC_test5 ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - bResult = FAIL; - } - - /* wait on the other thread to complete */ - ret = WaitForSingleObject( hThread, 2000 ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - bResult = FAIL; - } - - /* close the thread handle */ - if( ! CloseHandle( hThread ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - bResult = FAIL; - } - - /* output final failure result for failure case */ - if( bResult == FAIL ) - { - Fail( "test failed\n" ); - } - - /* PAL termination */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.cpp b/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.cpp deleted file mode 100644 index 1bcbfbfb0a4f3e..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test6/test6.cpp +++ /dev/null @@ -1,128 +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: test6.c -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** CreateEvent -** SetEvent -** CreateThread -** ResumeThread -** WaitForMultipleObjectsEx -** CloseHandle -** -** Purpose: -** -** Test to ensure proper operation of the QueueUserAPC() -** API by trying to queue APC functions on a thread that -** has already terminated. -** -** -**===========================================================================*/ -#include - - -static BOOL bAPCExecuted_QueueUserAPC_test6 = FALSE; - -VOID PALAPI APCFunc_QueueUserAPC_test6( ULONG_PTR dwParam ) -{ - bAPCExecuted_QueueUserAPC_test6 = TRUE; -} - -/** - * ThreadFunc - * - * Dummy thread function for APC queuing. - */ -DWORD PALAPI ThreadFunc_QueueUserAPC_test6( LPVOID param ) -{ - int i; - - /* simulate some activity */ - for( i=0; i<250000; i++ ) - ; - - return 0; -} - - -PALTEST(threading_QueueUserAPC_test6_paltest_queueuserapc_test6, "threading/QueueUserAPC/test6/paltest_queueuserapc_test6") - -{ - /* local variables */ - HANDLE hThread = NULL; - DWORD IDThread; - DWORD ret; - - /* PAL initialization */ - if( (PAL_Initialize(argc, argv)) != 0 ) - { - return( FAIL ); - } - - /* run another dummy thread to cause notification of the library */ - hThread = CreateThread( NULL, /* no security attributes */ - 0, /* use default stack size */ - (LPTHREAD_START_ROUTINE) ThreadFunc_QueueUserAPC_test6, /* thread function */ - (LPVOID) NULL, /* pass thread index as */ - /* function argument */ - CREATE_SUSPENDED, /* create suspended */ - &IDThread ); /* returns thread id */ - - /* Check the return value for success. */ - if( hThread == NULL ) - { - /* error creating thread */ - Fail( "ERROR:%lu:CreateThread call failed\n", GetLastError() ); - } - - /* Resume the suspended thread */ - ResumeThread( hThread ); - - /* wait on the other thread to complete */ - ret = WaitForSingleObject( hThread, INFINITE ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - if( ! CloseHandle( hThread ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "test failed\n" ); - } - - /* queue our APC on the finished thread */ - ret = QueueUserAPC( APCFunc_QueueUserAPC_test6, hThread, 0 ); - if( ret != 0 ) - { - Trace( "ERROR:QueueUserAPC call succeeded on a terminated thread\n" ); - if( ! CloseHandle( hThread ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "test failed\n" ); - } - - if( ! CloseHandle( hThread ) ) - { - Fail( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - - /* dummy check that the APC function wasn't actually executed */ - if( bAPCExecuted_QueueUserAPC_test6 != FALSE ) - { - Fail( "ERROR:APC function was executed\n" ); - } - - - /* PAL termination */ - PAL_Terminate(); - - /* return success */ - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.cpp b/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.cpp deleted file mode 100644 index 555b4955ed3768..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/QueueUserAPC/test7/test7.cpp +++ /dev/null @@ -1,252 +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: test7.c -** -** Dependencies: PAL_Initialize -** PAL_Terminate -** CreateEvent -** SetEvent -** CreateThread -** ResumeThread -** WaitForMultipleObjectsEx -** CloseHandle -** -** Purpose: -** -** Test to ensure proper operation of the QueueUserAPC() -** API by trying to queue an APC function on a thread and -** activating it with WaitForMultipleObjectsEx. -** -** -**===========================================================================*/ -#include - - -static HANDLE hSyncEvent_QueueUserAPC_test7 = NULL; -static HANDLE hTestEvent_QueueUserAPC_test7 = NULL; -static int nAPCExecuted_QueueUserAPC_test7 = 0; -static BOOL bThreadResult_QueueUserAPC_test7 = FALSE; - -VOID PALAPI APCFunc_QueueUserAPC_test7( ULONG_PTR dwParam ) -{ - ++nAPCExecuted_QueueUserAPC_test7; -} - -/** - * ThreadFunc - * - * Dummy thread function for APC queuing. - */ -DWORD PALAPI ThreadFunc_QueueUserAPC_test7( LPVOID param ) -{ - DWORD ret = 0; - - /* pessimism */ - bThreadResult_QueueUserAPC_test7 = FALSE; - - /* set the sync event to notify the main thread */ - if( ! SetEvent( hSyncEvent_QueueUserAPC_test7 ) ) - { - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - goto done; - } - - /* wait until the test event is signalled */ - ret = WaitForSingleObject( hTestEvent_QueueUserAPC_test7, INFINITE ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - goto done; - } - - /* now do an alertable wait on the same event, which is now - in an unsignalled state */ - ret = WaitForMultipleObjectsEx( 1, &hTestEvent_QueueUserAPC_test7, TRUE, 2000, TRUE ); - - /* verify that we got a WAIT_IO_COMPLETION result */ - if( ret != WAIT_IO_COMPLETION ) - { - Trace( "ERROR:WaitForMultipleObjectsEx returned %lu, " - "expected WAIT_IO_COMPLETION\n", - ret ); - goto done; - } - - /* set the event again */ - if( ! SetEvent( hTestEvent_QueueUserAPC_test7 ) ) - { - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - goto done; - } - - /* do a non-alertable wait on the same event */ - ret = WaitForMultipleObjectsEx( 1, &hTestEvent_QueueUserAPC_test7, TRUE, INFINITE, FALSE ); - - /* verify that we got a WAIT_OBJECT_0 result */ - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForMultipleObjectsEx returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - goto done; - } - - /* success at this point */ - bThreadResult_QueueUserAPC_test7 = TRUE; - - -done: - return bThreadResult_QueueUserAPC_test7; -} - - -PALTEST(threading_QueueUserAPC_test7_paltest_queueuserapc_test7, "threading/QueueUserAPC/test7/paltest_queueuserapc_test7") - -{ - /* local variables */ - HANDLE hThread = NULL; - DWORD IDThread; - DWORD ret; - BOOL bResult = FALSE; - - /* PAL initialization */ - if( (PAL_Initialize(argc, argv)) != 0 ) - { - return( FAIL ); - } - - /* create an auto-reset event for the other thread to wait on */ - hTestEvent_QueueUserAPC_test7 = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( hTestEvent_QueueUserAPC_test7 == NULL ) - { - Fail( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - } - - /* create an auto-reset event for synchronization */ - hSyncEvent_QueueUserAPC_test7 = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( hSyncEvent_QueueUserAPC_test7 == NULL ) - { - Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - if( ! CloseHandle( hTestEvent_QueueUserAPC_test7 ) ) - { - Trace( "ERROR:%lu:CreateEvent() call failed\n", GetLastError() ); - } - Fail( "test failed\n" ); - } - - /* run another dummy thread to cause notification of the library */ - hThread = CreateThread( NULL, /* no security attributes */ - 0, /* use default stack size */ - (LPTHREAD_START_ROUTINE) ThreadFunc_QueueUserAPC_test7, /* thread function */ - (LPVOID) NULL, /* pass thread index as */ - /* function argument */ - CREATE_SUSPENDED, /* create suspended */ - &IDThread ); /* returns thread id */ - - /* Check the return value for success. */ - if( hThread == NULL ) - { - /* error creating thread */ - Trace( "ERROR:%lu:CreateThread call failed\n", GetLastError() ); - if( ! CloseHandle( hTestEvent_QueueUserAPC_test7 ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - } - Fail( "test failed\n" ); - } - - /* Resume the suspended thread */ - ResumeThread( hThread ); - - /* wait until the other thread is ready to proceed */ - ret = WaitForSingleObject( hSyncEvent_QueueUserAPC_test7, 10000 ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - goto cleanup; - } - - - /* now queue our APC on the test thread */ - ret = QueueUserAPC( APCFunc_QueueUserAPC_test7, hThread, 0 ); - if( ret == 0 ) - { - Trace( "ERROR:%lu:QueueUserAPC call failed\n", GetLastError() ); - goto cleanup; - } - - /* signal the test event so the other thread will proceed */ - if( ! SetEvent( hTestEvent_QueueUserAPC_test7 ) ) - { - Trace( "ERROR:%lu:SetEvent() call failed\n", GetLastError() ); - goto cleanup; - } - - /* wait on the other thread to complete */ - ret = WaitForSingleObject( hThread, INFINITE ); - if( ret != WAIT_OBJECT_0 ) - { - Trace( "ERROR:WaitForSingleObject() returned %lu, " - "expected WAIT_OBJECT_0\n", - ret ); - goto cleanup; - } - - /* check the result of the other thread */ - if( bThreadResult_QueueUserAPC_test7 == FALSE ) - { - goto cleanup; - } - - /* check that the APC function was actually executed exactly one time */ - if( nAPCExecuted_QueueUserAPC_test7 != 1 ) - { - Trace( "ERROR:APC function was executed %d times, " - "expected once\n", nAPCExecuted_QueueUserAPC_test7 ); - goto cleanup; - } - - /* set the success flag */ - bResult = PASS; - - -cleanup: - /* close the global event handles */ - if( ! CloseHandle( hTestEvent_QueueUserAPC_test7 ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - bResult = FAIL; - } - - if( ! CloseHandle( hSyncEvent_QueueUserAPC_test7 ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - bResult = FAIL; - } - - /* close the thread handle */ - if( ! CloseHandle( hThread ) ) - { - Trace( "ERROR:%lu:CloseHandle() call failed\n", GetLastError() ); - bResult = FAIL; - } - - /* output final failure result for failure case */ - if( bResult == FAIL ) - { - Fail( "test failed\n" ); - } - - /* PAL termination */ - PAL_Terminate(); - - /* return success */ - return PASS; -} 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 deleted file mode 100644 index 3601abb4b904b7..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/SignalObjectAndWait/SignalObjectAndWaitTest.cpp +++ /dev/null @@ -1,413 +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 - -enum class SignalableObjectType -{ - First = 0, - - Invalid = First, - ManualResetEvent, - AutoResetEvent, - Semaphore, - FullSemaphore, - Mutex, - UnlockedMutex, - - Last = UnlockedMutex -}; - -enum class WaitableObjectType -{ - First = 0, - - Invalid = First, - ManualResetEvent, - UnsignaledManualResetEvent, - AutoResetEvent, - UnsignaledAutoResetEvent, - Semaphore, - EmptySemaphore, - Mutex, - LockedMutex, - - Last = LockedMutex -}; - -void operator ++(SignalableObjectType &objectType) -{ - ++(int &)objectType; -} - -void operator ++(WaitableObjectType &objectType) -{ - ++(int &)objectType; -} - -struct AssertionFailureException -{ - const int lineNumber; - const char *const expression; - SignalableObjectType signalableObjectType; - WaitableObjectType waitableObjectType; - DWORD waitResult; - DWORD errorCode; - - AssertionFailureException(int lineNumber, const char *expression) - : lineNumber(lineNumber), - expression(expression), - signalableObjectType(SignalableObjectType::Invalid), - waitableObjectType(WaitableObjectType::Invalid), - waitResult(WAIT_OBJECT_0), - errorCode(ERROR_SUCCESS) - { - } -}; - -#define TestAssert(expression) \ - do \ - { \ - if (!(expression)) \ - { \ - throw AssertionFailureException(__LINE__, "" #expression ""); \ - } \ - } while (false) - -HANDLE CreateObjectToSignal(SignalableObjectType objectType) -{ - switch (objectType) - { - case SignalableObjectType::Invalid: - return nullptr; - - case SignalableObjectType::ManualResetEvent: - return CreateEvent(nullptr, true, false, nullptr); - - case SignalableObjectType::AutoResetEvent: - return CreateEvent(nullptr, false, false, nullptr); - - case SignalableObjectType::Semaphore: - return CreateSemaphoreExW(nullptr, 0, 1, nullptr, 0, 0); - - 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); - } -} - -void VerifySignal(HANDLE h, SignalableObjectType objectType) -{ - switch (objectType) - { - case SignalableObjectType::ManualResetEvent: - TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); - break; - - case SignalableObjectType::AutoResetEvent: - TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0); - SetEvent(h); - break; - - case SignalableObjectType::Semaphore: - TestAssert(!ReleaseSemaphore(h, 1, nullptr)); - break; - - case SignalableObjectType::Mutex: - TestAssert(!ReleaseMutex(h)); - break; - - default: - TestAssert(false); - } -} - -void CloseObjectToSignal(HANDLE h, SignalableObjectType objectType) -{ - if (objectType != SignalableObjectType::Invalid) - { - CloseHandle(h); - } -} - -HANDLE CreateObjectToWaitOn(WaitableObjectType objectType) -{ - switch (objectType) - { - case WaitableObjectType::Invalid: - return nullptr; - - case WaitableObjectType::ManualResetEvent: - return CreateEvent(nullptr, true, true, nullptr); - - case WaitableObjectType::UnsignaledManualResetEvent: - return CreateEvent(nullptr, true, false, nullptr); - - case WaitableObjectType::AutoResetEvent: - return CreateEvent(nullptr, false, true, nullptr); - - case WaitableObjectType::UnsignaledAutoResetEvent: - return CreateEvent(nullptr, false, false, nullptr); - - case WaitableObjectType::Semaphore: - return CreateSemaphoreExW(nullptr, 1, 1, nullptr, 0, 0); - - 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); - } -} - -void VerifyWait(HANDLE h, WaitableObjectType objectType) -{ - switch (objectType) - { - case WaitableObjectType::ManualResetEvent: - case WaitableObjectType::UnsignaledManualResetEvent: - break; - - case WaitableObjectType::AutoResetEvent: - case WaitableObjectType::UnsignaledAutoResetEvent: - case WaitableObjectType::Semaphore: - case WaitableObjectType::EmptySemaphore: - 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); - } -} - -void CloseObjectToWaitOn(HANDLE h, WaitableObjectType objectType) -{ - switch (objectType) - { - case WaitableObjectType::ManualResetEvent: - case WaitableObjectType::UnsignaledManualResetEvent: - case WaitableObjectType::AutoResetEvent: - case WaitableObjectType::UnsignaledAutoResetEvent: - CloseHandle(h); - break; - - case WaitableObjectType::Semaphore: - case WaitableObjectType::EmptySemaphore: - ReleaseSemaphore(h, 1, nullptr); - CloseHandle(h); - break; - - case WaitableObjectType::Mutex: - ReleaseMutex(h); - CloseHandle(h); - break; - - case WaitableObjectType::LockedMutex: - ReleaseMutex(h); - ReleaseMutex(h); - CloseHandle(h); - break; - - default: - break; - } -} - -bool Verify(SignalableObjectType signalableObjectType, WaitableObjectType waitableObjectType, DWORD waitResult, DWORD errorCode) -{ - if (signalableObjectType == SignalableObjectType::Invalid || waitableObjectType == WaitableObjectType::Invalid) - { - TestAssert(waitResult == WAIT_FAILED); - TestAssert(errorCode == ERROR_INVALID_HANDLE); - return false; - } - - switch (signalableObjectType) - { - case SignalableObjectType::FullSemaphore: - TestAssert(waitResult == WAIT_FAILED); - TestAssert(errorCode == ERROR_TOO_MANY_POSTS); - return false; - - case SignalableObjectType::UnlockedMutex: - TestAssert(waitResult == WAIT_FAILED); - TestAssert(errorCode == ERROR_NOT_OWNER); - return false; - - default: - break; - } - - switch (waitableObjectType) - { - case WaitableObjectType::UnsignaledManualResetEvent: - case WaitableObjectType::UnsignaledAutoResetEvent: - case WaitableObjectType::EmptySemaphore: - TestAssert(waitResult == WAIT_TIMEOUT); - break; - - default: - TestAssert(waitResult == WAIT_OBJECT_0); - break; - } - TestAssert(errorCode == ERROR_SUCCESS); - return true; -} - -void Run(SignalableObjectType signalableObjectType, WaitableObjectType waitableObjectType) -{ - HANDLE objectToSignal = CreateObjectToSignal(signalableObjectType); - TestAssert(signalableObjectType == SignalableObjectType::Invalid || objectToSignal != nullptr); - HANDLE objectToWaitOn = CreateObjectToWaitOn(waitableObjectType); - TestAssert(waitableObjectType == WaitableObjectType::Invalid || objectToWaitOn != nullptr); - DWORD waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); - DWORD errorCode = waitResult == WAIT_FAILED ? GetLastError() : ERROR_SUCCESS; - - try - { - if (Verify(signalableObjectType, waitableObjectType, waitResult, errorCode)) - { - VerifySignal(objectToSignal, signalableObjectType); - VerifyWait(objectToWaitOn, waitableObjectType); - } - } - catch (AssertionFailureException ex) - { - ex.signalableObjectType = signalableObjectType; - ex.waitableObjectType = waitableObjectType; - ex.waitResult = waitResult; - ex.errorCode = errorCode; - throw ex; - } -} - -static bool s_apcCalled = false; - -void CALLBACK ApcCallback(ULONG_PTR dwParam) -{ - s_apcCalled = true; - HANDLE *objects = (HANDLE *)dwParam; - HANDLE objectToSignal = objects[0]; - HANDLE objectToWaitOn = objects[1]; - TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred - TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_OBJECT_0); // wait has not occurred yet - SetEvent(objectToWaitOn); -} - -void Run() -{ - for (SignalableObjectType signalableObjectType = SignalableObjectType::First; - signalableObjectType <= SignalableObjectType::Last; - ++signalableObjectType) - { - for (WaitableObjectType waitableObjectType = WaitableObjectType::First; - waitableObjectType <= WaitableObjectType::Last; - ++waitableObjectType) - { - Run(signalableObjectType, waitableObjectType); - } - } - - DWORD waitResult = WAIT_FAILED; - try - { - HANDLE objectToSignal = CreateObjectToSignal(SignalableObjectType::ManualResetEvent); - TestAssert(objectToSignal != nullptr); - HANDLE objectToWaitOn = CreateObjectToWaitOn(WaitableObjectType::AutoResetEvent); - TestAssert(objectToWaitOn != nullptr); - HANDLE objects[] = {objectToSignal, objectToWaitOn}; - - // Verify that a queued APC is not called if the wait is not alertable - QueueUserAPC(&ApcCallback, GetCurrentThread(), (ULONG_PTR)&objects); - waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, false); - TestAssert(waitResult == WAIT_OBJECT_0); - TestAssert(!s_apcCalled); - TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred - TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_TIMEOUT); // wait has occurred - - // Verify that signal, call APC, wait, occur in that order - ResetEvent(objectToSignal); - SetEvent(objectToWaitOn); - waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); - TestAssert(waitResult == WAIT_IO_COMPLETION); - TestAssert(s_apcCalled); - TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred - TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_OBJECT_0); // wait has not occurred yet - s_apcCalled = false; - ResetEvent(objectToSignal); - SetEvent(objectToWaitOn); - waitResult = SignalObjectAndWait(objectToSignal, objectToWaitOn, 0, true); - TestAssert(waitResult == WAIT_OBJECT_0); - TestAssert(!s_apcCalled); - TestAssert(WaitForSingleObject(objectToSignal, 0) == WAIT_OBJECT_0); // signal has occurred - TestAssert(WaitForSingleObject(objectToWaitOn, 0) == WAIT_TIMEOUT); // wait has occurred - - CloseHandle(objectToSignal); - CloseHandle(objectToWaitOn); - } - catch (AssertionFailureException ex) - { - ex.signalableObjectType = SignalableObjectType::ManualResetEvent; - ex.waitableObjectType = WaitableObjectType::AutoResetEvent; - ex.waitResult = waitResult; - throw ex; - } -} - -PALTEST(threading_SignalObjectAndWait_paltest_signalobjectandwaittest, "threading/SignalObjectAndWait/paltest_signalobjectandwaittest") -{ - if (PAL_Initialize(argc, argv) != 0) - { - return FAIL; - } - - int testReturnCode = PASS; - try - { - Run(); - } - catch (AssertionFailureException ex) - { - printf( - "SignalObjectAndWaitTest - Assertion failure (line %d, signalable object type %d, waitable object type %d, wait result 0x%x, error code %u): '%s'\n", - ex.lineNumber, - (int)ex.signalableObjectType, - (int)ex.waitableObjectType, - ex.waitResult, - ex.errorCode, - ex.expression); - fflush(stdout); - testReturnCode = FAIL; - } - - PAL_TerminateEx(testReturnCode); - return testReturnCode; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/SleepEx/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/threading/SleepEx/test2/test2.cpp deleted file mode 100644 index 15e11174b65ae5..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/SleepEx/test2/test2.cpp +++ /dev/null @@ -1,186 +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: Tests that a child thread in the middle of a SleepEx call will be -** interrupted by QueueUserAPC if the alert flag was set. -** -** -**===================================================================*/ - -#include - -const int ChildThreadSleepTime = 2000; -const int InterruptTime = 1000; -/* We need to keep in mind that BSD has a timer resolution of 10ms, so - we need to adjust our delta to keep that in mind. Besides we need some - tolerance to account for different scheduling strategies, heavy load - scenarios, etc. - - Real-world data also tells us we can expect a big difference between - values when run on real iron vs run in a hypervisor. - - Thread-interruption times when run on bare metal will typically yield - around 0ms on Linux and between 0 and 16ms on FreeBSD. However, when run - in a hypervisor (like VMWare ESXi) we may get values around an order of - magnitude higher, up to 110 ms for some tests. -*/ -const DWORD AcceptableDelta = 150; - -const int Iterations = 5; - -void RunTest_SleepEx_test2(BOOL AlertThread); -VOID PALAPI APCFunc_SleepEx_test2(ULONG_PTR dwParam); -DWORD PALAPI SleeperProc_SleepEx_test2(LPVOID lpParameter); - -DWORD ThreadSleepDelta; -static volatile bool s_preWaitTimestampRecorded = false; - -PALTEST(threading_SleepEx_test2_paltest_sleepex_test2, "threading/SleepEx/test2/paltest_sleepex_test2") -{ - int i; - DWORD dwAvgDelta; - - 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); - - /* - * Check that Queueing an APC in the middle of a sleep does interrupt - * it, if it's in an alertable state. - */ - dwAvgDelta = 0; - for (i=0;i AcceptableDelta) - { - Fail("Expected thread to sleep for %d ms (and get interrupted).\n" - "Average delta: %u ms, acceptable delta: %u\n", - InterruptTime, dwAvgDelta, AcceptableDelta); - } - - /* - * Check that Queueing an APC in the middle of a sleep does NOT interrupt - * it, if it is not in an alertable state. - */ - dwAvgDelta = 0; - for (i=0;i AcceptableDelta) - { - Fail("Expected thread to sleep for %d ms (and not be interrupted).\n" - "Average delta: %u ms, acceptable delta: %u\n", - ChildThreadSleepTime, dwAvgDelta, AcceptableDelta); - } - - PAL_Terminate(); - return PASS; -} - -void RunTest_SleepEx_test2(BOOL AlertThread) -{ - HANDLE hThread = 0; - DWORD dwThreadId = 0; - int ret; - - s_preWaitTimestampRecorded = false; - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)SleeperProc_SleepEx_test2, - (LPVOID) AlertThread, - 0, - &dwThreadId); - - if (hThread == NULL) - { - Fail("ERROR: Was not able to create the thread to test SleepEx!\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); - } - - if (SleepEx(InterruptTime, FALSE) != 0) - { - Fail("The creating thread did not sleep!\n"); - } - - ret = QueueUserAPC(APCFunc_SleepEx_test2, 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.", - GetLastError()); - } -} - -/* Function doesn't do anything, just needed to interrupt SleepEx */ -VOID PALAPI APCFunc_SleepEx_test2(ULONG_PTR dwParam) -{ - -} - -/* Entry Point for child thread. */ -DWORD PALAPI SleeperProc_SleepEx_test2(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 = SleepEx(ChildThreadSleepTime, Alertable); - - NewTimeStamp = minipal_hires_ticks(); - - if (Alertable && ret != WAIT_IO_COMPLETION) - { - Fail("Expected the interrupted sleep to return WAIT_IO_COMPLETION.\n" - "Got %d\n", ret); - } - else if (!Alertable && ret != 0) - { - Fail("Sleep did not timeout. Expected return of 0, got %d.\n", ret); - } - - - ThreadSleepDelta = (NewTimeStamp - OldTimeStamp) / (minipal_hires_tick_frequency() / 1000); - - return 0; -} diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.cpp deleted file mode 100644 index 776cd621d42389..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForMultipleObjectsEx/test2/test2.cpp +++ /dev/null @@ -1,191 +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: Tests that a child thread in the middle of a -** WaitForMultipleObjectsEx call will be interrupted by QueueUserAPC -** if the alert flag was set. -** -** -**===================================================================*/ - -#include - -/* Based on SleepEx/test2 */ - -const unsigned int ChildThreadWaitTime = 1000; -const unsigned int InterruptTime = 500; - -#define TOLERANCE 10 - -void RunTest_WFMO_test2(BOOL AlertThread); -VOID PALAPI APCFunc_WFMO_test2(ULONG_PTR dwParam); -DWORD PALAPI WaiterProc_WFMO_test2(LPVOID lpParameter); - -DWORD ThreadWaitDelta_WFMO_test2; -static volatile bool s_preWaitTimestampRecorded = false; - -PALTEST(threading_WaitForMultipleObjectsEx_test2_paltest_waitformultipleobjectsex_test2, "threading/WaitForMultipleObjectsEx/test2/paltest_waitformultipleobjectsex_test2") -{ - - DWORD delta = 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); - - - /* - * Check that Queueing an APC in the middle of a wait does interrupt - * it, if it's in an alertable state. - */ - RunTest_WFMO_test2(TRUE); - // Make sure that the wait returns in time greater than interrupt and less than - // wait timeout - if ( - ((ThreadWaitDelta_WFMO_test2 >= ChildThreadWaitTime) && (ThreadWaitDelta_WFMO_test2 - ChildThreadWaitTime) > TOLERANCE) - || (( ThreadWaitDelta_WFMO_test2 < InterruptTime) && (InterruptTime - ThreadWaitDelta_WFMO_test2) > TOLERANCE) - ) - { - Fail("Expected thread to wait for %d ms (and get interrupted).\n" - "Interrupt Time: %d ms, ThreadWaitDelta %u\n", - ChildThreadWaitTime, InterruptTime, ThreadWaitDelta_WFMO_test2); - } - - /* - * Check that Queueing an APC in the middle of a wait does NOT interrupt - * it, if it is not in an alertable state. - */ - RunTest_WFMO_test2(FALSE); - - // Make sure that time taken for thread to return from wait is more than interrupt - // and also not less than the complete child thread wait time - - delta = ThreadWaitDelta_WFMO_test2 - ChildThreadWaitTime; - if( (ThreadWaitDelta_WFMO_test2 < ChildThreadWaitTime) && ( delta > TOLERANCE) ) - { - Fail("Expected thread to wait for %d ms (and not get interrupted).\n" - "Interrupt Time: %d ms, ThreadWaitDelta %u\n", - ChildThreadWaitTime, InterruptTime, ThreadWaitDelta_WFMO_test2); - } - - - PAL_Terminate(); - return PASS; -} - -void RunTest_WFMO_test2(BOOL AlertThread) -{ - HANDLE hThread = 0; - DWORD dwThreadId = 0; - int ret; - - s_preWaitTimestampRecorded = false; - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)WaiterProc_WFMO_test2, - (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_WFMO_test2, 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()); - } -} - -/* Function doesn't do anything, just needed to interrupt the wait*/ -VOID PALAPI APCFunc_WFMO_test2(ULONG_PTR dwParam) -{ -} - -/* Entry Point for child thread. */ -DWORD PALAPI WaiterProc_WFMO_test2(LPVOID lpParameter) -{ - HANDLE Semaphore; - int64_t OldTimeStamp; - int64_t NewTimeStamp; - BOOL Alertable; - DWORD ret; - - /* Create a semaphore that is not in the signalled state */ - Semaphore = CreateSemaphoreExW(NULL, 0, 1, NULL, 0, 0); - - if (Semaphore == NULL) - { - Fail("Failed to create semaphore! GetLastError returned %d.\n", - GetLastError()); - } - - Alertable = (BOOL)(SIZE_T) lpParameter; - - OldTimeStamp = minipal_hires_ticks(); - s_preWaitTimestampRecorded = true; - - ret = WaitForMultipleObjectsEx(1, &Semaphore, FALSE, 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("WaitForMultipleObjectsEx did not timeout.\n" - "Expected return of WAIT_TIMEOUT, got %d.\n", ret); - } - - ThreadWaitDelta_WFMO_test2 = (NewTimeStamp - OldTimeStamp) / (minipal_hires_tick_frequency() / 1000); - - ret = CloseHandle(Semaphore); - if (!ret) - { - Fail("Unable to close handle to semaphore!\n" - "GetLastError returned %d\n", GetLastError()); - } - - return 0; -} - - 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/WFSOExSemaphoreTest/WFSOExSemaphoreTest.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.cpp deleted file mode 100644 index b67776597fd963..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.cpp +++ /dev/null @@ -1,187 +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: WFSOExSemaphore.c -** -** Purpose: Tests a child thread in the middle of a -** WaitForSingleObjectEx call will be interrupted by QueueUserAPC -** if the alert flag was set. -** -** -**===================================================================*/ - -#include - -/*Based on SleepEx/test2 */ - -const int ChildThreadWaitTime = 4000; -const int InterruptTime = 2000; -const DWORD AcceptableDelta = 300; - -void RunTest_WFSOExSemaphoreTest(BOOL AlertThread); -VOID PALAPI APCFunc_WFSOExSemaphoreTest(ULONG_PTR dwParam); -DWORD PALAPI WaiterProc_WFSOExSemaphoreTest(LPVOID lpParameter); - -DWORD ThreadWaitDelta_WFSOExSemaphoreTest; -static volatile bool s_preWaitTimestampRecorded = false; - -PALTEST(threading_WaitForSingleObject_WFSOExSemaphoreTest_paltest_waitforsingleobject_wfsoexsemaphoretest, "threading/WaitForSingleObject/WFSOExSemaphoreTest/paltest_waitforsingleobject_wfsoexsemaphoretest") -{ - 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); - - /* - * Check that Queueing an APC in the middle of a wait does interrupt - * it, if it's in an alertable state. - */ - - RunTest_WFSOExSemaphoreTest(TRUE); - if ((ThreadWaitDelta_WFSOExSemaphoreTest - 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_WFSOExSemaphoreTest, 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_WFSOExSemaphoreTest(FALSE); - if ((ThreadWaitDelta_WFSOExSemaphoreTest - 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_WFSOExSemaphoreTest, AcceptableDelta); - } - - - PAL_Terminate(); - return PASS; -} - -void RunTest_WFSOExSemaphoreTest(BOOL AlertThread) -{ - HANDLE hThread = 0; - DWORD dwThreadId = 0; - int ret; - - s_preWaitTimestampRecorded = false; - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)WaiterProc_WFSOExSemaphoreTest, - (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_WFSOExSemaphoreTest, 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_WFSOExSemaphoreTest(ULONG_PTR dwParam) -{ -} - -/* Entry Point for child thread. */ -DWORD PALAPI WaiterProc_WFSOExSemaphoreTest(LPVOID lpParameter) -{ - HANDLE hSemaphore; - int64_t OldTimeStamp; - int64_t NewTimeStamp; - BOOL Alertable; - DWORD ret; - - /* Create a semaphore that is not in the signalled state */ - hSemaphore = CreateSemaphoreExW(NULL, 0, 1, NULL, 0, 0); - - if (hSemaphore == NULL) - { - Fail("Failed to create semaphore! GetLastError returned %d.\n", - GetLastError()); - } - - Alertable = (BOOL)(SIZE_T) lpParameter; - - OldTimeStamp = minipal_hires_ticks(); - s_preWaitTimestampRecorded = true; - - ret = WaitForSingleObjectEx( hSemaphore, - 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_WFSOExSemaphoreTest = (NewTimeStamp - OldTimeStamp) / (minipal_hires_tick_frequency() / 1000); - - ret = CloseHandle(hSemaphore); - if (!ret) - { - Fail("Unable to close handle to semaphore!\n" - "GetLastError returned %d\n", GetLastError()); - } - - return 0; -} - - - diff --git a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.cpp b/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.cpp deleted file mode 100644 index eaf0433cc5c6c3..00000000000000 --- a/src/coreclr/pal/tests/palsuite/threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.cpp +++ /dev/null @@ -1,207 +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: WFSOExThreadTest.c -** -** Purpose: Tests a child thread in the middle of a -** WaitForSingleObjectEx call will be interrupted by QueueUserAPC -** if the alert flag was set. -** -** -**===================================================================*/ - -#include - -/*Based on SleepEx/test2 */ - -const int ChildThreadWaitTime = 4000; -const int InterruptTime = 2000; -const DWORD AcceptableDelta = 300; - -void RunTest_WFSOExThreadTest(BOOL AlertThread); -VOID PALAPI APCFunc_WFSOExThreadTest(ULONG_PTR dwParam); -DWORD PALAPI WaiterProc_WFSOExThreadTest(LPVOID lpParameter); -void WorkerThread_WFSOExThreadTest(void); - -int ThreadWaitDelta_WFSOExThreadTest; -static volatile bool s_preWaitTimestampRecorded = false; - -PALTEST(threading_WaitForSingleObject_WFSOExThreadTest_paltest_waitforsingleobject_wfsoexthreadtest, "threading/WaitForSingleObject/WFSOExThreadTest/paltest_waitforsingleobject_wfsoexthreadtest") -{ - 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); - - /* - * Check that Queueing an APC in the middle of a wait does interrupt - * it, if it's in an alertable state. - */ - - RunTest_WFSOExThreadTest(TRUE); - if (abs(ThreadWaitDelta_WFSOExThreadTest - 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_WFSOExThreadTest, 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_WFSOExThreadTest(FALSE); - if (abs(ThreadWaitDelta_WFSOExThreadTest - 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_WFSOExThreadTest, AcceptableDelta); - } - - - PAL_Terminate(); - return PASS; -} - -void RunTest_WFSOExThreadTest(BOOL AlertThread) -{ - HANDLE hThread = 0; - DWORD dwThreadId = 0; - int ret; - - //Create thread - s_preWaitTimestampRecorded = false; - hThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)WaiterProc_WFSOExThreadTest, - (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_WFSOExThreadTest, 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_WFSOExThreadTest(ULONG_PTR dwParam) -{ -} - -/* Entry Point for child thread. */ -DWORD PALAPI WaiterProc_WFSOExThreadTest(LPVOID lpParameter) -{ - HANDLE hWaitThread; - int64_t OldTimeStamp; - int64_t NewTimeStamp; - BOOL Alertable; - DWORD ret; - DWORD dwThreadId = 0; - -/* -When a thread terminates, the thread object attains a signaled state, -satisfying any threads that were waiting on the object. -*/ - -/* Create a thread that does not return immediately to maintain a non signaled test*/ - hWaitThread = CreateThread( NULL, - 0, - (LPTHREAD_START_ROUTINE)WorkerThread_WFSOExThreadTest, - NULL, - 0, - &dwThreadId); - - if (hWaitThread == NULL) - { - Fail("ERROR: Was not able to create worker thread to wait on!\n" - "GetLastError returned %d\n", GetLastError()); - } - - Alertable = (BOOL)(SIZE_T) lpParameter; - - OldTimeStamp = minipal_hires_ticks(); - s_preWaitTimestampRecorded = true; - - ret = WaitForSingleObjectEx( hWaitThread, - 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_WFSOExThreadTest = (NewTimeStamp - OldTimeStamp) / (minipal_hires_tick_frequency() / 1000); - - ret = CloseHandle(hWaitThread); - if (!ret) - { - Fail("Unable to close handle to Thread!\n" - "GetLastError returned %d\n", GetLastError()); - } - - return 0; -} - - -void WorkerThread_WFSOExThreadTest(void) -{ - - //Make the worker thread sleep to test WFSOEx Functionality - - Sleep(2*ChildThreadWaitTime); -} - 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 @@ -411,7 +410,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/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 8dfbdb1af00bf0..ee94a3222a4cea 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -936,6 +936,10 @@ void EEStartupHelper() FinalizerThread::FinalizerThreadCreate(); #endif // TARGET_WINDOWS + // Start the thread that will clean up managed resources for any non-.NET created threads + // that exit. + ThreadCleanupThread::EnsureCleanupThreadExists(); + #ifdef FEATURE_PERFTRACING // Finish setting up rest of EventPipe - specifically enable SampleProfiler if it was requested at startup. // SampleProfiler needs to cooperate with the GC which hasn't fully finished setting up in the first part of the diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 64be7831043167..1b28b878a0f1a5 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -239,7 +239,6 @@ extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int thr pNewThread->SetThreadPriority(NTPriority); pNewThread->ChooseThreadCPUGroupAffinity(); - pNewThread->SetThreadState(Thread::TS_LegalToJoin); if (isThreadPool) pNewThread->SetIsThreadPoolThread(); @@ -418,6 +417,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 @@ -496,105 +521,33 @@ extern "C" INT32 QCALLTYPE ThreadNative_SetApartmentState(QCall::ObjectHandleOnS } #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT -void ReleaseThreadExternalCount(Thread * pThread) -{ - WRAPPER_NO_CONTRACT; - pThread->DecExternalCount(FALSE); -} - -typedef Holder ThreadExternalCountHolder; - -// Wait for the thread to die -static BOOL DoJoin(THREADBASEREF dyingThread, INT32 timeout) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(dyingThread != NULL); - PRECONDITION((timeout >= 0) || (timeout == INFINITE_TIMEOUT)); - } - CONTRACTL_END; - - Thread* DyingInternal = dyingThread->GetInternal(); - - // Validate the handle. It's valid to Join a thread that's not running -- so - // long as it was once started. - if (DyingInternal == NULL || - !(DyingInternal->m_State & Thread::TS_LegalToJoin)) - { - COMPlusThrow(kThreadStateException, W("ThreadState_NotStarted")); - } - - // Don't grab the handle until we know it has started, to eliminate the race - // condition. - if (ThreadIsDead(DyingInternal) || !DyingInternal->HasValidThreadHandle()) - return TRUE; - - // There is a race here. The Thread is going to close its thread handle. - // If we grab the handle and then the Thread closes it, we will wait forever - // in DoAppropriateWait. - int RefCount = DyingInternal->IncExternalCount(); - if (RefCount == 1) - { - // !!! We resurrect the Thread Object. - // !!! We will keep the Thread ref count to be 1 so that we will not try - // !!! to destroy the Thread Object again. - // !!! Do not call DecExternalCount here! - _ASSERTE (!DyingInternal->HasValidThreadHandle()); - return TRUE; - } - - ThreadExternalCountHolder dyingInternalHolder(DyingInternal); - - if (!DyingInternal->HasValidThreadHandle()) - { - return TRUE; - } - - GCX_PREEMP(); - DWORD dwTimeOut32 = (timeout == INFINITE_TIMEOUT - ? INFINITE - : (DWORD) timeout); - - DWORD rv = DyingInternal->JoinEx(dwTimeOut32, WaitMode_Alertable); - switch(rv) - { - case WAIT_OBJECT_0: - return TRUE; - - case WAIT_TIMEOUT: - break; - - case WAIT_FAILED: - if(!DyingInternal->HasValidThreadHandle()) - return TRUE; - break; - - default: - _ASSERTE(!"This return code is not understood \n"); - break; - } - - return FALSE; -} - -extern "C" BOOL QCALLTYPE ThreadNative_Join(QCall::ObjectHandleOnStack thread, INT32 Timeout) +#if TARGET_WINDOWS +extern "C" HANDLE QCALLTYPE ThreadNative_GetOSHandle(QCall::ThreadHandle t) { QCALL_CONTRACT; - BOOL retVal = FALSE; + HANDLE retVal = INVALID_HANDLE_VALUE; BEGIN_QCALL; - GCX_COOP(); - retVal = DoJoin((THREADBASEREF)thread.Get(), Timeout); + HANDLE currentHandle = t->GetThreadHandle(); + if (currentHandle != INVALID_HANDLE_VALUE) + { + DuplicateHandle( + GetCurrentProcess(), + currentHandle, + GetCurrentProcess(), + &retVal, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + } END_QCALL; return retVal; } +#endif // If the exposed object is created after-the-fact, for an existing thread, we call // InitExisting on it. This is the other "construction", as opposed to SetDelegate. @@ -770,6 +723,7 @@ extern "C" void QCALLTYPE ThreadNative_SpinWait(INT32 iterations) YieldProcessorNormalized(iterations); } +#ifdef TARGET_WINDOWS // This service can be called on unstarted and dead threads. For unstarted ones, the // next wait will be interrupted. For dead ones, this service quietly does nothing. extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread) @@ -788,16 +742,17 @@ extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread) END_QCALL; } -extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime) +extern "C" void QCALLTYPE ThreadNative_CheckForPendingInterrupt(QCall::ThreadHandle thread) { QCALL_CONTRACT; BEGIN_QCALL; - GetThread()->UserSleep(iTime); + thread->HandleThreadInterrupt(); END_QCALL; } +#endif // TARGET_WINDOWS #ifdef FEATURE_COMINTEROP extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::ThreadHandle thread) @@ -909,3 +864,20 @@ FCIMPL1(ObjHeader::HeaderLockResult, ObjHeader_ReleaseThinLock, Object* obj) return obj->GetHeader()->ReleaseHeaderThinLock(GetThread()->GetThreadId()); } 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 = (INT32)pThread->DoReentrantWaitAny(count, handles, timeout, mode); + + END_QCALL; + + return retVal; +} diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index ca6f3fb5be1619..16b1fe6d898b7a 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -56,18 +56,26 @@ 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); +#ifdef TARGET_WINDOWS +extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread); +extern "C" void QCALLTYPE ThreadNative_CheckForPendingInterrupt(QCall::ThreadHandle thread); +#endif // TARGET_WINDOWS #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT extern "C" INT32 QCALLTYPE ThreadNative_GetApartmentState(QCall::ObjectHandleOnStack t); extern "C" INT32 QCALLTYPE ThreadNative_SetApartmentState(QCall::ObjectHandleOnStack t, INT32 iState); #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT -extern "C" BOOL QCALLTYPE ThreadNative_Join(QCall::ObjectHandleOnStack thread, INT32 Timeout); +#ifdef TARGET_WINDOWS +extern "C" HANDLE QCALLTYPE ThreadNative_GetOSHandle(QCall::ThreadHandle t); +#endif // TARGET_WINDOWS + 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 c1d21bf7112e61..00000000000000 --- a/src/coreclr/vm/comwaithandle.cpp +++ /dev/null @@ -1,107 +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, BOOL doNotSendWaitEvents) -{ - 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); - if (doNotSendWaitEvents) - { - waitMode = (WaitMode)(waitMode | WaitMode_DoNotSendWaitEvents); - } - 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 34583783fcb661..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, BOOL doNotSendWaitEvents); -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 9e89019b200780..596f8cd816f99a 100644 --- a/src/coreclr/vm/corelib.cpp +++ b/src/coreclr/vm/corelib.cpp @@ -33,7 +33,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 193358e11af195..e02bb7638660ed 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -963,11 +963,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 @@ -985,6 +980,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 fe468252c1cfbe..8f704f2b9dc98a 100644 --- a/src/coreclr/vm/finalizerthread.cpp +++ b/src/coreclr/vm/finalizerthread.cpp @@ -146,12 +146,13 @@ static void DoExtraWorkForFinalizer(Thread* finalizerThread) SystemDomain::System()->ProcessDelayedUnloadLoaderAllocators(); } - if (Thread::m_DetachCount > 0 - || Thread::CleanupNeededForFinalizedThread()) + if (Thread::CleanupNeededForFinalizedThread()) { - Thread::CleanupDetachedThreads(); + Thread::CleanupFinalizedThreads(); } + ThreadStore::s_pThreadStore->TriggerGCForDeadThreadsIfNecessary(); + if (YieldProcessorNormalization::IsMeasurementScheduled()) { GCX_PREEMP(); @@ -163,8 +164,6 @@ static void DoExtraWorkForFinalizer(Thread* finalizerThread) GCX_PREEMP(); CleanupDelayedDynamicMethods(); } - - ThreadStore::s_pThreadStore->TriggerGCForDeadThreadsIfNecessary(); } OBJECTREF FinalizerThread::GetNextFinalizableObject() @@ -287,8 +286,6 @@ void FinalizerThread::WaitForFinalizerEvent (CLREvent *event) { case (WAIT_OBJECT_0): return; - case (WAIT_ABANDONED): - return; case (WAIT_TIMEOUT): break; } @@ -350,8 +347,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 44e9be70a90a17..3ed6d188062213 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -525,9 +525,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 08bbcd920f782c..98e6fa86a8e414 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1143,37 +1143,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 @@ -1233,6 +1211,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. @@ -1279,12 +1260,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 102932dba0162b..62f41366d104a3 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -27,7 +27,6 @@ #include "comdatetime.h" #include "debugdebugger.h" #include "assemblynative.hpp" -#include "comwaithandle.h" #include "proftoeeinterfaceimpl.h" @@ -287,27 +286,25 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_GetCurrentOSThreadId) DllImportEntry(ThreadNative_Initialize) DllImportEntry(ThreadNative_GetThreadState) + DllImportEntry(ThreadNative_SetWaitSleepJoinState) + DllImportEntry(ThreadNative_ClearWaitSleepJoinState) + DllImportEntry(ThreadNative_ReentrantWaitAny) #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT DllImportEntry(ThreadNative_GetApartmentState) DllImportEntry(ThreadNative_SetApartmentState) #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - DllImportEntry(ThreadNative_Join) DllImportEntry(ThreadNative_Abort) DllImportEntry(ThreadNative_ResetAbort) DllImportEntry(ThreadNative_SpinWait) +#ifdef TARGET_WINDOWS + DllImportEntry(ThreadNative_CheckForPendingInterrupt) DllImportEntry(ThreadNative_Interrupt) - DllImportEntry(ThreadNative_Sleep) +#endif // TARGET_WINDOWS DllImportEntry(ThreadNative_PollGC) #ifdef FEATURE_COMINTEROP DllImportEntry(ThreadNative_DisableComObjectEagerCleanup) #endif // FEATURE_COMINTEROP DllImportEntry(Monitor_GetOrCreateLockObject) - 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) @@ -478,24 +475,12 @@ static const Entry s_QCall[] = #endif // FEATURE_PERFTRACING #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/runtimecallablewrapper.cpp b/src/coreclr/vm/runtimecallablewrapper.cpp index a87c2305e1beb9..693fcf5f256899 100644 --- a/src/coreclr/vm/runtimecallablewrapper.cpp +++ b/src/coreclr/vm/runtimecallablewrapper.cpp @@ -1195,7 +1195,7 @@ VOID RCWCleanupList::CleanupWrappersInCurrentCtxThread(BOOL fWait, BOOL fManualC // Do a noop wait just to make sure we are cooperating // with the finalizer thread - pThread->Join(1, TRUE); + pThread->DoReentrantWaitWithRetry(pThread->GetThreadHandle(), 1, WaitMode_Alertable); } } } diff --git a/src/coreclr/vm/synch.cpp b/src/coreclr/vm/synch.cpp index 7acc4254efe396..e66f44b91ce3f6 100644 --- a/src/coreclr/vm/synch.cpp +++ b/src/coreclr/vm/synch.cpp @@ -19,7 +19,6 @@ void CLREventBase::CreateAutoEvent (BOOL bInitialState // If TRUE, initial stat // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -45,7 +44,6 @@ BOOL CLREventBase::CreateAutoEventNoThrow (BOOL bInitialState // If TRUE, initi // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -71,7 +69,6 @@ void CLREventBase::CreateManualEvent (BOOL bInitialState // If TRUE, initial st // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -94,7 +91,6 @@ BOOL CLREventBase::CreateManualEventNoThrow (BOOL bInitialState // If TRUE, ini // disallow creation of Crst before EE starts // Can not assert here. ASP.NET uses our Threadpool before EE is started. PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -110,103 +106,6 @@ BOOL CLREventBase::CreateManualEventNoThrow (BOOL bInitialState // If TRUE, ini return IsValid(); } -void CLREventBase::CreateOSAutoEvent (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - // Can not assert here. ASP.NET uses our Threadpool before EE is started. - //_ASSERTE (g_fEEStarted); - - SetOSEvent(); - SetAutoEvent(); - - HANDLE h = CreateEvent(NULL,FALSE,bInitialState,NULL); - if (h == NULL) { - ThrowOutOfMemory(); - } - m_handle = h; -} - -BOOL CLREventBase::CreateOSAutoEventNoThrow (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - EX_TRY - { - CreateOSAutoEvent(bInitialState); - } - EX_CATCH - { - } - EX_END_CATCH - - return IsValid(); -} - -void CLREventBase::CreateOSManualEvent (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - // Can not assert here. ASP.NET uses our Threadpool before EE is started. - //_ASSERTE (g_fEEStarted); - - SetOSEvent(); - - HANDLE h = CreateEvent(NULL,TRUE,bInitialState,NULL); - if (h == NULL) { - ThrowOutOfMemory(); - } - m_handle = h; -} - -BOOL CLREventBase::CreateOSManualEventNoThrow (BOOL bInitialState // If TRUE, initial state is signalled - ) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - // disallow creation of Crst before EE starts - PRECONDITION((m_handle == INVALID_HANDLE_VALUE)); - } - CONTRACTL_END; - - EX_TRY - { - CreateOSManualEvent(bInitialState); - } - EX_CATCH - { - } - EX_END_CATCH - - return IsValid(); -} - void CLREventBase::CloseEvent() { CONTRACTL @@ -319,20 +218,10 @@ DWORD CLREventBase::WaitEx(DWORD dwMilliseconds, WaitMode mode) BOOL alertable = (mode & WaitMode_Alertable)!=0; CONTRACTL { - if (alertable) - { - THROWS; // Thread::DoAppropriateWait can throw - } - else - { - NOTHROW; - } + NOTHROW; if (GetThreadNULLOk()) { - if (alertable) - GC_TRIGGERS; - else - GC_NOTRIGGER; + GC_NOTRIGGER; } else { @@ -347,19 +236,12 @@ DWORD CLREventBase::WaitEx(DWORD dwMilliseconds, WaitMode mode) Thread * pThread = GetThreadNULLOk(); -#ifdef _DEBUG - // If a CLREvent is OS event only, we can not wait for the event on a managed thread - if (IsOSEvent()) - _ASSERTE (pThread == NULL); -#endif _ASSERTE((pThread != NULL) || !g_fEEStarted || dbgOnly_IsSpecialEEThread()); { if (pThread && alertable) { - DWORD dwRet = WAIT_FAILED; - dwRet = pThread->DoAppropriateWait(1, &m_handle, FALSE, dwMilliseconds, - mode); - return dwRet; + GCX_PREEMP(); + return pThread->DoReentrantWaitWithRetry(m_handle, dwMilliseconds, mode); } else { return CLREventWaitHelper(m_handle,dwMilliseconds,alertable); diff --git a/src/coreclr/vm/synch.h b/src/coreclr/vm/synch.h index e57d30b559ed5d..100ad8a583f63a 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_DoNotSendWaitEvents = 0x4, // Has an associated managed object with this wait. }; class CLREventBase @@ -26,7 +24,6 @@ class CLREventBase m_dwFlags = 0; } - // Create an Event that is host aware void CreateAutoEvent(BOOL bInitialState); void CreateManualEvent(BOOL bInitialState); @@ -34,14 +31,6 @@ class CLREventBase BOOL CreateAutoEventNoThrow(BOOL bInitialState); BOOL CreateManualEventNoThrow(BOOL bInitialState); - // Create an Event that is not host aware - void CreateOSAutoEvent (BOOL bInitialState); - void CreateOSManualEvent (BOOL bInitialState); - - // Non-throwing variants of the functions above - BOOL CreateOSAutoEventNoThrow (BOOL bInitialState); - BOOL CreateOSManualEventNoThrow (BOOL bInitialState); - void CloseEvent(); BOOL IsValid() const @@ -69,7 +58,6 @@ class CLREventBase enum { CLREVENT_FLAGS_AUTO_EVENT = 0x0001, - CLREVENT_FLAGS_OS_EVENT = 0x0002, CLREVENT_FLAGS_STATIC = 0x0020, @@ -85,13 +73,6 @@ class CLREventBase // cannot use `|=' operator on `Volatile' m_dwFlags = m_dwFlags | CLREVENT_FLAGS_AUTO_EVENT; } - BOOL IsOSEvent() { LIMITED_METHOD_CONTRACT; return m_dwFlags & CLREVENT_FLAGS_OS_EVENT; } - void SetOSEvent () - { - LIMITED_METHOD_CONTRACT; - // cannot use `|=' operator on `Volatile' - m_dwFlags = m_dwFlags | CLREVENT_FLAGS_OS_EVENT; - } }; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index fa7735fc6ad976..dbbc07f59515dd 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -373,59 +373,6 @@ void SetThread(Thread* t) t_CurrentThreadInfo.m_pAppDomain = t == NULL ? NULL : AppDomain::GetCurrentDomain(); } -BOOL Thread::Alert () -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - BOOL fRetVal = FALSE; - { - HANDLE handle = GetThreadHandle(); - if (handle != INVALID_HANDLE_VALUE) - { - fRetVal = ::QueueUserAPC(UserInterruptAPC, handle, APC_Code); - } - } - - return fRetVal; -} - - -DWORD Thread::Join(DWORD timeout, BOOL alertable) -{ - WRAPPER_NO_CONTRACT; - return JoinEx(timeout,alertable?WaitMode_Alertable:WaitMode_None); -} -DWORD Thread::JoinEx(DWORD timeout, WaitMode mode) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; // For DoAppropriateWait - } - CONTRACTL_END; - - BOOL alertable = (mode & WaitMode_Alertable)?TRUE:FALSE; - - Thread *pCurThread = GetThreadNULLOk(); - _ASSERTE(pCurThread || dbgOnly_IsSpecialEEThread()); - - { - HANDLE handle = GetThreadHandle(); - if (handle == INVALID_HANDLE_VALUE) { - return WAIT_FAILED; - } - if (pCurThread) { - return pCurThread->DoAppropriateWait(1, &handle, FALSE, timeout, mode); - } - else { - return WaitForSingleObjectEx(handle,timeout,alertable); - } - } -} - extern INT32 MapFromNTPriority(INT32 NTPriority); BOOL Thread::SetThreadPriority( @@ -694,7 +641,6 @@ Thread* SetupThread() // reset any unstarted bits on the thread object pThread->ResetThreadState(Thread::TS_Unstarted); - pThread->SetThreadState(Thread::TS_LegalToJoin); ThreadStore::AddThread(pThread); @@ -939,14 +885,9 @@ HRESULT Thread::DetachThread(BOOL inTerminationCallback) SetThreadState((Thread::ThreadState)(Thread::TS_Detached | Thread::TS_ReportDead)); // Do not touch Thread object any more. It may be destroyed. - // These detached threads will be cleaned up by finalizer thread. But if the process uses - // little managed heap, it will be a while before GC happens, and finalizer thread starts - // working on detached thread. So we wake up finalizer thread to clean up resources. - // - // (It's possible that this is the startup thread, and startup failed, and so the finalization - // machinery isn't fully initialized. Hence this check.) + // These detached threads will be cleaned up by external thread cleanup thread. if (g_fEEStarted) - FinalizerThread::EnableFinalization(); + ThreadCleanupThread::SetHasThreadsToCleanUp(); return S_OK; } @@ -2437,78 +2378,149 @@ 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); - // 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->IsDetached()) + { + STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - detaching thread 0x%p\n", 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 = + 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); + } + + // 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)) + + 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); + } +} + +void Thread::CleanupFinalizedThreads() +{ + CONTRACTL { + THROWS; + MODE_COOPERATIVE; + GC_TRIGGERS; + } + CONTRACTL_END; + + _ASSERTE(!ThreadStore::HoldingThreadStore()); + + ThreadStoreLockHolder threadStoreLockHolder; + + Thread *thread = ThreadStore::GetAllThreadList(NULL, 0, 0); + + STRESS_LOG0(LF_SYNC, LL_INFO1000, "T::CDT called\n"); + + while (thread != NULL) + { + Thread *next = ThreadStore::GetAllThreadList(thread, 0, 0); + + if (thread->HasThreadState(TS_Finalized)) { STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - finalized thread 0x%p\n", thread); @@ -2931,528 +2943,87 @@ DWORD MsgWaitHelper(int numWaiters, HANDLE* phEvent, BOOL bWaitAll, DWORD millis #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT -//-------------------------------------------------------------------- -// Do appropriate wait based on apartment state (STA or MTA) -DWORD Thread::DoAppropriateAptStateWait(int numWaiters, HANDLE* pHandles, BOOL bWaitAll, - DWORD timeout, WaitMode mode) -{ - STANDARD_VM_CONTRACT; - - BOOL alertable = (mode & WaitMode_Alertable) != 0; - -#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT - if (alertable && !AppDomain::GetCurrentDomain()->MustForceTrivialWaitOperations()) - { - ApartmentState as = GetFinalApartment(); - if (AS_InMTA != as) - { - return MsgWaitHelper(numWaiters, pHandles, bWaitAll, timeout, alertable); - } - } -#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - - return WaitForMultipleObjectsEx(numWaiters, pHandles, bWaitAll, timeout, alertable); -} - -// A helper called by our two flavors of DoAppropriateWaitWorker -void Thread::DoAppropriateWaitAlertableHelper(WaitMode mode) +DWORD Thread::DoReentrantWaitAny(int numWaiters, HANDLE* pHandles, DWORD timeout, WaitMode mode) { CONTRACTL { THROWS; GC_TRIGGERS; + MODE_PREEMPTIVE; } CONTRACTL_END; - // 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 (HasThreadStateNC(TSNC_InRestoringSyncBlock)) - { - // The thread is restoring SyncBlock for Object.Wait. - ResetThreadStateNC(TSNC_InRestoringSyncBlock); - } - else - { - HandleThreadInterrupt(); - - // Safe to clear the interrupted state, no APC could have fired since we - // reset m_UserInterrupt (which inhibits our APC callback from doing - // anything). - ResetThreadState(TS_Interrupted); - } -} - -void MarkOSAlertableWait() -{ - LIMITED_METHOD_CONTRACT; - GetThread()->SetThreadStateNC (Thread::TSNC_OSAlertableWait); + return DoAppropriateAptStateWait(numWaiters, pHandles, FALSE, timeout, mode); } -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. -//-------------------------------------------------------------------- -DWORD Thread::DoAppropriateWait(int countHandles, HANDLE *handles, BOOL waitAll, - DWORD millis, WaitMode mode) +DWORD Thread::DoReentrantWaitWithRetry(HANDLE handle, DWORD timeout, WaitMode mode) { CONTRACTL { THROWS; GC_TRIGGERS; + MODE_PREEMPTIVE; } CONTRACTL_END; - 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 - // doesn't work reliably, see DevDiv Bugs 699245. Some methods call in here already in - // COOP mode so we set the bit before the transition. For the calls that are already - // in pre-emptive mode those are still buggy. This is only a partial fix. - BOOL isCoop = PreemptiveGCDisabled(); - ThreadStateNCStackHolder tsNC(isCoop && alertable, TSNC_DebuggerSleepWaitJoin); - - GCX_PREEMP(); - - if (alertable) - { - DoAppropriateWaitAlertableHelper(mode); - } - - StateHolder OSAlertableWait(alertable); - - ThreadStateHolder tsh(alertable, TS_Interruptible | TS_Interrupted); - - bool sendWaitEvents = - millis != 0 && - (mode & WaitMode_Alertable) != 0 && - (mode & WaitMode_DoNotSendWaitEvents) == 0 && // wait events for waits with associated objects are sent from managed code. - ETW_TRACING_CATEGORY_ENABLED( - MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, - TRACE_LEVEL_VERBOSE, - CLR_WAITHANDLE_KEYWORD); - - // When sending the wait events try a nonblocking wait first - // such that the events sent are more likely to represent blocking waits. - bool tryNonblockingWaitFirst = sendWaitEvents; - if (tryNonblockingWaitFirst) - { - ret = DoAppropriateAptStateWait(countHandles, handles, waitAll, 0 /* timeout */, mode); - if (ret == WAIT_TIMEOUT) - { - // Do a full wait and send the wait events - tryNonblockingWaitFirst = false; - } - else if (ret != WAIT_IO_COMPLETION && ret != WAIT_FAILED) - { - // The nonblocking wait was successful, don't send the wait events - sendWaitEvents = false; - } - } - - if (sendWaitEvents) - { - FireEtwWaitHandleWaitStart(ETW::WaitHandleLog::WaitHandleStructs::Unknown, NULL, GetClrInstanceId()); - } - +#ifdef TARGET_UNIX + return WaitForSingleObjectEx(handle, timeout, mode == WaitMode_Alertable); +#else ULONGLONG dwStart = 0, dwEnd; - if (millis != INFINITE) + if (timeout != INFINITE) { dwStart = minipal_lowres_ticks(); } -retry: - if (tryNonblockingWaitFirst) - { - // We have a final wait result from the nonblocking wait above - tryNonblockingWaitFirst = false; - } - else - { - ret = DoAppropriateAptStateWait(countHandles, handles, waitAll, millis, mode); - } - - if (ret == WAIT_IO_COMPLETION) + while (true) { - _ASSERTE (alertable); + HRESULT ret = DoAppropriateAptStateWait(1, &handle, FALSE, timeout, mode); - if (m_State & TS_Interrupted) + if (ret != WAIT_IO_COMPLETION) { - HandleThreadInterrupt(); + return ret; } - // 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 (millis != INFINITE) + + _ASSERTE (alertable); + if (timeout != INFINITE) { dwEnd = minipal_lowres_ticks(); - if (dwEnd - dwStart >= millis) + if (dwEnd - dwStart >= timeout) { ret = WAIT_TIMEOUT; - goto WaitCompleted; + return ret; } - millis -= (DWORD)(dwEnd - dwStart); + timeout -= (DWORD)(dwEnd - dwStart); dwStart = dwEnd; } - 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); - - // We support precisely one WAIT_FAILED case, where we attempt to wait on a - // thread handle and the thread is in the process of dying we might get a - // invalid handle substatus. Turn this into a successful wait. - // There are three cases to consider: - // 1) Only waiting on one handle: return success right away. - // 2) Waiting for all handles to be signalled: retry the wait without the - // affected handle. - // 3) Waiting for one of multiple handles to be signalled: return with the - // first handle that is either signalled or has become invalid. - if (ret == WAIT_FAILED) - { - DWORD errorCode = ::GetLastError(); - if (errorCode == ERROR_INVALID_PARAMETER) - { - if (CheckForDuplicateHandles(countHandles, handles)) - COMPlusThrow(kDuplicateWaitObjectException); - else - COMPlusThrowHR(HRESULT_FROM_WIN32(errorCode)); - } - else if (errorCode == ERROR_ACCESS_DENIED) - { - // A Win32 ACL could prevent us from waiting on the handle. - COMPlusThrow(kUnauthorizedAccessException); - } - else if (errorCode == ERROR_NOT_ENOUGH_MEMORY) - { - 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); - } - - if (countHandles == 1) - ret = WAIT_OBJECT_0; - else if (waitAll) - { - // Probe all handles with a timeout of zero. When we find one that's - // invalid, move it out of the list and retry the wait. - for (int i = 0; i < countHandles; i++) - { - // WaitForSingleObject won't pump memssage; we already probe enough space - // before calling this function and we don't want to fail here, so we don't - // do a transition to tolerant code here - DWORD subRet = WaitForSingleObject (handles[i], 0); - if (subRet != WAIT_FAILED) - continue; - _ASSERTE(::GetLastError() == ERROR_INVALID_HANDLE); - if ((countHandles - i - 1) > 0) - memmove(&handles[i], &handles[i+1], (countHandles - i - 1) * sizeof(HANDLE)); - countHandles--; - break; - } - - // Compute the new timeout value by assume that the timeout - // is not large enough for more than one wrap - if (millis != INFINITE) - { - dwEnd = minipal_lowres_ticks(); - if (dwEnd - dwStart >= millis) - { - ret = WAIT_TIMEOUT; - goto WaitCompleted; - } - - millis -= (DWORD)(dwEnd - dwStart); - dwStart = dwEnd; - } - goto retry; - } - else - { - // Probe all handles with a timeout as zero, succeed with the first - // handle that doesn't timeout. - ret = WAIT_OBJECT_0; - int i; - for (i = 0; i < countHandles; i++) - { - TryAgain: - // WaitForSingleObject won't pump memssage; we already probe enough space - // before calling this function and we don't want to fail here, so we don't - // do a transition to tolerant code here - DWORD subRet = WaitForSingleObject (handles[i], 0); - if ((subRet == WAIT_OBJECT_0) || (subRet == WAIT_FAILED)) - break; - if (subRet == WAIT_ABANDONED) - { - ret = (ret - WAIT_OBJECT_0) + WAIT_ABANDONED; - break; - } - // If we get alerted it just masks the real state of the current - // handle, so retry the wait. - if (subRet == WAIT_IO_COMPLETION) - goto TryAgain; - _ASSERTE(subRet == WAIT_TIMEOUT); - ret++; - } - } } - -WaitCompleted: - - _ASSERTE((ret != WAIT_TIMEOUT) || (millis != INFINITE)); - - if (sendWaitEvents) - { - FireEtwWaitHandleWaitStop(GetClrInstanceId()); - } - - return ret; +#endif } -DWORD Thread::DoSignalAndWait(HANDLE* pHandles, DWORD millis,BOOL alertable) -{ - CONTRACTL { - THROWS; - GC_TRIGGERS; - } - CONTRACTL_END; - - DWORD ret = 0; - - GCX_PREEMP(); - - if(alertable) - { - DoAppropriateWaitAlertableHelper(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; - } +//-------------------------------------------------------------------- +// Do appropriate wait based on apartment state (STA or MTA) +DWORD Thread::DoAppropriateAptStateWait(int numWaiters, HANDLE* pHandles, BOOL bWaitAll, + DWORD timeout, WaitMode mode) +{ + STANDARD_VM_CONTRACT; - 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; - } + BOOL alertable = (mode & WaitMode_Alertable) != 0; - if (WAIT_FAILED == ret) +#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT + if (alertable && !AppDomain::GetCurrentDomain()->MustForceTrivialWaitOperations()) { - 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) + ApartmentState as = GetFinalApartment(); + if (AS_InMTA != as) { - 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; + return MsgWaitHelper(numWaiters, pHandles, bWaitAll, timeout, alertable); } } +#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT -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); -} - -// Return whether or not a timeout occurred. TRUE=>we waited successfully -DWORD Thread::Wait(HANDLE *objs, int cntObjs, INT32 timeOut) -{ - WRAPPER_NO_CONTRACT; - - DWORD dwResult; - DWORD dwTimeOut32; - - _ASSERTE(timeOut >= 0 || timeOut == INFINITE_TIMEOUT); - - dwTimeOut32 = (timeOut == INFINITE_TIMEOUT - ? INFINITE - : (DWORD) timeOut); - - dwResult = DoAppropriateWait(cntObjs, objs, FALSE /*=waitAll*/, dwTimeOut32, - WaitMode_Alertable /*alertable*/); - - // Either we succeeded in the wait, or we timed out - _ASSERTE((dwResult >= WAIT_OBJECT_0 && dwResult < (DWORD)(WAIT_OBJECT_0 + cntObjs)) || - (dwResult == WAIT_TIMEOUT)); - - return dwResult; -} - -// Return whether or not a timeout occurred. TRUE=>we waited successfully -DWORD Thread::Wait(CLREvent *pEvent, INT32 timeOut) -{ - WRAPPER_NO_CONTRACT; - - DWORD dwResult; - DWORD dwTimeOut32; - - _ASSERTE(timeOut >= 0 || timeOut == INFINITE_TIMEOUT); - - dwTimeOut32 = (timeOut == INFINITE_TIMEOUT - ? INFINITE - : (DWORD) timeOut); - - dwResult = pEvent->Wait(dwTimeOut32, TRUE /*alertable*/); - - // Either we succeeded in the wait, or we timed out - _ASSERTE((dwResult == WAIT_OBJECT_0) || - (dwResult == WAIT_TIMEOUT)); - - return dwResult; + return WaitForMultipleObjectsEx(numWaiters, pHandles, bWaitAll, timeout, alertable); } -#define WAIT_INTERRUPT_THREADABORT 0x1 -#define WAIT_INTERRUPT_INTERRUPT 0x2 -#define WAIT_INTERRUPT_OTHEREXCEPTION 0x4 - +#ifdef TARGET_WINDOWS // This is the callback from the OS, when we queue an APC to interrupt a waiting thread. // The callback occurs on the thread we wish to interrupt. It is a STATIC method. void WINAPI Thread::UserInterruptAPC(ULONG_PTR data) @@ -3497,85 +3068,14 @@ void Thread::UserInterrupt(ThreadInterruptMode mode) if (HasValidThreadHandle() && HasThreadState (TS_Interruptible)) { - Alert(); - } -} - -// 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 + HANDLE handle = GetThreadHandle(); + if (handle != INVALID_HANDLE_VALUE) { - ULONGLONG actDuration = minipal_lowres_ticks() - start; - - if (dwTime > actDuration) - { - dwTime -= (DWORD)actDuration; - goto retry; - } - else - { - res = WAIT_TIMEOUT; - } + ::QueueUserAPC(UserInterruptAPC, handle, APC_Code); } } - _ASSERTE(res == WAIT_TIMEOUT || res == WAIT_OBJECT_0); } - +#endif // TARGET_WINDOWS // Correspondence between an EE Thread and an exposed System.Thread: OBJECTREF Thread::GetExposedObject() @@ -4674,7 +4174,6 @@ void ThreadStore::TransferStartedThread(Thread *thread) // As soon as we erase this bit, the thread becomes eligible for suspension, // stopping, interruption, etc. thread->ResetThreadState(Thread::TS_Unstarted); - thread->SetThreadState(Thread::TS_LegalToJoin); // One of the components of OtherThreadsComplete() has changed, so check whether // we should now exit the EE. @@ -7475,6 +6974,99 @@ PTR_GCFrame Thread::GetGCFrame() return m_pGCFrame; } +#ifndef DACCESS_COMPILE +namespace +{ + CLREvent* ForeignThreadsToCleanUpEvent = nullptr; + + void ForeignThreadCleanupWorker(LPVOID args) + { + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + { + GCX_PREEMP(); + + ForeignThreadsToCleanUpEvent->Wait(INFINITE, FALSE); + } + + if (Thread::m_DetachCount > 0) + { + Thread::CleanupDetachedThreads(); + } + + ThreadStore::s_pThreadStore->TriggerGCForDeadThreadsIfNecessary(); + } + + DWORD WINAPI ForeignThreadCleanupThreadStart(LPVOID lpParameter) + { + bool cleanupThreadOk = ((Thread*)lpParameter)->HasStarted(); + + _ASSERTE(cleanupThreadOk); + + // finalizer should always park in default domain + + if (cleanupThreadOk) + { + INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; + { + while (true) + { + ManagedThreadBase::KickOff(ForeignThreadCleanupWorker, NULL); + } + } + UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; + } + return 0; + } +} + +void +ThreadCleanupThread::EnsureCleanupThreadExists() +{ + CONTRACTL{ + THROWS; + GC_TRIGGERS; + MODE_ANY; + } CONTRACTL_END; + + ForeignThreadsToCleanUpEvent = new CLREvent(); + ForeignThreadsToCleanUpEvent->CreateAutoEvent(FALSE); + + Thread* pCleanupThread = SetupUnstartedThread(); + + if (pCleanupThread->CreateNewThread(0, &ForeignThreadCleanupThreadStart, pCleanupThread, W(".NET External Thread Cleanup Thread"))) + { + DWORD dwRet = pCleanupThread->StartThread(); + + // When running under a user mode native debugger there is a race + // between the moment we've created the thread (in CreateNewThread) and + // the moment we resume it (in StartThread); the debugger may receive + // the "ct" (create thread) notification, and it will attempt to + // suspend/resume all threads in the process. Now imagine the debugger + // resumes this thread first, and only later does it try to resume the + // newly created thread (the finalizer thread). In these conditions our + // call to ResumeThread may come before the debugger's call to ResumeThread + // actually causing dwRet to equal 2. + // We cannot use IsDebuggerPresent() in the condition below because the + // debugger may have been detached between the time it got the notification + // and the moment we execute the test below. + _ASSERTE(dwRet == 1 || dwRet == 2); + } +} +void +ThreadCleanupThread::SetHasThreadsToCleanUp() +{ + _ASSERT(ForeignThreadsToCleanUpEvent != nullptr); + ForeignThreadsToCleanUpEvent->Set(); +} +#endif // DACCESS_COMPILE + #ifdef DACCESS_COMPILE void diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 572c57fd9a08e3..38f055898f83ad 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -512,7 +512,7 @@ class Thread TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads? TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only) - TS_LegalToJoin = 0x00000020, // Is it now legal to attempt a Join() + // unused = 0x00000020, TS_ExecutingOnAltStack = 0x00000040, // Runtime is executing on an alternate stack located anywhere in the memory @@ -598,7 +598,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, @@ -869,8 +869,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. @@ -1127,6 +1127,7 @@ class Thread void OnThreadTerminate(BOOL holdingLock); static void CleanupDetachedThreads(); + static void CleanupFinalizedThreads(); //-------------------------------------------------------------- // Returns innermost active Frame. //-------------------------------------------------------------- @@ -1590,25 +1591,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(); @@ -1680,9 +1662,6 @@ class Thread BOOL SetThreadPriority( int nPriority // thread priority level ); - BOOL Alert (); - DWORD Join(DWORD timeout, BOOL alertable); - DWORD JoinEx(DWORD timeout, WaitMode mode); BOOL GetThreadContext( LPCONTEXT lpContext // context structure @@ -1769,8 +1748,6 @@ class Thread static bool SysSweepThreadsForDebug(bool forceSync); static void SysResumeFromDebug(AppDomain *pAppDomain); - void UserSleep(INT32 time); - private: // Specifies type of thread abort. @@ -1846,8 +1823,6 @@ class Thread BOOL ReadyForAsyncException(); public: - void UserInterrupt(ThreadInterruptMode mode); - BOOL ReadyForAbort() { return ReadyForAsyncException(); @@ -2109,14 +2084,10 @@ class Thread #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT // Either perform WaitForSingleObject or MsgWaitForSingleObject as appropriate. - DWORD DoAppropriateWait(int countHandles, HANDLE *handles, BOOL waitAll, - DWORD millis, WaitMode mode); - - DWORD DoSignalAndWait(HANDLE *handles, DWORD millis, BOOL alertable); + DWORD DoReentrantWaitAny(int numWaiters, HANDLE* pHandles, DWORD timeout, WaitMode mode); + DWORD DoReentrantWaitWithRetry(HANDLE handle, DWORD timeout, WaitMode mode); private: - void DoAppropriateWaitAlertableHelper(WaitMode mode); 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: //************************************************************************ @@ -2581,10 +2552,6 @@ class Thread #endif // FEATURE_HIJACK - // Support for Wait/Notify - DWORD Wait(HANDLE *objs, int cntObjs, INT32 timeOut); - DWORD Wait(CLREvent* pEvent, INT32 timeOut); - // support for Thread.Interrupt() which breaks out of Waits, Sleeps, Joins LONG m_UserInterrupt; DWORD IsUserInterrupted() @@ -2598,10 +2565,14 @@ class Thread InterlockedExchange(&m_UserInterrupt, 0); } - void HandleThreadInterrupt(); +#ifdef TARGET_WINDOWS + static void WINAPI UserInterruptAPC(ULONG_PTR ignore); +public: + void UserInterrupt(ThreadInterruptMode mode); +#endif // TARGET_WINDOWS public: - static void WINAPI UserInterruptAPC(ULONG_PTR ignore); + void HandleThreadInterrupt(); // Access to thread handle and ThreadId. HANDLE GetThreadHandle() @@ -5512,6 +5483,13 @@ class StackWalkerWalkingThreadHolder Thread* m_PreviousValue; }; +class ThreadCleanupThread +{ + public: + static void EnsureCleanupThreadExists(); + static void SetHasThreadsToCleanUp(); +}; + #ifndef DACCESS_COMPILE #if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) EXTERN_C void STDCALL ClrRestoreNonvolatileContextWorker(PCONTEXT ContextRecord, DWORD64 ssp); diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index e003ae328df79b..eebff31ab55ba1 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -1424,6 +1424,10 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout) else #endif // FEATURE_THREAD_ACTIVATION { +#ifdef TARGET_UNIX + _ASSERTE_MSG(false, "Thread::UserAbort: Activation injection is required on Unix platforms"); + UNREACHABLE(); +#else // TARGET_UNIX BOOL fOutOfRuntime = FALSE; BOOL fNeedStackCrawl = FALSE; @@ -1622,6 +1626,7 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout) LPrepareRetry: checkForAbort.Release(); +#endif // TARGET_UNIX } // Don't do a Sleep. It's possible that the thread we are trying to abort is @@ -1630,7 +1635,7 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout) // will time out, but it will pump if we need it to. if (pCurThread) { - pCurThread->Join(ABORT_POLL_TIMEOUT, TRUE); + pCurThread->DoReentrantWaitWithRetry(pCurThread->GetThreadHandle(), ABORT_POLL_TIMEOUT, WaitMode_Alertable); } else { @@ -1666,7 +1671,7 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout) if (pCurThread) { - pCurThread->Join(100, TRUE); + pCurThread->DoReentrantWaitWithRetry(pCurThread->GetThreadHandle(), 100, WaitMode_Alertable); } else { 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 b19e8388fcb996..fe83530280c983 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 @@ -1783,6 +1783,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 @@ -2244,6 +2253,7 @@ + @@ -2282,15 +2292,18 @@ + + + @@ -2310,16 +2323,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 @@ -2332,12 +2341,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 @@ -2348,8 +2351,6 @@ Common\System\Memory\FixedBufferExtensions.cs - - @@ -2817,6 +2818,7 @@ + @@ -2857,7 +2859,7 @@ - + @@ -2868,16 +2870,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 92% 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..fbeb95a922689b 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Threading { @@ -13,6 +14,7 @@ internal sealed partial class LowLevelLifoSemaphore : IDisposable { 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..6f17d7504637f5 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,8 +38,40 @@ 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); + +#if !MONO + private bool JoinInternal(int millisecondsTimeout) + { + // This method assumes the thread has been started + Debug.Assert((ThreadState & ThreadState.Unstarted) == 0 || (millisecondsTimeout == 0)); + SafeWaitHandle waitHandle = GetJoinHandle(); + + // If an OS thread is terminated and its Thread object is resurrected, waitHandle may be finalized and closed + if (waitHandle.IsClosed) + { + return true; + } + + // Prevent race condition with the finalizer + try + { + waitHandle.DangerousAddRef(); + } + catch (ObjectDisposedException) + { + return true; + } + + try + { + return WaitSubsystem.Wait(waitHandle.DangerousGetHandle(), millisecondsTimeout, interruptible: false) == WaitHandle.WaitSuccess; + } + finally + { + waitHandle.DangerousRelease(); + } + } #endif // sched_getcpu doesn't exist on all platforms. On those it doesn't exist on, the shim returns -1 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..68605788d96e10 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,14 +18,142 @@ 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 +#if !MONO + private bool JoinInternal(int millisecondsTimeout) + { + // This method assumes the thread has been started + Debug.Assert((ThreadState & ThreadState.Unstarted) == 0 || (millisecondsTimeout == 0)); + SafeWaitHandle waitHandle = GetJoinHandle(); + + // If an OS thread is terminated and its Thread object is resurrected, waitHandle may be finalized and closed + if (waitHandle.IsClosed) + { + return true; + } + + // Handle race condition with the finalizer + try + { + waitHandle.DangerousAddRef(); + } + catch (ObjectDisposedException) + { + return true; + } + + try + { + if (millisecondsTimeout == 0) + { + int result = (int)Interop.Kernel32.WaitForSingleObject(waitHandle.DangerousGetHandle(), 0); + return result == (int)Interop.Kernel32.WAIT_OBJECT_0; + } + else + { + Thread currentThread = CurrentThread; + currentThread.SetWaitSleepJoinState(); + uint result; + if (millisecondsTimeout == Timeout.Infinite) + { + // Infinite wait + while (true) + { + result = Interop.Kernel32.WaitForSingleObjectEx(waitHandle.DangerousGetHandle(), Timeout.UnsignedInfinite, Interop.BOOL.TRUE); + if (result != Interop.Kernel32.WAIT_IO_COMPLETION) + { + break; + } + // Check if this was our interrupt APC + CheckForPendingInterrupt(); + } + } + else + { + long startTime = Environment.TickCount64; + while (true) + { + result = Interop.Kernel32.WaitForSingleObjectEx(waitHandle.DangerousGetHandle(), (uint)millisecondsTimeout, Interop.BOOL.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) + { + result = Interop.Kernel32.WAIT_TIMEOUT; + break; + } + millisecondsTimeout -= (int)elapsed; + startTime = currentTime; + } + } + currentThread.ClearWaitSleepJoinState(); + return result == (int)Interop.Kernel32.WAIT_OBJECT_0; + } + } + finally + { + waitHandle.DangerousRelease(); + } + } +#endif internal static int GetCurrentProcessorNumber() { Interop.Kernel32.PROCESSOR_NUMBER procNumber; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index 5626e91d313aaa..b9dd510b80c02a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -272,6 +272,18 @@ private void Start(bool captureContext) StartCore(); } +#if !MONO + public bool Join(int millisecondsTimeout) + { + ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, Timeout.Infinite); + if ((ThreadState & ThreadState.Unstarted) != 0) + { + throw new ThreadStateException(SR.ThreadState_NotStarted); + } + return JoinInternal(millisecondsTimeout); + } +#endif + private void RequireCurrentThread() { if (this != CurrentThread) 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 fb934b1ca07f48..f5c5320e6409ed 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @@ -146,9 +146,6 @@ internal bool WaitOneNoCheck( bool sendWaitEvents = millisecondsTimeout != 0 && !useTrivialWaits && -#if !CORECLR // CoreCLR sends the wait events from the native side when there's no associated object - associatedObject is not null && -#endif NativeRuntimeEventSource.Log.IsEnabled( EventLevel.Verbose, NativeRuntimeEventSource.Keywords.WaitHandleKeyword); @@ -160,19 +157,11 @@ associatedObject is not null && waitSource != NativeRuntimeEventSource.WaitHandleWaitSourceMap.MonitorWait; if (tryNonblockingWaitFirst) { - // Split into separate calls instead of conditionally adding an argument. -#if CORECLR - waitResult = WaitOneCore( - waitHandle.DangerousGetHandle(), - millisecondsTimeout: 0, - useTrivialWaits, - associatedObject is not null); -#else waitResult = WaitOneCore( waitHandle.DangerousGetHandle(), millisecondsTimeout: 0, useTrivialWaits); -#endif + if (waitResult == WaitTimeout) { // Do a full wait and send the wait events @@ -193,19 +182,10 @@ associatedObject is not null && // When tryNonblockingWaitFirst is true, we have a final wait result from the nonblocking wait above if (!tryNonblockingWaitFirst) { - // Split into separate calls instead of conditionally adding an argument. -#if CORECLR - waitResult = WaitOneCore( - waitHandle.DangerousGetHandle(), - millisecondsTimeout, - useTrivialWaits, - associatedObject is not null); -#else waitResult = WaitOneCore( waitHandle.DangerousGetHandle(), millisecondsTimeout, useTrivialWaits); -#endif } if (sendWaitEvents) @@ -423,7 +403,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( @@ -455,17 +434,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.Threading/tests/MutexTests.cs b/src/libraries/System.Threading/tests/MutexTests.cs index 7ce7d878d09005..a87522ec3211bd 100644 --- a/src/libraries/System.Threading/tests/MutexTests.cs +++ b/src/libraries/System.Threading/tests/MutexTests.cs @@ -232,13 +232,6 @@ public void Ctor_InvalidNames_Unix() { Assert.Throws(() => new Mutex("Foo/Bar", options: default)); Assert.Throws(() => new Mutex("Global\\Foo/Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); - if (PlatformDetection.IsCoreCLR) - { - AssertExtensions.Throws("name", null, () => new Mutex(new string('a', 1000), options: default)); - AssertExtensions.Throws("name", null, () => new Mutex("Foo\\Bar", options: default)); - AssertExtensions.Throws("name", null, () => new Mutex("Foo\\Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); - Assert.Throws(() => new Mutex("Global\\Foo\\Bar", options: new NamedWaitHandleOptions { CurrentSessionOnly = false })); - } } [Theory] diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index b3d7e1e4073baa..e379ab5a819f34 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -262,9 +262,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/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__