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__