mirror of https://github.com/dotnet/runtime
Merge 06de3b2ca4
into 02596ba8d9
This commit is contained in:
commit
6e61722bd7
|
@ -238,7 +238,6 @@
|
|||
<Compile Include="$(BclSourcesRoot)\System\Threading\Monitor.CoreCLR.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\SynchronizationContext.CoreCLR.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\Thread.CoreCLR.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\WaitHandle.CoreCLR.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\Type.CoreCLR.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\TypedReference.CoreCLR.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\TypeLoadException.CoreCLR.cs" />
|
||||
|
@ -290,8 +289,6 @@
|
|||
<ItemGroup Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true'" >
|
||||
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\InMemoryAssemblyLoader.PlatformNotSupported.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\Interop\Unix\Interop.Libraries.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\LowLevelLifoSemaphore.Unix.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\Mutex.CoreCLR.Unix.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
|
||||
<Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\InMemoryAssemblyLoader.cs" />
|
||||
|
@ -301,6 +298,7 @@
|
|||
<Compile Include="$(CommonPath)Interop\Windows\OleAut32\Interop.VariantChangeTypeEx.cs">
|
||||
<Link>Common\Interop\Windows\OleAut32\Interop.VariantChangeTypeEx.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\WaitHandle.CoreCLR.Windows.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(FeatureObjCMarshal)' == 'true'">
|
||||
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ObjectiveCMarshal.CoreCLR.cs" />
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// A LIFO semaphore implemented using the PAL's semaphore with uninterruptible waits.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Synchronization primitive that can also be used for interprocess synchronization
|
||||
/// </summary>
|
||||
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<byte>(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);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,10 @@ namespace System.Threading
|
|||
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
|
||||
|
@ -298,6 +302,30 @@ namespace System.Threading
|
|||
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")]
|
||||
private static partial int GetThreadState(ThreadHandle t);
|
||||
|
||||
#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
|
||||
internal void SetWaitSleepJoinState()
|
||||
{
|
||||
// This method is called when the thread is about to enter a wait, sleep, or join state.
|
||||
// It sets the state in the native layer to indicate that the thread is waiting.
|
||||
SetWaitSleepJoinState(GetNativeHandle());
|
||||
GC.KeepAlive(this);
|
||||
}
|
||||
|
||||
internal void ClearWaitSleepJoinState()
|
||||
{
|
||||
// This method is called when the thread is no longer in a wait, sleep, or join state.
|
||||
// It clears the state in the native layer to indicate that the thread is no longer waiting.
|
||||
ClearWaitSleepJoinState(GetNativeHandle());
|
||||
GC.KeepAlive(this);
|
||||
}
|
||||
|
||||
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetWaitSleepJoinState")]
|
||||
private static partial void SetWaitSleepJoinState(ThreadHandle t);
|
||||
|
||||
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ClearWaitSleepJoinState")]
|
||||
private static partial void ClearWaitSleepJoinState(ThreadHandle t);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// An unstarted thread can be marked to indicate that it will host a
|
||||
/// single-threaded or multi-threaded apartment.
|
||||
|
@ -513,6 +541,35 @@ namespace System.Threading
|
|||
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
|
||||
|
||||
private void OnThreadExiting()
|
||||
{
|
||||
#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
|
||||
// Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by
|
||||
// the thread.
|
||||
_waitInfo?.OnThreadExiting();
|
||||
#endif
|
||||
}
|
||||
|
||||
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_CurrentThreadIsFinalizerThread")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool CurrentThreadIsFinalizerThread();
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct NativeThreadClass
|
||||
{
|
||||
|
|
|
@ -64,8 +64,6 @@ nativeStringResourceTable_mscorrc
|
|||
#CreateFileMappingW
|
||||
#CreateFileA
|
||||
#CreateFileW
|
||||
#CreateMutexW
|
||||
#CreateMutexExW
|
||||
#CreateEventW
|
||||
#CreateEventExW
|
||||
#CreateProcessW
|
||||
|
|
|
@ -262,3 +262,9 @@ FCIMPL0(OBJECTREF, RhpGetNextFinalizableObject)
|
|||
}
|
||||
}
|
||||
FCIMPLEND
|
||||
|
||||
FCIMPL0(FC_BOOL_RET, RhpCurrentThreadIsFinalizerThread)
|
||||
{
|
||||
FC_RETURN_BOOL(ThreadStore::GetCurrentThread() == g_pFinalizerThread);
|
||||
}
|
||||
FCIMPLEND
|
||||
|
|
|
@ -294,7 +294,6 @@
|
|||
<Compile Include="System\Environment.NativeAot.Unix.cs" />
|
||||
<Compile Include="System\Runtime\InteropServices\NativeLibrary.NativeAot.Unix.cs" />
|
||||
<Compile Include="System\Runtime\InteropServices\PInvokeMarshal.Unix.cs" />
|
||||
<Compile Include="System\Threading\LowLevelLifoSemaphore.Unix.cs" />
|
||||
<Compile Include="System\Threading\Thread.NativeAot.Unix.cs" />
|
||||
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Abort.cs">
|
||||
<Link>Interop\Unix\System.Native\Interop.Abort.cs</Link>
|
||||
|
@ -302,15 +301,9 @@
|
|||
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Exit.cs">
|
||||
<Link>Interop\Unix\System.Native\Interop.Exit.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MMap.cs">
|
||||
<Link>Interop\Unix\System.Native\Interop.MMap.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MProtect.cs">
|
||||
<Link>Interop\Unix\System.Native\Interop.MProtect.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MUnmap.cs">
|
||||
<Link>Interop\Unix\System.Native\Interop.MUnmap.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(CompilerCommonPath)\System\Collections\Generic\ArrayBuilder.cs">
|
||||
|
|
|
@ -90,8 +90,12 @@ namespace System.Runtime
|
|||
RhWaitForPendingFinalizers(allowReentrantWait ? 1 : 0);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||
[RuntimeImport(RuntimeLibrary, "RhpCurrentThreadIsFinalizerThread")]
|
||||
internal static extern bool RhpCurrentThreadIsFinalizerThread();
|
||||
|
||||
// Get maximum GC generation number.
|
||||
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||||
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
||||
[RuntimeImport(RuntimeLibrary, "RhGetMaxGcGeneration")]
|
||||
internal static extern int RhGetMaxGcGeneration();
|
||||
|
||||
|
|
|
@ -517,5 +517,10 @@ namespace System.Threading
|
|||
}
|
||||
s_allDone.WaitOne();
|
||||
}
|
||||
|
||||
internal static bool CurrentThreadIsFinalizerThread()
|
||||
{
|
||||
return RuntimeImports.RhpCurrentThreadIsFinalizerThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -742,65 +742,6 @@ OpenEventW(
|
|||
#define OpenEvent OpenEventW
|
||||
#endif
|
||||
|
||||
PALIMPORT
|
||||
HANDLE
|
||||
PALAPI
|
||||
CreateMutexW(
|
||||
IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
|
||||
IN BOOL bInitialOwner,
|
||||
IN LPCWSTR lpName);
|
||||
|
||||
PALIMPORT
|
||||
HANDLE
|
||||
PALAPI
|
||||
CreateMutexExW(
|
||||
IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
|
||||
IN LPCWSTR lpName,
|
||||
IN DWORD dwFlags,
|
||||
IN DWORD dwDesiredAccess);
|
||||
|
||||
PALIMPORT
|
||||
HANDLE
|
||||
PALAPI
|
||||
PAL_CreateMutexW(
|
||||
IN BOOL bInitialOwner,
|
||||
IN LPCWSTR lpName,
|
||||
IN BOOL bCurrentUserOnly,
|
||||
IN LPSTR lpSystemCallErrors,
|
||||
IN DWORD dwSystemCallErrorsBufferSize);
|
||||
|
||||
// CreateMutexExW: dwFlags
|
||||
#define CREATE_MUTEX_INITIAL_OWNER ((DWORD)0x1)
|
||||
|
||||
#define CreateMutex CreateMutexW
|
||||
|
||||
PALIMPORT
|
||||
HANDLE
|
||||
PALAPI
|
||||
OpenMutexW(
|
||||
IN DWORD dwDesiredAccess,
|
||||
IN BOOL bInheritHandle,
|
||||
IN LPCWSTR lpName);
|
||||
|
||||
PALIMPORT
|
||||
HANDLE
|
||||
PALAPI
|
||||
PAL_OpenMutexW(
|
||||
IN LPCWSTR lpName,
|
||||
IN BOOL bCurrentUserOnly,
|
||||
IN LPSTR lpSystemCallErrors,
|
||||
IN DWORD dwSystemCallErrorsBufferSize);
|
||||
|
||||
#ifdef UNICODE
|
||||
#define OpenMutex OpenMutexW
|
||||
#endif
|
||||
|
||||
PALIMPORT
|
||||
BOOL
|
||||
PALAPI
|
||||
ReleaseMutex(
|
||||
IN HANDLE hMutex);
|
||||
|
||||
PALIMPORT
|
||||
DWORD
|
||||
PALAPI
|
||||
|
@ -906,8 +847,6 @@ GetExitCodeProcess(
|
|||
|
||||
#define MAXIMUM_WAIT_OBJECTS 64
|
||||
#define WAIT_OBJECT_0 0
|
||||
#define WAIT_ABANDONED 0x00000080
|
||||
#define WAIT_ABANDONED_0 0x00000080
|
||||
#define WAIT_TIMEOUT 258
|
||||
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
||||
|
||||
|
@ -920,13 +859,6 @@ WaitForSingleObject(
|
|||
IN HANDLE hHandle,
|
||||
IN DWORD dwMilliseconds);
|
||||
|
||||
PALIMPORT
|
||||
DWORD
|
||||
PALAPI
|
||||
PAL_WaitForSingleObjectPrioritized(
|
||||
IN HANDLE hHandle,
|
||||
IN DWORD dwMilliseconds);
|
||||
|
||||
PALIMPORT
|
||||
DWORD
|
||||
PALAPI
|
||||
|
|
|
@ -200,10 +200,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
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 <sys/mman.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <new>
|
||||
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)
|
||||
|
|
|
@ -75,8 +75,7 @@ CObjectType CorUnix::otFile(
|
|||
CFileProcessLocalDataCleanupRoutine,
|
||||
CObjectType::UnwaitableObject,
|
||||
CObjectType::SignalingNotApplicable,
|
||||
CObjectType::ThreadReleaseNotApplicable,
|
||||
CObjectType::OwnershipNotApplicable
|
||||
CObjectType::ThreadReleaseNotApplicable
|
||||
);
|
||||
|
||||
CAllowedObjectTypes CorUnix::aotFile(otiFile);
|
||||
|
|
|
@ -29,6 +29,7 @@ extern "C"
|
|||
#include <pthread.h>
|
||||
|
||||
#include <minipal/cpuid.h>
|
||||
#include <static_assert.h>
|
||||
|
||||
/* A type to wrap the native context type, which is ucontext_t on some
|
||||
* platforms and another type elsewhere. */
|
||||
|
|
|
@ -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
|
||||
|
@ -917,7 +862,6 @@ namespace CorUnix
|
|||
{
|
||||
WaitSucceeded,
|
||||
Alerted,
|
||||
MutexAbandoned,
|
||||
WaitTimeout,
|
||||
WaitFailed
|
||||
};
|
||||
|
@ -948,13 +892,6 @@ namespace CorUnix
|
|||
DWORD *pdwSignaledObject // OUT
|
||||
) = 0;
|
||||
|
||||
virtual
|
||||
PAL_ERROR
|
||||
AbandonObjectsOwnedByThread(
|
||||
CPalThread *pCallingThread,
|
||||
CPalThread *pTargetThread
|
||||
) = 0;
|
||||
|
||||
virtual
|
||||
PAL_ERROR
|
||||
QueueUserAPC(
|
||||
|
|
|
@ -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 <pthread.h>
|
||||
|
||||
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<sessionId>/<mutexName>
|
||||
- Globally-scoped mutexes (name prefixed with Global) go in /tmp/.dotnet/shm/global/<mutexName>
|
||||
- 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<sessionId>|global/<mutexName>
|
||||
- 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_
|
|
@ -1,317 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
#ifndef _PAL_SHARED_MEMORY_H_
|
||||
#define _PAL_SHARED_MEMORY_H_
|
||||
|
||||
#include "corunix.hpp"
|
||||
#include <minipal/utils.h>
|
||||
|
||||
#ifndef static_assert_no_msg
|
||||
#define static_assert_no_msg( cond ) static_assert( cond, #cond )
|
||||
#endif // !static_assert_no_msg
|
||||
|
||||
// The folder used for storing shared memory files and their lock files is defined in
|
||||
// the gSharedFilesPath global variable. The value of the variable depends on which
|
||||
// OS is being used, and if the application is running in a sandbox in Mac.
|
||||
// gSharedFilesPath ends with '/'
|
||||
// - Global shared memory files go in:
|
||||
// {gSharedFilesPath}/.dotnet/shm/global/<fileName>
|
||||
// - Session-scoped shared memory files go in:
|
||||
// {gSharedFilesPath}/.dotnet/shm/session<sessionId>/<fileName>
|
||||
// - Lock files associated with global shared memory files go in:
|
||||
// {gSharedFilesPath}/.dotnet/lockfiles/global/<fileName>
|
||||
// - Lock files associated with session-scoped shared memory files go in:
|
||||
// {gSharedFilesPath}/.dotnet/lockfiles/session<sessionId>/<fileName>
|
||||
|
||||
#define SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT (_MAX_FNAME - 1)
|
||||
#define SHARED_MEMORY_MAX_NAME_CHAR_COUNT (STRING_LENGTH("Global\\") + SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT)
|
||||
|
||||
#define SHARED_MEMORY_USER_UNSCOPED_RUNTIME_TEMP_DIRECTORY_NAME ".dotnet"
|
||||
#define SHARED_MEMORY_USER_SCOPED_RUNTIME_TEMP_DIRECTORY_NAME_PREFIX ".dotnet-uid"
|
||||
#define SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME "shm"
|
||||
#define SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME "lockfiles"
|
||||
static_assert_no_msg(STRING_LENGTH(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) >= STRING_LENGTH(SHARED_MEMORY_SHARED_MEMORY_DIRECTORY_NAME));
|
||||
|
||||
#define SHARED_MEMORY_GLOBAL_DIRECTORY_NAME "global"
|
||||
#define SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX "session"
|
||||
|
||||
#define SHARED_MEMORY_UNIQUE_TEMP_NAME_TEMPLATE ".dotnet.XXXXXX"
|
||||
|
||||
// Note that this Max size does not include the prefix folder path size which is unknown (in the case of sandbox) until runtime
|
||||
#define SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT \
|
||||
( \
|
||||
STRING_LENGTH(SHARED_MEMORY_USER_SCOPED_RUNTIME_TEMP_DIRECTORY_NAME_PREFIX) + \
|
||||
11 /* user ID, path separator */ + \
|
||||
STRING_LENGTH(SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME) + \
|
||||
1 /* path separator */ + \
|
||||
STRING_LENGTH(SHARED_MEMORY_SESSION_DIRECTORY_NAME_PREFIX) + \
|
||||
11 /* session ID, path separator */ + \
|
||||
SHARED_MEMORY_MAX_FILE_NAME_CHAR_COUNT \
|
||||
)
|
||||
|
||||
class AutoFreeBuffer
|
||||
{
|
||||
private:
|
||||
void *m_buffer;
|
||||
bool m_cancel;
|
||||
|
||||
public:
|
||||
AutoFreeBuffer(void *buffer);
|
||||
~AutoFreeBuffer();
|
||||
|
||||
public:
|
||||
void Cancel();
|
||||
};
|
||||
|
||||
enum class SharedMemoryError : DWORD
|
||||
{
|
||||
NameEmpty = ERROR_INVALID_PARAMETER,
|
||||
NameTooLong = ERROR_FILENAME_EXCED_RANGE,
|
||||
NameInvalid = ERROR_INVALID_NAME,
|
||||
HeaderMismatch = ERROR_INVALID_HANDLE,
|
||||
OutOfMemory = ERROR_NOT_ENOUGH_MEMORY,
|
||||
IO = ERROR_OPEN_FAILED
|
||||
};
|
||||
|
||||
class SharedMemoryException
|
||||
{
|
||||
private:
|
||||
DWORD m_errorCode;
|
||||
|
||||
public:
|
||||
SharedMemoryException(DWORD errorCode);
|
||||
DWORD GetErrorCode() const;
|
||||
};
|
||||
|
||||
class SharedMemorySystemCallErrors
|
||||
{
|
||||
private:
|
||||
char *m_buffer;
|
||||
int m_bufferSize;
|
||||
int m_length;
|
||||
bool m_isTracking;
|
||||
|
||||
public:
|
||||
SharedMemorySystemCallErrors(char *buffer, int bufferSize);
|
||||
void Append(LPCSTR format, ...);
|
||||
};
|
||||
|
||||
class SharedMemoryId;
|
||||
|
||||
class SharedMemoryHelpers
|
||||
{
|
||||
private:
|
||||
static const mode_t PermissionsMask_OwnerUser_ReadWrite;
|
||||
static const mode_t PermissionsMask_OwnerUser_ReadWriteExecute;
|
||||
static const mode_t PermissionsMask_NonOwnerUsers_Write;
|
||||
static const mode_t PermissionsMask_AllUsers_ReadWrite;
|
||||
static const mode_t PermissionsMask_AllUsers_ReadWriteExecute;
|
||||
static const mode_t PermissionsMask_Sticky;
|
||||
public:
|
||||
static const UINT32 InvalidProcessId;
|
||||
static const SIZE_T InvalidThreadId;
|
||||
static const UINT64 InvalidSharedThreadId;
|
||||
|
||||
public:
|
||||
static SIZE_T AlignDown(SIZE_T value, SIZE_T alignment);
|
||||
static SIZE_T AlignUp(SIZE_T value, SIZE_T alignment);
|
||||
|
||||
static void *Alloc(SIZE_T byteCount);
|
||||
static bool AppendUInt32String(PathCharString& destination, UINT32 value);
|
||||
|
||||
static bool EnsureDirectoryExists(SharedMemorySystemCallErrors *errors, const char *path, const SharedMemoryId *id, bool isGlobalLockAcquired, bool createIfNotExist = true, bool isSystemDirectory = false);
|
||||
private:
|
||||
static int Open(SharedMemorySystemCallErrors *errors, LPCSTR path, int flags, mode_t mode = static_cast<mode_t>(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_
|
|
@ -21,7 +21,6 @@ Abstract:
|
|||
|
||||
#include "corunix.hpp"
|
||||
#include "threadinfo.hpp"
|
||||
#include "mutex.hpp"
|
||||
#include "list.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
@ -68,7 +67,6 @@ namespace CorUnix
|
|||
class CSynchData;
|
||||
|
||||
typedef struct _WaitingThreadsListNode * PWaitingThreadsListNode;
|
||||
typedef struct _OwnedObjectsListNode * POwnedObjectsListNode;
|
||||
typedef struct _ThreadApcInfoNode * PThreadApcInfoNode;
|
||||
|
||||
typedef struct _ThreadWaitInfo
|
||||
|
@ -113,8 +111,6 @@ namespace CorUnix
|
|||
Volatile<LONG> m_lLocalSynchLockCount;
|
||||
LIST_ENTRY m_leOwnedObjsList;
|
||||
|
||||
NamedMutexProcessData *m_ownedNamedMutexListHead;
|
||||
|
||||
ThreadNativeWaitData m_tnwdNativeData;
|
||||
ThreadWaitInfo m_twiWaitInfo;
|
||||
|
||||
|
@ -160,20 +156,6 @@ namespace CorUnix
|
|||
PAL_ERROR RunDeferredThreadConditionSignalings();
|
||||
#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
|
||||
|
||||
// NOTE: the following methods provide non-synchronized access to
|
||||
// the list of owned objects for this thread. Any thread
|
||||
// accessing this list MUST own the appropriate
|
||||
// synchronization lock(s).
|
||||
void AddObjectToOwnedList(POwnedObjectsListNode pooln);
|
||||
void RemoveObjectFromOwnedList(POwnedObjectsListNode pooln);
|
||||
POwnedObjectsListNode RemoveFirstObjectFromOwnedList(void);
|
||||
|
||||
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)
|
||||
|
|
|
@ -24,7 +24,6 @@ Abstract:
|
|||
|
||||
#include "pal/threadinfo.hpp"
|
||||
#include "pal/thread.hpp"
|
||||
#include "pal/mutex.hpp"
|
||||
#include "pal/init.h"
|
||||
#if !HAVE_MACH_EXCEPTIONS
|
||||
#include <signal.h>
|
||||
|
|
|
@ -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
|
||||
|
@ -1135,63 +1116,3 @@ static LPWSTR INIT_GetCurrentEXEPath()
|
|||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/*++
|
||||
Function:
|
||||
INIT_SharedFilesPath
|
||||
|
||||
Abstract:
|
||||
Initializes the shared application
|
||||
--*/
|
||||
static BOOL INIT_SharedFilesPath(void)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// Store application group Id. It will be null if not set
|
||||
gApplicationGroupId = getenv("DOTNET_SANDBOX_APPLICATION_GROUP_ID");
|
||||
|
||||
if (nullptr != gApplicationGroupId)
|
||||
{
|
||||
// Verify the length of the application group ID
|
||||
gApplicationGroupIdLength = strlen(gApplicationGroupId);
|
||||
if (gApplicationGroupIdLength > MAX_APPLICATION_GROUP_ID_LENGTH)
|
||||
{
|
||||
SetLastError(ERROR_BAD_LENGTH);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// In sandbox, all IPC files (locks, pipes) should be written to the application group
|
||||
// container. There will be no write permissions to TEMP_DIRECTORY_PATH
|
||||
if (!GetApplicationContainerFolder(*gSharedFilesPath, gApplicationGroupId, gApplicationGroupIdLength))
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Verify the size of the path won't exceed maximum allowed size
|
||||
if (gSharedFilesPath->GetCount() + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ > MAX_LONGPATH)
|
||||
{
|
||||
SetLastError(ERROR_FILENAME_EXCED_RANGE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check if the path already exists and it's a directory
|
||||
struct stat statInfo;
|
||||
int statResult = stat(*gSharedFilesPath, &statInfo);
|
||||
|
||||
// If the path exists, check that it's a directory
|
||||
if (statResult != 0 || !(statInfo.st_mode & S_IFDIR))
|
||||
{
|
||||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
||||
// If we are here, then we are not in sandbox mode, resort to TEMP_DIRECTORY_PATH as shared files path
|
||||
return gSharedFilesPath->Set(TEMP_DIRECTORY_PATH);
|
||||
|
||||
// We can verify statically the non sandboxed case, since the size is known during compile time
|
||||
static_assert_no_msg(STRING_LENGTH(TEMP_DIRECTORY_PATH) + SHARED_MEMORY_MAX_FILE_PATH_CHAR_COUNT + 1 /* null terminator */ <= MAX_LONGPATH);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -23,6 +23,7 @@ Abstract:
|
|||
#include "pal/cruntime.h"
|
||||
#include "pal/file.h"
|
||||
#include "pal/environ.h"
|
||||
#include <static_assert.h>
|
||||
|
||||
/* standard headers */
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -133,8 +133,7 @@ namespace CorUnix
|
|||
signaled)
|
||||
--*/
|
||||
PAL_ERROR CSynchWaitController::CanThreadWaitWithoutBlocking(
|
||||
bool * pfCanWaitWithoutBlocking,
|
||||
bool * pfAbandoned)
|
||||
bool * pfCanWaitWithoutBlocking)
|
||||
{
|
||||
VALIDATEOBJECT(m_psdSynchData);
|
||||
|
||||
|
@ -142,9 +141,8 @@ namespace CorUnix
|
|||
|
||||
_ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
|
||||
_ASSERTE(NULL != pfCanWaitWithoutBlocking);
|
||||
_ASSERTE(NULL != pfAbandoned);
|
||||
|
||||
fRetVal = m_psdSynchData->CanWaiterWaitWithoutBlocking(m_pthrOwner, pfAbandoned);
|
||||
fRetVal = m_psdSynchData->CanWaiterWaitWithoutBlocking(m_pthrOwner);
|
||||
|
||||
if(!fRetVal && otiProcess == m_psdSynchData->GetObjectTypeId())
|
||||
{
|
||||
|
@ -561,117 +559,6 @@ namespace CorUnix
|
|||
return palErr;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CSynchStateController::SetOwner
|
||||
|
||||
Sets the owner of the target object and initializes the ownership
|
||||
count to 1 (for objects with tracked ownership).
|
||||
--*/
|
||||
PAL_ERROR CSynchStateController::SetOwner(CPalThread * pNewOwningThread)
|
||||
{
|
||||
VALIDATEOBJECT(m_psdSynchData);
|
||||
|
||||
PAL_ERROR palErr = NO_ERROR;
|
||||
|
||||
_ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
|
||||
_ASSERTE(NULL != pNewOwningThread);
|
||||
_ASSERT_MSG(CObjectType::OwnershipTracked ==
|
||||
m_potObjectType->GetOwnershipSemantics(),
|
||||
"SetOwner called on an object without OwnershipTracked "
|
||||
"semantics\n");
|
||||
|
||||
if (0 != m_psdSynchData->GetOwnershipCount())
|
||||
{
|
||||
ASSERT("Ownership count should be zero at this time\n");
|
||||
palErr = ERROR_INTERNAL_ERROR;
|
||||
goto SO_exit;
|
||||
}
|
||||
|
||||
palErr = m_psdSynchData->AssignOwnershipToThread(m_pthrOwner,
|
||||
pNewOwningThread);
|
||||
|
||||
_ASSERT_MSG(0 == m_psdSynchData->GetOwnershipCount() ||
|
||||
0 == m_psdSynchData->GetSignalCount(),
|
||||
"Conflicting values for SignalCount [%d] and "
|
||||
"OwnershipCount [%d]\n",
|
||||
m_psdSynchData->GetOwnershipCount(),
|
||||
m_psdSynchData->GetSignalCount());
|
||||
|
||||
SO_exit:
|
||||
return palErr;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CSynchStateController::DecrementOwnershipCount
|
||||
|
||||
Decrements the ownership count of the target object possibly triggering
|
||||
waiting threads awakening (for objects with tracked ownership).
|
||||
--*/
|
||||
PAL_ERROR CSynchStateController::DecrementOwnershipCount()
|
||||
{
|
||||
VALIDATEOBJECT(m_psdSynchData);
|
||||
|
||||
PAL_ERROR palErr = NO_ERROR;
|
||||
LONG lOwnershipCount = m_psdSynchData->GetOwnershipCount();
|
||||
|
||||
_ASSERTE(InternalGetCurrentThread() == m_pthrOwner);
|
||||
_ASSERT_MSG(CObjectType::OwnershipTracked ==
|
||||
m_potObjectType->GetOwnershipSemantics(),
|
||||
"Trying to decrement ownership count on an object with "
|
||||
"ownership semantics other than OwnershipTracked\n");
|
||||
_ASSERT_MSG(0 <= lOwnershipCount,
|
||||
"Operation would make ownership count negative - object "
|
||||
"should be owned at this time [ownership count=%d]\n",
|
||||
lOwnershipCount);
|
||||
|
||||
if ( (1 > lOwnershipCount) ||
|
||||
(m_psdSynchData->GetOwnerProcessID() != gPID) ||
|
||||
(m_psdSynchData->GetOwnerThread() != m_pthrOwner) )
|
||||
{
|
||||
palErr = ERROR_NOT_OWNER;
|
||||
goto DOC_exit;
|
||||
}
|
||||
|
||||
lOwnershipCount--;
|
||||
m_psdSynchData->SetOwnershipCount(lOwnershipCount);
|
||||
|
||||
if (0 == lOwnershipCount)
|
||||
{
|
||||
CPalSynchronizationManager * pSynchManager =
|
||||
CPalSynchronizationManager::GetInstance();
|
||||
OwnedObjectsListNode * pooln =
|
||||
m_psdSynchData->GetOwnershipListNode();
|
||||
|
||||
_ASSERT_MSG(NULL != pooln,
|
||||
"Null ownership node pointer in SynchData with ownership "
|
||||
"semantics\n");
|
||||
_ASSERT_MSG(m_psdSynchData == pooln->pPalObjSynchData,
|
||||
"Corrupted ownership node\n");
|
||||
|
||||
// Object has been released
|
||||
// Remove it from list of owned objs for current thread
|
||||
m_pthrOwner->synchronizationInfo.RemoveObjectFromOwnedList(pooln);
|
||||
|
||||
// Release SynchData reference count implied by the ownership
|
||||
// list node
|
||||
m_psdSynchData->Release(m_pthrOwner);
|
||||
|
||||
// Return node to the cache
|
||||
pSynchManager->CacheAddOwnedObjsListNode(m_pthrOwner, pooln);
|
||||
|
||||
// Reset ownership
|
||||
m_psdSynchData->ResetOwnership();
|
||||
|
||||
// Signal it and trigger waiter thread awakening
|
||||
m_psdSynchData->Signal(m_pthrOwner, 1);
|
||||
}
|
||||
|
||||
DOC_exit:
|
||||
return palErr;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CSynchStateController::ReleaseController
|
||||
|
@ -786,11 +673,8 @@ namespace CorUnix
|
|||
CObjectType::SignalingSemantics ssSignalingSemantics =
|
||||
potObjectType->GetSignalingSemantics();
|
||||
#endif // _DEBUG
|
||||
CObjectType::OwnershipSemantics osOwnershipSemantics =
|
||||
potObjectType->GetOwnershipSemantics();
|
||||
CObjectType::ThreadReleaseSemantics trsThreadReleaseSemantics =
|
||||
potObjectType->GetThreadReleaseSemantics();
|
||||
bool fReenteringObjWithOwnership = false;
|
||||
|
||||
_ASSERT_MSG(CObjectType::SignalingNotApplicable != ssSignalingSemantics,
|
||||
"Signaling not applicable");
|
||||
|
@ -798,59 +682,24 @@ namespace CorUnix
|
|||
trsThreadReleaseSemantics,
|
||||
"Thread releasing not applicable");
|
||||
_ASSERT_MSG(CObjectType::SingleTransitionObject != ssSignalingSemantics ||
|
||||
(CObjectType::ThreadReleaseHasNoSideEffects ==
|
||||
trsThreadReleaseSemantics &&
|
||||
CObjectType::NoOwner == osOwnershipSemantics),
|
||||
CObjectType::ThreadReleaseHasNoSideEffects == trsThreadReleaseSemantics,
|
||||
"Conflicting object synchronization attributes "
|
||||
"[SignalingSemantics=%u OwnershipSemantics=%u "
|
||||
"ThreadReleaseSemantics=%u]\n", ssSignalingSemantics,
|
||||
osOwnershipSemantics, trsThreadReleaseSemantics);
|
||||
"[SignalingSemantics=%u "
|
||||
"ThreadReleaseSemantics=%u]\n",
|
||||
ssSignalingSemantics,
|
||||
trsThreadReleaseSemantics);
|
||||
|
||||
if (CObjectType::OwnershipTracked == osOwnershipSemantics &&
|
||||
0 < GetOwnershipCount())
|
||||
{
|
||||
// We are rentering an object with ownership: we need to skip
|
||||
// the object unsignaling
|
||||
fReenteringObjWithOwnership = true;
|
||||
}
|
||||
|
||||
if (!fReenteringObjWithOwnership &&
|
||||
CObjectType::ThreadReleaseAltersSignalCount == trsThreadReleaseSemantics)
|
||||
if (CObjectType::ThreadReleaseAltersSignalCount == trsThreadReleaseSemantics)
|
||||
{
|
||||
_ASSERT_MSG(0 < GetSignalCount(),
|
||||
"Internal error: operation would make signal count "
|
||||
"negative - object should be signaled at this time "
|
||||
"[signal count=%d]", GetSignalCount());
|
||||
_ASSERT_MSG(CObjectType::OwnershipTracked != osOwnershipSemantics ||
|
||||
1 == GetSignalCount(),
|
||||
"Ownable objects cannot have signal count greater "
|
||||
"than zero [current SignalCount=%d]\n",
|
||||
GetSignalCount());
|
||||
|
||||
// Unsignal the object
|
||||
DecrementSignalCount();
|
||||
}
|
||||
|
||||
if (CObjectType::OwnershipTracked == osOwnershipSemantics)
|
||||
{
|
||||
_ASSERT_MSG(0 == GetOwnershipCount() || 0 == GetSignalCount(),
|
||||
"OwnershipCount and SignalCount with conflicting "
|
||||
"values\n");
|
||||
|
||||
// Take ownership or increment ownership count.
|
||||
// We do this after the object unsignaling to minimize possibilities
|
||||
// of having both SignalCount and OwnershipCount greater than zero
|
||||
// (see comment in AssignOwnershipToThread)
|
||||
palErr = AssignOwnershipToThread(pthrCurrent, pthrTarget);
|
||||
|
||||
if (NO_ERROR != palErr)
|
||||
{
|
||||
ERROR("AssignOwnershipToThread failed with error %u; "
|
||||
"ownership data on object with SynchData {p=%p} "
|
||||
"may be corrupted\n", palErr, this);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SYNCH_STATISTICS
|
||||
if (NO_ERROR == palErr)
|
||||
{
|
||||
|
@ -873,45 +722,11 @@ namespace CorUnix
|
|||
object is local, both local and shared one if the object is shared).
|
||||
--*/
|
||||
bool CSynchData::CanWaiterWaitWithoutBlocking(
|
||||
CPalThread * pWaiterThread,
|
||||
bool * pfAbandoned)
|
||||
CPalThread * pWaiterThread)
|
||||
{
|
||||
VALIDATEOBJECT(this);
|
||||
|
||||
bool fRetVal = (0 < GetSignalCount());
|
||||
bool fAbandoned = false;
|
||||
bool fOwnershipTracked = (CObjectType::OwnershipTracked ==
|
||||
GetObjectType()->GetOwnershipSemantics());
|
||||
if (fRetVal)
|
||||
{
|
||||
// Object signaled: thread can wait without blocking
|
||||
if (fOwnershipTracked)
|
||||
{
|
||||
fAbandoned = IsAbandoned();
|
||||
}
|
||||
|
||||
goto CWWWB_exit;
|
||||
}
|
||||
|
||||
// Object not signaled: thread can wait without blocking only if the
|
||||
// object is an ownable one, and it is owned by the current thread
|
||||
if (fOwnershipTracked)
|
||||
{
|
||||
_ASSERT_MSG(0 < GetSignalCount() || 0 < GetOwnershipCount(),
|
||||
"Objects with ownership must be either signaled or "
|
||||
"owned by a thread\n");
|
||||
|
||||
if ((GetOwnerProcessID() == gPID) &&
|
||||
(GetOwnerThread() == pWaiterThread) )
|
||||
{
|
||||
fRetVal = true;
|
||||
goto CWWWB_exit;
|
||||
}
|
||||
}
|
||||
|
||||
CWWWB_exit:
|
||||
*pfAbandoned = fAbandoned;
|
||||
return fRetVal;
|
||||
return 0 < GetSignalCount();
|
||||
}
|
||||
|
||||
/*++
|
||||
|
@ -958,16 +773,6 @@ namespace CorUnix
|
|||
}
|
||||
}
|
||||
|
||||
_ASSERT_MSG(CObjectType::OwnershipTracked !=
|
||||
GetObjectType()->GetOwnershipSemantics() ||
|
||||
0 == GetOwnershipCount() || 0 == GetSignalCount(),
|
||||
"Conflicting values for SignalCount [%d] and "
|
||||
"OwnershipCount [%d]\n",
|
||||
GetOwnershipCount(), GetSignalCount());
|
||||
|
||||
_ASSERT_MSG(otiMutex != m_otiObjectTypeId || m_lSignalCount <= 1,
|
||||
"Mutex with invalid singal count\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1042,31 +847,6 @@ namespace CorUnix
|
|||
dwObjIdx = pwtlnItem->dwObjIndex;
|
||||
|
||||
ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo;
|
||||
bool fAbandoned = false;
|
||||
|
||||
if (CObjectType::OwnershipTracked ==
|
||||
GetObjectType()->GetOwnershipSemantics())
|
||||
{
|
||||
// Get the abandoned status before resetting it by
|
||||
// assigning ownership to target thread
|
||||
fAbandoned = IsAbandoned();
|
||||
|
||||
// Assign ownership to target thread
|
||||
// Note: This will cause both ownership count and
|
||||
// signal count to be greater than zero at the
|
||||
// same time; the signal count will be anyway
|
||||
// decremented immediately by the caller
|
||||
// CsynchData::Signal
|
||||
palErr = AssignOwnershipToThread(pthrCurrent,
|
||||
ptwiWaitInfo->pthrOwner);
|
||||
if (NO_ERROR != palErr)
|
||||
{
|
||||
ERROR("Synch Worker: AssignOwnershipToThread "
|
||||
"failed with error %u; ownership data on "
|
||||
"object with SynchData %p may be "
|
||||
"corrupted\n", palErr, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (fWaitAll)
|
||||
{
|
||||
|
@ -1093,7 +873,7 @@ namespace CorUnix
|
|||
palErr = CPalSynchronizationManager::WakeUpLocalThread(
|
||||
pthrCurrent,
|
||||
ptwiWaitInfo->pthrOwner,
|
||||
fAbandoned ? MutexAbandoned : WaitSucceeded,
|
||||
WaitSucceeded,
|
||||
dwObjIdx);
|
||||
|
||||
if (NO_ERROR != palErr)
|
||||
|
@ -1176,26 +956,6 @@ namespace CorUnix
|
|||
dwObjIdx = pwtlnItem->dwObjIndex;
|
||||
|
||||
ThreadWaitInfo * ptwiWaitInfo = pwtlnItem->ptwiWaitInfo;
|
||||
bool fAbandoned = false;
|
||||
|
||||
if (CObjectType::OwnershipTracked ==
|
||||
GetObjectType()->GetOwnershipSemantics())
|
||||
{
|
||||
// Get the abandoned status before resetting it by
|
||||
// assigning ownership to target thread
|
||||
fAbandoned = IsAbandoned();
|
||||
|
||||
// Assign ownership to target thread
|
||||
palErr = AssignOwnershipToThread(pthrCurrent,
|
||||
ptwiWaitInfo->pthrOwner);
|
||||
if (NO_ERROR != palErr)
|
||||
{
|
||||
ERROR("Synch Worker: AssignOwnershipToThread "
|
||||
"failed with error %u; ownership data on "
|
||||
"object with SynchData %p may be "
|
||||
"corrupted\n", palErr, this);
|
||||
}
|
||||
}
|
||||
|
||||
if (fWaitAll)
|
||||
{
|
||||
|
@ -1222,7 +982,7 @@ namespace CorUnix
|
|||
palErr = CPalSynchronizationManager::WakeUpLocalThread(
|
||||
pthrCurrent,
|
||||
ptwiWaitInfo->pthrOwner,
|
||||
fAbandoned ? MutexAbandoned : WaitSucceeded,
|
||||
WaitSucceeded,
|
||||
dwObjIdx);
|
||||
|
||||
if (NO_ERROR != palErr)
|
||||
|
@ -1267,7 +1027,7 @@ namespace CorUnix
|
|||
WaitCompletionState CSynchData::IsRestOfWaitAllSatisfied(
|
||||
WaitingThreadsListNode * pwtlnNode)
|
||||
{
|
||||
int iSignaledOrOwnedObjCount = 0;
|
||||
int iSignaledObjCount = 0;
|
||||
int iTgtCount = 0;
|
||||
int i;
|
||||
WaitCompletionState wcsWaitCompletionState = WaitIsNotSatisfied;
|
||||
|
@ -1294,7 +1054,6 @@ namespace CorUnix
|
|||
{
|
||||
WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
|
||||
bool fRetVal;
|
||||
bool fIsAbandoned;
|
||||
|
||||
VALIDATEOBJECT(pwtlnItem);
|
||||
|
||||
|
@ -1311,17 +1070,16 @@ namespace CorUnix
|
|||
// The target object (the one related to pwtlnNode) is counted as
|
||||
// signaled/owned without checking it (also if it is not, as
|
||||
// it normally happens when this method is called)
|
||||
iSignaledOrOwnedObjCount++;
|
||||
iSignaledObjCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
fRetVal = psdSynchDataItem->CanWaiterWaitWithoutBlocking(
|
||||
ptwiWaitInfo->pthrOwner,
|
||||
&fIsAbandoned);
|
||||
ptwiWaitInfo->pthrOwner);
|
||||
|
||||
if (fRetVal)
|
||||
{
|
||||
iSignaledOrOwnedObjCount++;
|
||||
iSignaledObjCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1329,7 +1087,7 @@ namespace CorUnix
|
|||
}
|
||||
}
|
||||
|
||||
if (iSignaledOrOwnedObjCount < iTgtCount)
|
||||
if (iSignaledObjCount < iTgtCount)
|
||||
{
|
||||
wcsWaitCompletionState = WaitIsNotSatisfied;
|
||||
}
|
||||
|
@ -1343,145 +1101,6 @@ namespace CorUnix
|
|||
return wcsWaitCompletionState;
|
||||
}
|
||||
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CSynchData::SetOwner
|
||||
|
||||
Blindly sets the thread whose CPalThread is passed as argument, as the
|
||||
owner of the current object.
|
||||
WARNING: this method discards any previous ownership data and does not
|
||||
update the list of the object owned by the owner thread.
|
||||
|
||||
Note: this method must be called while holding the appropriate
|
||||
synchronization locks (the local process synch lock if the target
|
||||
object is local, both local and shared one if the object is shared).
|
||||
--*/
|
||||
void CSynchData::SetOwner(CPalThread * pOwnerThread)
|
||||
{
|
||||
VALIDATEOBJECT(this);
|
||||
|
||||
m_dwOwnerPid = gPID;
|
||||
m_dwOwnerTid = pOwnerThread->GetThreadId();
|
||||
m_pOwnerThread = pOwnerThread;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CSynchData::ResetOwnership
|
||||
|
||||
Resets current object's ownership data
|
||||
|
||||
Note: this method must be called while holding the appropriate
|
||||
synchronization locks (the local process synch lock if the target
|
||||
object is local, both local and shared one if the object is shared).
|
||||
--*/
|
||||
void CSynchData::ResetOwnership()
|
||||
{
|
||||
VALIDATEOBJECT(this);
|
||||
|
||||
m_lOwnershipCount = 0;
|
||||
m_dwOwnerPid = 0;
|
||||
m_dwOwnerTid = 0;
|
||||
m_pOwnerThread = NULL;
|
||||
m_poolnOwnedObjectListNode = NULL;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CSynchData::AssignOwnershipToThread
|
||||
|
||||
Assigns thw ownership of the current object to the target thread, performing
|
||||
all the operations neede to mantain the correct status of ownership data,
|
||||
also handling recursive object ownership acquisition
|
||||
|
||||
Note: this method must be called while holding the appropriate
|
||||
synchronization locks (the local process synch lock if the target
|
||||
object is local, both local and shared one if the object is shared).
|
||||
--*/
|
||||
PAL_ERROR CSynchData::AssignOwnershipToThread(
|
||||
CPalThread * pthrCurrent,
|
||||
CPalThread * pthrTarget)
|
||||
{
|
||||
// Note: when this method is called by ReleaseFirstWaiter there is
|
||||
// a small time window in which both SignalCount and
|
||||
// OwnershipCount can be greater than zero (which normally
|
||||
// is illegal). Anyway that is fine since ReleaseFirstWaiter
|
||||
// will restore the value right after, and such situation
|
||||
// takes place while holding synchroniztion locks, so no
|
||||
// other thread/process can access the object.
|
||||
|
||||
PAL_ERROR palErr = NO_ERROR;
|
||||
|
||||
_ASSERT_MSG(CObjectType::OwnershipTracked ==
|
||||
GetObjectType()->GetOwnershipSemantics(),
|
||||
"AssignOwnershipToThread called on a non-ownable "
|
||||
"CSynchData [this=%p OwnershipSemantics=%u]\n", this,
|
||||
GetObjectType()->GetOwnershipSemantics());
|
||||
|
||||
|
||||
if (0 < m_lOwnershipCount)
|
||||
{
|
||||
//
|
||||
// Object already owned, incrementing ownership count
|
||||
//
|
||||
_ASSERT_MSG(0 == GetSignalCount(),
|
||||
"Conflicting OwnershipCount and SignalCount values\n");
|
||||
|
||||
_ASSERT_MSG(pthrTarget == m_pOwnerThread && gPID == m_dwOwnerPid,
|
||||
"Attempting to assign ownership of CSynchData %p to "
|
||||
"thread {pid=%#x tid=%#x} while it is currently owned "
|
||||
"by thread {pid=%#x tid=%#x}\n", this,
|
||||
gPID, pthrTarget->GetThreadId(),
|
||||
m_dwOwnerPid, m_pOwnerThread->GetThreadId());
|
||||
|
||||
m_lOwnershipCount++;
|
||||
|
||||
TRACE("Incrementing ownership count for object with "
|
||||
"SynchData %p owned by thread %#x [new count=%d]\n",
|
||||
this, pthrTarget->GetThreadId(), m_lOwnershipCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Acquiring currently not owned object
|
||||
//
|
||||
CPalSynchronizationManager * pSynchManager =
|
||||
CPalSynchronizationManager::GetInstance();
|
||||
OwnedObjectsListNode * pooln;
|
||||
|
||||
pooln = pSynchManager->CacheGetOwnedObjsListNode(pthrCurrent);
|
||||
if (NULL == pooln)
|
||||
{
|
||||
ERROR("Out of memory while acquiring mutex ownership");
|
||||
// In this case we bail out. It will result in no
|
||||
// thread being awakend, which may cause deadlock,
|
||||
// but it is anyway better than corrupting the
|
||||
// ownership list
|
||||
palErr = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto AOTT_exit;
|
||||
}
|
||||
|
||||
TRACE("Assigning ownable object with SynchData %p to "
|
||||
"thread %#x\n",
|
||||
this, pthrTarget->GetThreadId());
|
||||
|
||||
// Set ownership data
|
||||
SetOwner(pthrTarget);
|
||||
SetOwnershipListNode(pooln);
|
||||
SetOwnershipCount(1);
|
||||
SetAbandoned(false);
|
||||
|
||||
// Add object to list of owned objs for current thread
|
||||
pooln->pPalObjSynchData = this;
|
||||
AddRef();
|
||||
pthrTarget->synchronizationInfo.AddObjectToOwnedList(pooln);
|
||||
}
|
||||
|
||||
AOTT_exit:
|
||||
return palErr;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CSynchData::WaiterEnqueue
|
||||
|
|
|
@ -159,8 +159,7 @@ namespace CorUnix
|
|||
m_cacheSHRSynchData(SynchDataCacheMaxSize),
|
||||
m_cacheWTListNodes(WTListNodeCacheMaxSize),
|
||||
m_cacheSHRWTListNodes(WTListNodeCacheMaxSize),
|
||||
m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize),
|
||||
m_cacheOwnedObjectsListNodes(OwnedObjectsListCacheMaxSize)
|
||||
m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize)
|
||||
{
|
||||
#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
|
||||
m_iKQueue = -1;
|
||||
|
@ -396,7 +395,6 @@ namespace CorUnix
|
|||
break;
|
||||
}
|
||||
case WaitSucceeded:
|
||||
case MutexAbandoned:
|
||||
*pdwSignaledObject = dwSigObjIdx;
|
||||
break;
|
||||
default:
|
||||
|
@ -533,128 +531,6 @@ namespace CorUnix
|
|||
return palErr;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CPalSynchronizationManager::AbandonObjectsOwnedByThread
|
||||
|
||||
This method is called by a thread at thread-exit time to abandon
|
||||
any currently owned waitable object (mutexes). If pthrTarget is
|
||||
different from pthrCurrent, AbandonObjectsOwnedByThread assumes
|
||||
to be called whether by TerminateThread or at shutdown time. See
|
||||
comments below for more details
|
||||
--*/
|
||||
PAL_ERROR CPalSynchronizationManager::AbandonObjectsOwnedByThread(
|
||||
CPalThread * pthrCurrent,
|
||||
CPalThread * pthrTarget)
|
||||
{
|
||||
PAL_ERROR palErr = NO_ERROR;
|
||||
OwnedObjectsListNode * poolnItem;
|
||||
CThreadSynchronizationInfo * pSynchInfo = &pthrTarget->synchronizationInfo;
|
||||
CPalSynchronizationManager * pSynchManager = GetInstance();
|
||||
|
||||
// The shared memory manager's process lock is acquired before calling into some PAL synchronization primitives that may
|
||||
// take the PAL synchronization manager's synch lock (acquired below). For example, when using a file lock
|
||||
// implementation for a named mutex (see NamedMutexProcessData::NamedMutexProcessData()), under the shared memory
|
||||
// manager's process lock, CreateMutex is called, which acquires the PAL synchronization manager's synch lock. The same
|
||||
// lock order needs to be maintained here to avoid a deadlock.
|
||||
bool abandonNamedMutexes = pSynchInfo->OwnsAnyNamedMutex();
|
||||
if (abandonNamedMutexes)
|
||||
{
|
||||
SharedMemoryManager::AcquireCreationDeletionProcessLock();
|
||||
}
|
||||
|
||||
// Local lock
|
||||
AcquireLocalSynchLock(pthrCurrent);
|
||||
|
||||
// Abandon owned objects
|
||||
while (NULL != (poolnItem = pSynchInfo->RemoveFirstObjectFromOwnedList()))
|
||||
{
|
||||
CSynchData * psdSynchData = poolnItem->pPalObjSynchData;
|
||||
|
||||
_ASSERT_MSG(NULL != psdSynchData,
|
||||
"NULL psdSynchData pointer in ownership list node\n");
|
||||
|
||||
VALIDATEOBJECT(psdSynchData);
|
||||
|
||||
TRACE("Abandoning object with SynchData at %p\n", psdSynchData);
|
||||
|
||||
// Reset ownership data
|
||||
psdSynchData->ResetOwnership();
|
||||
|
||||
// Set abandoned status; in case there is a thread to be released:
|
||||
// - if the thread is local, ReleaseFirstWaiter will reset the
|
||||
// abandoned status
|
||||
// - if the thread is remote, the remote worker thread will use
|
||||
// the value and reset it
|
||||
psdSynchData->SetAbandoned(true);
|
||||
|
||||
// Signal the object and trigger thread awakening
|
||||
psdSynchData->Signal(pthrCurrent, 1);
|
||||
|
||||
// Release reference to SynchData
|
||||
psdSynchData->Release(pthrCurrent);
|
||||
|
||||
// Return node to the cache
|
||||
pSynchManager->m_cacheOwnedObjectsListNodes.Add(pthrCurrent, poolnItem);
|
||||
}
|
||||
|
||||
if (abandonNamedMutexes)
|
||||
{
|
||||
// Abandon owned named mutexes
|
||||
while (true)
|
||||
{
|
||||
NamedMutexProcessData *processData = pSynchInfo->RemoveFirstOwnedNamedMutex();
|
||||
if (processData == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
processData->Abandon();
|
||||
}
|
||||
}
|
||||
|
||||
if (pthrTarget != pthrCurrent)
|
||||
{
|
||||
// If the target thead is not the current one, we are being called
|
||||
// at shutdown time, right before the target thread is suspended,
|
||||
// or anyway the target thread is being terminated.
|
||||
// In this case we switch its wait state to TWS_EARLYDEATH so that,
|
||||
// if the thread is currently waiting/sleeping and it wakes up
|
||||
// before shutdown code manage to suspend it, it will be rerouted
|
||||
// to ThreadPrepareForShutdown (that will be done without holding
|
||||
// any internal lock, in a way to accommodate shutdown time thread
|
||||
// suspension).
|
||||
// At this time we also unregister the wait, so no dummy nodes are
|
||||
// left around on waiting objects.
|
||||
// The TWS_EARLYDEATH wait-state will also prevent the thread from
|
||||
// successfully registering for a possible new wait in the same
|
||||
// time window.
|
||||
LONG lTWState;
|
||||
DWORD * pdwWaitState;
|
||||
|
||||
pdwWaitState = SharedIDToTypePointer(DWORD, pthrTarget->synchronizationInfo.m_shridWaitAwakened);
|
||||
lTWState = InterlockedExchange((LONG *)pdwWaitState, TWS_EARLYDEATH);
|
||||
|
||||
if (( ((LONG)TWS_WAITING == lTWState) || ((LONG)TWS_ALERTABLE == lTWState) ) &&
|
||||
(0 < pSynchInfo->m_twiWaitInfo.lObjCount))
|
||||
{
|
||||
// Unregister the wait
|
||||
UnRegisterWait(pthrCurrent, &pSynchInfo->m_twiWaitInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock
|
||||
ReleaseLocalSynchLock(pthrCurrent);
|
||||
|
||||
if (abandonNamedMutexes)
|
||||
{
|
||||
SharedMemoryManager::ReleaseCreationDeletionProcessLock();
|
||||
}
|
||||
|
||||
DiscardAllPendingAPCs(pthrCurrent, pthrTarget);
|
||||
|
||||
return palErr;
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CPalSynchronizationManager::GetSynchWaitControllersForObjects
|
||||
|
@ -2989,8 +2865,7 @@ namespace CorUnix
|
|||
CThreadSynchronizationInfo::CThreadSynchronizationInfo() :
|
||||
m_tsThreadState(TS_IDLE),
|
||||
m_shridWaitAwakened(NULL),
|
||||
m_lLocalSynchLockCount(0),
|
||||
m_ownedNamedMutexListHead(nullptr)
|
||||
m_lLocalSynchLockCount(0)
|
||||
{
|
||||
InitializeListHead(&m_leOwnedObjsList);
|
||||
|
||||
|
@ -3190,133 +3065,6 @@ namespace CorUnix
|
|||
return palErr;
|
||||
}
|
||||
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CThreadSynchronizationInfo::AddObjectToOwnedList
|
||||
|
||||
Adds an object to the list of currently owned objects.
|
||||
--*/
|
||||
void CThreadSynchronizationInfo::AddObjectToOwnedList(POwnedObjectsListNode pooln)
|
||||
{
|
||||
InsertTailList(&m_leOwnedObjsList, &pooln->Link);
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CThreadSynchronizationInfo::RemoveObjectFromOwnedList
|
||||
|
||||
Removes an object from the list of currently owned objects.
|
||||
--*/
|
||||
void CThreadSynchronizationInfo::RemoveObjectFromOwnedList(POwnedObjectsListNode pooln)
|
||||
{
|
||||
RemoveEntryList(&pooln->Link);
|
||||
}
|
||||
|
||||
/*++
|
||||
Method:
|
||||
CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList
|
||||
|
||||
Removes the first object from the list of currently owned objects.
|
||||
--*/
|
||||
POwnedObjectsListNode CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList()
|
||||
{
|
||||
OwnedObjectsListNode * poolnItem;
|
||||
|
||||
if (IsListEmpty(&m_leOwnedObjsList))
|
||||
{
|
||||
poolnItem = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
PLIST_ENTRY pLink = RemoveHeadList(&m_leOwnedObjsList);
|
||||
poolnItem = CONTAINING_RECORD(pLink, OwnedObjectsListNode, Link);
|
||||
}
|
||||
|
||||
return poolnItem;
|
||||
}
|
||||
|
||||
void CThreadSynchronizationInfo::AddOwnedNamedMutex(NamedMutexProcessData *processData)
|
||||
{
|
||||
_ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo);
|
||||
_ASSERTE(processData != nullptr);
|
||||
_ASSERTE(processData->IsLockOwnedByCurrentThread());
|
||||
_ASSERTE(processData->GetNextInThreadOwnedNamedMutexList() == nullptr);
|
||||
|
||||
processData->SetNextInThreadOwnedNamedMutexList(m_ownedNamedMutexListHead);
|
||||
m_ownedNamedMutexListHead = processData;
|
||||
}
|
||||
|
||||
void CThreadSynchronizationInfo::RemoveOwnedNamedMutex(NamedMutexProcessData *processData)
|
||||
{
|
||||
_ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo);
|
||||
_ASSERTE(processData != nullptr);
|
||||
_ASSERTE(processData->IsLockOwnedByCurrentThread());
|
||||
|
||||
if (m_ownedNamedMutexListHead == processData)
|
||||
{
|
||||
m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList();
|
||||
processData->SetNextInThreadOwnedNamedMutexList(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found = false;
|
||||
for (NamedMutexProcessData
|
||||
*previous = m_ownedNamedMutexListHead,
|
||||
*current = previous->GetNextInThreadOwnedNamedMutexList();
|
||||
current != nullptr;
|
||||
previous = current, current = current->GetNextInThreadOwnedNamedMutexList())
|
||||
{
|
||||
if (current == processData)
|
||||
{
|
||||
found = true;
|
||||
previous->SetNextInThreadOwnedNamedMutexList(current->GetNextInThreadOwnedNamedMutexList());
|
||||
current->SetNextInThreadOwnedNamedMutexList(nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ASSERTE(found);
|
||||
}
|
||||
}
|
||||
|
||||
NamedMutexProcessData *CThreadSynchronizationInfo::RemoveFirstOwnedNamedMutex()
|
||||
{
|
||||
_ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo);
|
||||
|
||||
NamedMutexProcessData *processData = m_ownedNamedMutexListHead;
|
||||
if (processData != nullptr)
|
||||
{
|
||||
_ASSERTE(processData->IsLockOwnedByCurrentThread());
|
||||
m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList();
|
||||
processData->SetNextInThreadOwnedNamedMutexList(nullptr);
|
||||
}
|
||||
return processData;
|
||||
}
|
||||
|
||||
bool CThreadSynchronizationInfo::OwnsNamedMutex(NamedMutexProcessData *processData)
|
||||
{
|
||||
_ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo);
|
||||
|
||||
for (NamedMutexProcessData *current = m_ownedNamedMutexListHead;
|
||||
current != nullptr;
|
||||
current = current->GetNextInThreadOwnedNamedMutexList())
|
||||
{
|
||||
_ASSERTE(current->IsLockOwnedByCurrentThread());
|
||||
if (current == processData)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CThreadSynchronizationInfo::OwnsAnyNamedMutex() const
|
||||
{
|
||||
_ASSERTE(this == &GetCurrentPalThread()->synchronizationInfo);
|
||||
return m_ownedNamedMutexListHead != nullptr;
|
||||
}
|
||||
|
||||
#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
|
||||
|
||||
/*++
|
||||
|
|
|
@ -116,12 +116,6 @@ namespace CorUnix
|
|||
CPalThread * pthrTarget;
|
||||
} DeferredSignalingListNode;
|
||||
|
||||
typedef struct _OwnedObjectsListNode
|
||||
{
|
||||
LIST_ENTRY Link;
|
||||
CSynchData * pPalObjSynchData;
|
||||
} OwnedObjectsListNode;
|
||||
|
||||
typedef struct _ThreadApcInfoNode
|
||||
{
|
||||
struct _ThreadApcInfoNode * pNext;
|
||||
|
@ -147,15 +141,6 @@ namespace CorUnix
|
|||
LONG m_lRefCount;
|
||||
LONG m_lSignalCount;
|
||||
|
||||
// Ownership data
|
||||
LONG m_lOwnershipCount;
|
||||
DWORD m_dwOwnerPid;
|
||||
DWORD m_dwOwnerTid; // used only by remote processes
|
||||
// (thread ids may be recycled)
|
||||
CPalThread * m_pOwnerThread; // valid only on the target process
|
||||
OwnedObjectsListNode * m_poolnOwnedObjectListNode;
|
||||
bool m_fAbandoned;
|
||||
|
||||
#ifdef SYNCH_STATISTICS
|
||||
ULONG m_lStatWaitCount;
|
||||
ULONG m_lStatContentionCount;
|
||||
|
@ -164,9 +149,7 @@ namespace CorUnix
|
|||
public:
|
||||
CSynchData()
|
||||
: m_ulcWaitingThreads(0), m_lRefCount(1),
|
||||
m_lSignalCount(0), m_lOwnershipCount(0), m_dwOwnerPid(0),
|
||||
m_dwOwnerTid(0), m_pOwnerThread(NULL),
|
||||
m_poolnOwnedObjectListNode(NULL), m_fAbandoned(false)
|
||||
m_lSignalCount(0)
|
||||
{
|
||||
// m_ptrWTLHead, m_ptrWTLTail
|
||||
// and m_otiObjectTypeId are initialized by
|
||||
|
@ -190,8 +173,7 @@ namespace CorUnix
|
|||
LONG Release(CPalThread * pthrCurrent);
|
||||
|
||||
bool CanWaiterWaitWithoutBlocking(
|
||||
CPalThread * pWaiterThread,
|
||||
bool * pfAbandoned);
|
||||
CPalThread * pWaiterThread);
|
||||
|
||||
PAL_ERROR ReleaseWaiterWithoutBlocking(
|
||||
CPalThread * pthrCurrent,
|
||||
|
@ -248,48 +230,6 @@ namespace CorUnix
|
|||
return --m_lSignalCount;
|
||||
}
|
||||
|
||||
// Object ownership accessor methods
|
||||
void SetOwner(CPalThread * pOwnerThread);
|
||||
void ResetOwnership(void);
|
||||
PAL_ERROR AssignOwnershipToThread(
|
||||
CPalThread * pthrCurrent,
|
||||
CPalThread * pthrTarget);
|
||||
DWORD GetOwnerProcessID(void)
|
||||
{
|
||||
return m_dwOwnerPid;
|
||||
}
|
||||
DWORD GetOwnerThreadID(void)
|
||||
{
|
||||
return m_dwOwnerTid;
|
||||
}
|
||||
CPalThread * GetOwnerThread(void)
|
||||
{
|
||||
return m_pOwnerThread;
|
||||
}
|
||||
OwnedObjectsListNode * GetOwnershipListNode(void)
|
||||
{
|
||||
return m_poolnOwnedObjectListNode;
|
||||
}
|
||||
void SetOwnershipListNode(OwnedObjectsListNode * pooln)
|
||||
{
|
||||
m_poolnOwnedObjectListNode = pooln;
|
||||
}
|
||||
|
||||
// Object ownership count accessor methods
|
||||
LONG GetOwnershipCount(void)
|
||||
{
|
||||
return m_lOwnershipCount;
|
||||
}
|
||||
void SetOwnershipCount(LONG lOwnershipCount)
|
||||
{
|
||||
m_lOwnershipCount = lOwnershipCount;
|
||||
}
|
||||
|
||||
// Object abandoned flag accessor methods
|
||||
void SetAbandoned(bool fAbandoned)
|
||||
{ m_fAbandoned = fAbandoned; }
|
||||
bool IsAbandoned(void) { return m_fAbandoned; }
|
||||
|
||||
void IncrementWaitingThreadCount(void)
|
||||
{
|
||||
m_ulcWaitingThreads += 1;
|
||||
|
@ -419,8 +359,7 @@ namespace CorUnix
|
|||
// ISynchWaitController methods
|
||||
//
|
||||
virtual PAL_ERROR CanThreadWaitWithoutBlocking(
|
||||
bool * pfCanWaitWithoutBlocking,
|
||||
bool * pfAbandoned);
|
||||
bool * pfCanWaitWithoutBlocking);
|
||||
|
||||
virtual PAL_ERROR ReleaseWaitingThreadWithoutBlocking(void);
|
||||
|
||||
|
@ -452,8 +391,6 @@ namespace CorUnix
|
|||
virtual PAL_ERROR SetSignalCount(LONG lNewCount);
|
||||
virtual PAL_ERROR IncrementSignalCount(LONG lAmountToIncrement);
|
||||
virtual PAL_ERROR DecrementSignalCount(LONG lAmountToDecrement);
|
||||
virtual PAL_ERROR SetOwner(CPalThread *pNewOwningThread);
|
||||
virtual PAL_ERROR DecrementOwnershipCount(void);
|
||||
virtual void ReleaseController(void);
|
||||
};
|
||||
|
||||
|
@ -470,7 +407,6 @@ namespace CorUnix
|
|||
typedef CSynchCache<WaitingThreadsListNode> CWaitingThreadsListNodeCache;
|
||||
typedef CSHRSynchCache<WaitingThreadsListNode> CSHRWaitingThreadsListNodeCache;
|
||||
typedef CSynchCache<ThreadApcInfoNode> CThreadApcInfoNodeCache;
|
||||
typedef CSynchCache<OwnedObjectsListNode> COwnedObjectsListNodeCache;
|
||||
|
||||
private:
|
||||
// types
|
||||
|
@ -510,7 +446,6 @@ namespace CorUnix
|
|||
static const int SynchDataCacheMaxSize = 256;
|
||||
static const int WTListNodeCacheMaxSize = 256;
|
||||
static const int ApcInfoNodeCacheMaxSize = 32;
|
||||
static const int OwnedObjectsListCacheMaxSize = 16;
|
||||
static const int MaxWorkerConsecutiveEintrs = 128;
|
||||
static const int MaxConsecutiveEagains = 128;
|
||||
static const int WorkerThreadProcMonitoringTimeout = 250; // ms
|
||||
|
@ -548,7 +483,6 @@ namespace CorUnix
|
|||
CWaitingThreadsListNodeCache m_cacheWTListNodes;
|
||||
CSHRWaitingThreadsListNodeCache m_cacheSHRWTListNodes;
|
||||
CThreadApcInfoNodeCache m_cacheThreadApcInfoNodes;
|
||||
COwnedObjectsListNodeCache m_cacheOwnedObjectsListNodes;
|
||||
|
||||
// static methods
|
||||
static PAL_ERROR Initialize();
|
||||
|
@ -715,19 +649,6 @@ namespace CorUnix
|
|||
m_cacheThreadApcInfoNodes.Add(pthrCurrent, pNode);
|
||||
}
|
||||
|
||||
OwnedObjectsListNode * CacheGetOwnedObjsListNode(
|
||||
CPalThread * pthrCurrent)
|
||||
{
|
||||
return m_cacheOwnedObjectsListNodes.Get(pthrCurrent);
|
||||
}
|
||||
void CacheAddOwnedObjsListNode(
|
||||
CPalThread * pthrCurrent,
|
||||
OwnedObjectsListNode * pNode)
|
||||
{
|
||||
m_cacheOwnedObjectsListNodes.Add(pthrCurrent, pNode);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// IPalSynchronizationManager methods
|
||||
//
|
||||
|
@ -739,10 +660,6 @@ namespace CorUnix
|
|||
ThreadWakeupReason *ptwrWakeupReason,
|
||||
DWORD *pdwSignaledObject);
|
||||
|
||||
virtual PAL_ERROR AbandonObjectsOwnedByThread(
|
||||
CPalThread *pthrCurrent,
|
||||
CPalThread *pthrTarget);
|
||||
|
||||
virtual PAL_ERROR GetSynchWaitControllersForObjects(
|
||||
CPalThread *pthrCurrent,
|
||||
IPalObject *rgObjects[],
|
||||
|
|
|
@ -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 <new>
|
||||
|
@ -39,8 +38,6 @@ static PalObjectTypeId sg_rgWaitObjectsIds[] =
|
|||
{
|
||||
otiAutoResetEvent,
|
||||
otiManualResetEvent,
|
||||
otiMutex,
|
||||
otiNamedMutex,
|
||||
otiSemaphore,
|
||||
otiProcess,
|
||||
otiThread
|
||||
|
@ -52,8 +49,6 @@ static PalObjectTypeId sg_rgSignalableObjectIds[] =
|
|||
{
|
||||
otiAutoResetEvent,
|
||||
otiManualResetEvent,
|
||||
otiMutex,
|
||||
otiNamedMutex,
|
||||
otiSemaphore
|
||||
};
|
||||
static CAllowedObjectTypes sg_aotSignalableObject(sg_rgSignalableObjectIds, ARRAY_SIZE(sg_rgSignalableObjectIds));
|
||||
|
@ -85,36 +80,6 @@ WaitForSingleObject(IN HANDLE hHandle,
|
|||
return dwRet;
|
||||
}
|
||||
|
||||
|
||||
/*++
|
||||
Function:
|
||||
WaitForSingleObjectPrioritized
|
||||
|
||||
Similar to WaitForSingleObject, except uses a LIFO release policy for waiting threads by prioritizing new waiters (registering
|
||||
them at the beginning of the wait queue rather than at the end).
|
||||
--*/
|
||||
DWORD
|
||||
PALAPI
|
||||
PAL_WaitForSingleObjectPrioritized(IN HANDLE hHandle,
|
||||
IN DWORD dwMilliseconds)
|
||||
{
|
||||
DWORD dwRet;
|
||||
|
||||
PERF_ENTRY(PAL_WaitForSingleObjectPrioritized);
|
||||
ENTRY("PAL_WaitForSingleObjectPrioritized(hHandle=%p, dwMilliseconds=%u)\n",
|
||||
hHandle, dwMilliseconds);
|
||||
|
||||
CPalThread * pThread = InternalGetCurrentThread();
|
||||
|
||||
dwRet = InternalWaitForMultipleObjectsEx(pThread, 1, &hHandle, FALSE,
|
||||
dwMilliseconds, FALSE, TRUE /* bPrioritize */);
|
||||
|
||||
LOGEXIT("PAL_WaitForSingleObjectPrioritized returns DWORD %u\n", dwRet);
|
||||
PERF_EXIT(PAL_WaitForSingleObjectPrioritized);
|
||||
return dwRet;
|
||||
}
|
||||
|
||||
|
||||
/*++
|
||||
Function:
|
||||
WaitForSingleObjectEx
|
||||
|
@ -363,7 +328,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx(
|
|||
PAL_ERROR palErr = NO_ERROR;
|
||||
int i, iSignaledObjCount, iSignaledObjIndex = -1;
|
||||
bool fWAll = (bool)bWaitAll, fNeedToBlock = false;
|
||||
bool fAbandoned = false;
|
||||
WaitType wtWaitType;
|
||||
|
||||
IPalObject * pIPalObjStackArray[MAXIMUM_STACK_WAITOBJ_ARRAY_SIZE] = { NULL };
|
||||
|
@ -417,55 +381,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<NamedMutexProcessData *>(processDataHeader->GetData())->TryAcquireLock(nullptr, dwMilliseconds);
|
||||
switch (tryAcquireLockResult)
|
||||
{
|
||||
case MutexTryAcquireLockResult::AcquiredLock:
|
||||
dwRet = WAIT_OBJECT_0;
|
||||
break;
|
||||
|
||||
case MutexTryAcquireLockResult::AcquiredLockButMutexWasAbandoned:
|
||||
dwRet = WAIT_ABANDONED_0;
|
||||
break;
|
||||
|
||||
case MutexTryAcquireLockResult::TimedOut:
|
||||
dwRet = WAIT_TIMEOUT;
|
||||
break;
|
||||
|
||||
default:
|
||||
_ASSERTE(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (SharedMemoryException ex)
|
||||
{
|
||||
pThread->SetLastError(ex.GetErrorCode());
|
||||
}
|
||||
goto WFMOExIntCleanup;
|
||||
}
|
||||
|
||||
if (fWAll)
|
||||
{
|
||||
// For a wait-all operation, check for duplicate wait objects in the array. This just uses a brute-force O(n^2)
|
||||
|
@ -528,8 +443,8 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx(
|
|||
iSignaledObjIndex = -1;
|
||||
for (i=0;i<(int)nCount;i++)
|
||||
{
|
||||
bool fValue, fWaitObjectAbandoned = false;
|
||||
palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue, &fWaitObjectAbandoned);
|
||||
bool fValue;
|
||||
palErr = ppISyncWaitCtrlrs[i]->CanThreadWaitWithoutBlocking(&fValue);
|
||||
if (NO_ERROR != palErr)
|
||||
{
|
||||
ERROR("ISynchWaitController::CanThreadWaitWithoutBlocking() failed for "
|
||||
|
@ -537,10 +452,6 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx(
|
|||
pThread->SetLastError(ERROR_INTERNAL_ERROR);
|
||||
goto WFMOExIntReleaseControllers;
|
||||
}
|
||||
if (fWaitObjectAbandoned)
|
||||
{
|
||||
fAbandoned = true;
|
||||
}
|
||||
if (fValue)
|
||||
{
|
||||
iSignaledObjCount++;
|
||||
|
@ -589,7 +500,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx(
|
|||
}
|
||||
}
|
||||
|
||||
dwRet = (fAbandoned ? WAIT_ABANDONED_0 : WAIT_OBJECT_0);
|
||||
dwRet = WAIT_OBJECT_0;
|
||||
}
|
||||
else if (0 == dwMilliseconds)
|
||||
{
|
||||
|
@ -655,9 +566,6 @@ WFMOExIntReleaseControllers:
|
|||
case WaitSucceeded:
|
||||
dwRet = WAIT_OBJECT_0; // offset added later
|
||||
break;
|
||||
case MutexAbandoned:
|
||||
dwRet = WAIT_ABANDONED_0; // offset added later
|
||||
break;
|
||||
case WaitTimeout:
|
||||
dwRet = WAIT_TIMEOUT;
|
||||
break;
|
||||
|
@ -679,14 +587,14 @@ WFMOExIntReleaseControllers:
|
|||
}
|
||||
}
|
||||
|
||||
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<DWORD>(iSignaledObjIndex),
|
||||
"SignaledObjIndex object out of range "
|
||||
"[index=%d obj_count=%u\n",
|
||||
iSignaledObjCount, nCount);
|
||||
iSignaledObjIndex, nCount);
|
||||
|
||||
if (iSignaledObjIndex < 0)
|
||||
{
|
||||
|
@ -760,11 +668,6 @@ DWORD CorUnix::InternalSignalObjectAndWait(
|
|||
palError = InternalSetEvent(thread, hObjectToSignal, true /* fSetEvent */);
|
||||
break;
|
||||
|
||||
case otiMutex:
|
||||
case otiNamedMutex:
|
||||
palError = InternalReleaseMutex(thread, hObjectToSignal);
|
||||
break;
|
||||
|
||||
case otiSemaphore:
|
||||
palError = InternalReleaseSemaphore(thread, hObjectToSignal, 1 /* lReleaseCount */, nullptr /* lpPreviousCount */);
|
||||
break;
|
||||
|
@ -873,9 +776,6 @@ DWORD CorUnix::InternalSleepEx (
|
|||
palErr = g_pSynchronizationManager->DispatchPendingAPCs(pThread);
|
||||
_ASSERT_MSG(NO_ERROR == palErr, "Awakened for APC, but no APC is pending\n");
|
||||
|
||||
break;
|
||||
case MutexAbandoned:
|
||||
ASSERT("Thread %p awakened with reason=MutexAbandoned from a SleepEx\n", pThread);
|
||||
break;
|
||||
case WaitFailed:
|
||||
default:
|
||||
|
|
|
@ -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};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
|
|
|
@ -34,6 +34,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d
|
|||
|
||||
#include <generatedumpflags.h>
|
||||
#include <clrconfignocache.h>
|
||||
#include <static_assert.h>
|
||||
|
||||
#include <errno.h>
|
||||
#if HAVE_POLL
|
||||
|
@ -130,8 +131,7 @@ CObjectType CorUnix::otProcess(
|
|||
NULL, // No process local data cleanup routine
|
||||
CObjectType::WaitableObject,
|
||||
CObjectType::SingleTransitionObject,
|
||||
CObjectType::ThreadReleaseHasNoSideEffects,
|
||||
CObjectType::NoOwner
|
||||
CObjectType::ThreadReleaseHasNoSideEffects
|
||||
);
|
||||
|
||||
//
|
||||
|
@ -189,7 +189,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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,9 +49,57 @@ 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:
|
||||
InternalSuspendNewThreadFromData
|
||||
|
|
|
@ -40,16 +40,10 @@ add_executable_clr(paltests
|
|||
#composite/object_management/event/nonshared/main.cpp
|
||||
#composite/object_management/event/shared/event.cpp
|
||||
#composite/object_management/event/shared/main.cpp
|
||||
#composite/object_management/mutex/nonshared/main.cpp
|
||||
#composite/object_management/mutex/nonshared/mutex.cpp
|
||||
#composite/object_management/mutex/shared/main.cpp
|
||||
#composite/object_management/mutex/shared/mutex.cpp
|
||||
#composite/object_management/semaphore/nonshared/main.cpp
|
||||
#composite/object_management/semaphore/nonshared/semaphore.cpp
|
||||
#composite/object_management/semaphore/shared/main.cpp
|
||||
#composite/object_management/semaphore/shared/semaphore.cpp
|
||||
#composite/wfmo/main.cpp
|
||||
#composite/wfmo/mutex.cpp
|
||||
c_runtime/atof/test1/test1.cpp
|
||||
c_runtime/atoi/test1/test1.cpp
|
||||
c_runtime/isalnum/test1/test1.cpp
|
||||
|
@ -376,11 +370,6 @@ add_executable_clr(paltests
|
|||
# pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test2_neg/reg_unreg_libraryw_neg.cpp
|
||||
samples/test1/test.cpp
|
||||
samples/test2/test.cpp
|
||||
threading/CreateEventW/test1/test1.cpp
|
||||
threading/CreateEventW/test2/test2.cpp
|
||||
threading/CreateEventW/test3/test3.cpp
|
||||
threading/CreateMutexW_ReleaseMutex/test1/CreateMutexW.cpp
|
||||
threading/CreateMutexW_ReleaseMutex/test2/CreateMutexW.cpp
|
||||
threading/CreateProcessW/test1/childProcess.cpp
|
||||
threading/CreateProcessW/test1/parentProcess.cpp
|
||||
threading/CreateProcessW/test2/childprocess.cpp
|
||||
|
@ -393,12 +382,9 @@ add_executable_clr(paltests
|
|||
threading/CreateThread/test3/test3.cpp
|
||||
threading/DuplicateHandle/test1/test1.cpp
|
||||
threading/DuplicateHandle/test10/test10.cpp
|
||||
threading/DuplicateHandle/test11/childprocess.cpp
|
||||
threading/DuplicateHandle/test11/test11.cpp
|
||||
threading/DuplicateHandle/test12/test12.cpp
|
||||
threading/DuplicateHandle/test2/test2.cpp
|
||||
threading/DuplicateHandle/test3/test3.cpp
|
||||
threading/DuplicateHandle/test4/test4.cpp
|
||||
threading/DuplicateHandle/test7/test7.cpp
|
||||
threading/DuplicateHandle/test8/test8.cpp
|
||||
# threading/DuplicateHandle/test9/test9.cpp
|
||||
|
@ -415,13 +401,10 @@ add_executable_clr(paltests
|
|||
threading/GetCurrentThreadId/test1/threadId.cpp
|
||||
threading/GetExitCodeProcess/test1/childProcess.cpp
|
||||
threading/GetExitCodeProcess/test1/test1.cpp
|
||||
threading/NamedMutex/test1/namedmutex.cpp
|
||||
threading/NamedMutex/test1/nopal.cpp
|
||||
threading/OpenEventW/test1/test1.cpp
|
||||
threading/OpenEventW/test2/test2.cpp
|
||||
threading/OpenEventW/test3/childprocess.cpp
|
||||
threading/OpenEventW/test3/test3.cpp
|
||||
threading/OpenEventW/test4/test4.cpp
|
||||
threading/OpenEventW/test5/test5.cpp
|
||||
threading/OpenProcess/test1/childProcess.cpp
|
||||
threading/OpenProcess/test1/test1.cpp
|
||||
|
@ -433,7 +416,6 @@ add_executable_clr(paltests
|
|||
threading/QueueUserAPC/test5/test5.cpp
|
||||
threading/QueueUserAPC/test6/test6.cpp
|
||||
threading/QueueUserAPC/test7/test7.cpp
|
||||
threading/ReleaseMutex/test3/ReleaseMutex.cpp
|
||||
threading/releasesemaphore/test1/test.cpp
|
||||
threading/ResetEvent/test1/test1.cpp
|
||||
threading/ResetEvent/test2/test2.cpp
|
||||
|
@ -455,17 +437,11 @@ add_executable_clr(paltests
|
|||
threading/WaitForMultipleObjects/test1/test1.cpp
|
||||
threading/WaitForMultipleObjectsEx/test1/test1.cpp
|
||||
threading/WaitForMultipleObjectsEx/test2/test2.cpp
|
||||
threading/WaitForMultipleObjectsEx/test3/test3.cpp
|
||||
threading/WaitForMultipleObjectsEx/test4/test4.cpp
|
||||
threading/WaitForMultipleObjectsEx/test5/helper.cpp
|
||||
threading/WaitForMultipleObjectsEx/test5/test5.cpp
|
||||
threading/WaitForMultipleObjectsEx/test6/child6.cpp
|
||||
threading/WaitForMultipleObjectsEx/test6/test6.cpp
|
||||
threading/WaitForSingleObject/test1/test1.cpp
|
||||
threading/WaitForSingleObject/WFSOExMutexTest/WFSOExMutexTest.cpp
|
||||
threading/WaitForSingleObject/WFSOExSemaphoreTest/WFSOExSemaphoreTest.cpp
|
||||
threading/WaitForSingleObject/WFSOExThreadTest/WFSOExThreadTest.cpp
|
||||
threading/WaitForSingleObject/WFSOMutexTest/WFSOMutexTest.cpp
|
||||
threading/WaitForSingleObject/WFSOProcessTest/ChildProcess.cpp
|
||||
threading/WaitForSingleObject/WFSOProcessTest/WFSOProcessTest.cpp
|
||||
threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.cpp
|
||||
|
|
|
@ -278,11 +278,6 @@ pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test1/paltest_pal_registerl
|
|||
pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test2_neg/paltest_reg_unreg_libraryw_neg
|
||||
samples/test1/paltest_samples_test1
|
||||
samples/test2/paltest_samples_test2
|
||||
threading/CreateEventW/test1/paltest_createeventw_test1
|
||||
threading/CreateEventW/test2/paltest_createeventw_test2
|
||||
threading/CreateEventW/test3/paltest_createeventw_test3
|
||||
threading/CreateMutexW_ReleaseMutex/test1/paltest_createmutexw_releasemutex_test1
|
||||
threading/CreateMutexW_ReleaseMutex/test2/paltest_createmutexw_releasemutex_test2
|
||||
threading/CreateProcessW/test1/paltest_createprocessw_test1
|
||||
threading/CreateProcessW/test2/paltest_createprocessw_test2
|
||||
threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1
|
||||
|
@ -293,11 +288,9 @@ threading/CreateThread/test2/paltest_createthread_test2
|
|||
threading/CreateThread/test3/paltest_createthread_test3
|
||||
threading/DuplicateHandle/test1/paltest_duplicatehandle_test1
|
||||
threading/DuplicateHandle/test10/paltest_duplicatehandle_test10
|
||||
threading/DuplicateHandle/test11/paltest_duplicatehandle_test11
|
||||
threading/DuplicateHandle/test12/paltest_duplicatehandle_test12
|
||||
threading/DuplicateHandle/test2/paltest_duplicatehandle_test2
|
||||
threading/DuplicateHandle/test3/paltest_duplicatehandle_test3
|
||||
threading/DuplicateHandle/test4/paltest_duplicatehandle_test4
|
||||
threading/DuplicateHandle/test7/paltest_duplicatehandle_test7
|
||||
threading/DuplicateHandle/test8/paltest_duplicatehandle_test8
|
||||
threading/DuplicateHandle/test9/paltest_duplicatehandle_test9
|
||||
|
@ -312,11 +305,9 @@ threading/GetCurrentThread/test1/paltest_getcurrentthread_test1
|
|||
threading/GetCurrentThread/test2/paltest_getcurrentthread_test2
|
||||
threading/GetCurrentThreadId/test1/paltest_getcurrentthreadid_test1
|
||||
threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1
|
||||
threading/NamedMutex/test1/paltest_namedmutex_test1
|
||||
threading/OpenEventW/test1/paltest_openeventw_test1
|
||||
threading/OpenEventW/test2/paltest_openeventw_test2
|
||||
threading/OpenEventW/test3/paltest_openeventw_test3
|
||||
threading/OpenEventW/test4/paltest_openeventw_test4
|
||||
threading/OpenEventW/test5/paltest_openeventw_test5
|
||||
threading/OpenProcess/test1/paltest_openprocess_test1
|
||||
threading/QueryThreadCycleTime/test1/paltest_querythreadcycletime_test1
|
||||
|
@ -327,7 +318,6 @@ threading/QueueUserAPC/test4/paltest_queueuserapc_test4
|
|||
threading/QueueUserAPC/test5/paltest_queueuserapc_test5
|
||||
threading/QueueUserAPC/test6/paltest_queueuserapc_test6
|
||||
threading/QueueUserAPC/test7/paltest_queueuserapc_test7
|
||||
threading/ReleaseMutex/test3/paltest_releasemutex_test3
|
||||
threading/releasesemaphore/test1/paltest_releasesemaphore_test1
|
||||
threading/ResetEvent/test1/paltest_resetevent_test1
|
||||
threading/ResetEvent/test2/paltest_resetevent_test2
|
||||
|
@ -350,11 +340,9 @@ threading/WaitForMultipleObjects/test1/paltest_waitformultipleobjects_test1
|
|||
threading/WaitForMultipleObjectsEx/test1/paltest_waitformultipleobjectsex_test1
|
||||
threading/WaitForMultipleObjectsEx/test2/paltest_waitformultipleobjectsex_test2
|
||||
threading/WaitForMultipleObjectsEx/test3/paltest_waitformultipleobjectsex_test3
|
||||
threading/WaitForMultipleObjectsEx/test4/paltest_waitformultipleobjectsex_test4
|
||||
threading/WaitForMultipleObjectsEx/test5/paltest_waitformultipleobjectsex_test5
|
||||
threading/WaitForMultipleObjectsEx/test6/paltest_waitformultipleobjectsex_test6
|
||||
threading/WaitForSingleObject/test1/paltest_waitforsingleobject_test1
|
||||
threading/WaitForSingleObject/WFSOExMutexTest/paltest_waitforsingleobject_wfsoexmutextest
|
||||
threading/WaitForSingleObject/WFSOExSemaphoreTest/paltest_waitforsingleobject_wfsoexsemaphoretest
|
||||
threading/WaitForSingleObject/WFSOExThreadTest/paltest_waitforsingleobject_wfsoexthreadtest
|
||||
threading/WaitForSingleObject/WFSOMutexTest/paltest_waitforsingleobject_wfsomutextest
|
||||
|
|
|
@ -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 <palsuite.h>
|
||||
#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;
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
#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);
|
||||
}
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
#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;
|
||||
}
|
||||
|
|
@ -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 <palsuite.h>
|
||||
#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);
|
||||
}
|
||||
}
|
|
@ -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,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
|
||||
PAL,Composite,palsuite\composite\object_management\mutex\shared,mutex=main.c mutex.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
|
||||
PAL,Composite,palsuite\composite\object_management\semaphore\nonshared,semaphore=main.c semaphore.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
|
||||
PAL,Composite,palsuite\composite\object_management\semaphore\shared,semaphore=main.c semaphore.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
|
||||
PAL,Composite,palsuite\composite\object_management\event\nonshared,event=main.c event.c,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
|
||||
|
@ -19,10 +17,10 @@ main [PROCESS_COUNT] [THREAD_COUNT] [REPEAT_COUNT]
|
|||
|
||||
|
||||
Output:
|
||||
The performance numbers will be in <process_logical_id>_[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 <process_logical_id>_[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<6F>
|
||||
|
||||
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
|
||||
|
|
|
@ -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 <palsuite.h>
|
||||
#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;
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
#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);
|
||||
}
|
||||
}
|
|
@ -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,<SUPPORTEXE>,<TESTLANGCPP>,<COMPILEONLY>
|
||||
|
||||
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 <process_logical_id>_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)).
|
||||
|
||||
|
|
@ -248,9 +248,6 @@ miscellaneous/SetLastError/test1/paltest_setlasterror_test1
|
|||
pal_specific/PAL_Initialize_Terminate/test1/paltest_pal_initialize_terminate_test1
|
||||
pal_specific/PAL_Initialize_Terminate/test2/paltest_pal_initialize_terminate_test2
|
||||
samples/test1/paltest_samples_test1
|
||||
threading/CreateEventW/test1/paltest_createeventw_test1
|
||||
threading/CreateEventW/test2/paltest_createeventw_test2
|
||||
threading/CreateMutexW_ReleaseMutex/test1/paltest_createmutexw_releasemutex_test1
|
||||
threading/CreateProcessW/test1/paltest_createprocessw_test1
|
||||
threading/CreateProcessW/test2/paltest_createprocessw_test2
|
||||
threading/CreateSemaphoreW_ReleaseSemaphore/test1/paltest_createsemaphorew_releasesemaphore_test1
|
||||
|
@ -259,7 +256,6 @@ threading/CreateThread/test1/paltest_createthread_test1
|
|||
threading/CreateThread/test3/paltest_createthread_test3
|
||||
threading/DuplicateHandle/test10/paltest_duplicatehandle_test10
|
||||
threading/DuplicateHandle/test2/paltest_duplicatehandle_test2
|
||||
threading/DuplicateHandle/test4/paltest_duplicatehandle_test4
|
||||
threading/DuplicateHandle/test7/paltest_duplicatehandle_test7
|
||||
threading/DuplicateHandle/test8/paltest_duplicatehandle_test8
|
||||
threading/ExitProcess/test1/paltest_exitprocess_test1
|
||||
|
@ -269,7 +265,6 @@ threading/ExitThread/test1/paltest_exitthread_test1
|
|||
threading/GetCurrentProcessId/test1/paltest_getcurrentprocessid_test1
|
||||
threading/GetCurrentThread/test1/paltest_getcurrentthread_test1
|
||||
threading/GetCurrentThread/test2/paltest_getcurrentthread_test2
|
||||
threading/NamedMutex/test1/paltest_namedmutex_test1
|
||||
threading/QueryThreadCycleTime/test1/paltest_querythreadcycletime_test1
|
||||
threading/QueueUserAPC/test2/paltest_queueuserapc_test2
|
||||
threading/QueueUserAPC/test3/paltest_queueuserapc_test3
|
||||
|
@ -277,7 +272,6 @@ threading/QueueUserAPC/test4/paltest_queueuserapc_test4
|
|||
threading/QueueUserAPC/test5/paltest_queueuserapc_test5
|
||||
threading/QueueUserAPC/test6/paltest_queueuserapc_test6
|
||||
threading/QueueUserAPC/test7/paltest_queueuserapc_test7
|
||||
threading/ReleaseMutex/test3/paltest_releasemutex_test3
|
||||
threading/releasesemaphore/test1/paltest_releasesemaphore_test1
|
||||
threading/ResetEvent/test1/paltest_resetevent_test1
|
||||
threading/ResetEvent/test2/paltest_resetevent_test2
|
||||
|
@ -294,9 +288,7 @@ threading/WaitForMultipleObjects/test1/paltest_waitformultipleobjects_test1
|
|||
threading/WaitForMultipleObjectsEx/test1/paltest_waitformultipleobjectsex_test1
|
||||
threading/WaitForMultipleObjectsEx/test2/paltest_waitformultipleobjectsex_test2
|
||||
threading/WaitForMultipleObjectsEx/test3/paltest_waitformultipleobjectsex_test3
|
||||
threading/WaitForMultipleObjectsEx/test4/paltest_waitformultipleobjectsex_test4
|
||||
threading/WaitForSingleObject/test1/paltest_waitforsingleobject_test1
|
||||
threading/WaitForSingleObject/WFSOExMutexTest/paltest_waitforsingleobject_wfsoexmutextest
|
||||
threading/WaitForSingleObject/WFSOExSemaphoreTest/paltest_waitforsingleobject_wfsoexsemaphoretest
|
||||
threading/WaitForSingleObject/WFSOExThreadTest/paltest_waitforsingleobject_wfsoexthreadtest
|
||||
threading/WaitForSingleObject/WFSOMutexTest/paltest_waitforsingleobject_wfsomutextest
|
||||
|
|
|
@ -69,12 +69,9 @@ miscellaneous/IsBadWritePtr/test3/paltest_isbadwriteptr_test3
|
|||
pal_specific/PAL_get_stdout/test1/paltest_pal_get_stdout_test1
|
||||
pal_specific/PAL_RegisterLibraryW_UnregisterLibraryW/test1/paltest_pal_registerlibraryw_unregisterlibraryw_test1
|
||||
samples/test2/paltest_samples_test2
|
||||
threading/CreateEventW/test3/paltest_createeventw_test3
|
||||
threading/CreateMutexW_ReleaseMutex/test2/paltest_createmutexw_releasemutex_test2
|
||||
threading/CreateSemaphoreW_ReleaseSemaphore/test3/paltest_createsemaphorew_releasesemaphore_test3
|
||||
threading/CreateThread/test2/paltest_createthread_test2
|
||||
threading/DuplicateHandle/test1/paltest_duplicatehandle_test1
|
||||
threading/DuplicateHandle/test11/paltest_duplicatehandle_test11
|
||||
threading/DuplicateHandle/test12/paltest_duplicatehandle_test12
|
||||
threading/DuplicateHandle/test3/paltest_duplicatehandle_test3
|
||||
threading/DuplicateHandle/test9/paltest_duplicatehandle_test9
|
||||
|
@ -85,7 +82,6 @@ threading/GetExitCodeProcess/test1/paltest_getexitcodeprocess_test1
|
|||
threading/OpenEventW/test1/paltest_openeventw_test1
|
||||
threading/OpenEventW/test2/paltest_openeventw_test2
|
||||
threading/OpenEventW/test3/paltest_openeventw_test3
|
||||
threading/OpenEventW/test4/paltest_openeventw_test4
|
||||
threading/OpenEventW/test5/paltest_openeventw_test5
|
||||
threading/OpenProcess/test1/paltest_openprocess_test1
|
||||
threading/QueueUserAPC/test1/paltest_queueuserapc_test1
|
||||
|
|
|
@ -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 <palsuite.h>
|
||||
|
||||
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 );
|
||||
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
|
||||
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 );
|
||||
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
|
||||
#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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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 <palsuite.h>
|
||||
|
||||
/* 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<MAIN_BUF_SIZE && Buffer[i] == 0; i++);
|
||||
if (i == MAIN_BUF_SIZE)
|
||||
{
|
||||
/* Its empty so just return */
|
||||
if (ReleaseMutex(hMutex) == FALSE)
|
||||
{
|
||||
Fail("ERROR: ReleaseMutex Failed.\n");
|
||||
}
|
||||
return OP_NONE;
|
||||
}
|
||||
|
||||
/* Its not empty so we must empty it. */
|
||||
for (i=0; i<MAIN_BUF_SIZE; i++)
|
||||
{
|
||||
/* Check for empty slots if we find one then the */
|
||||
/* fill operation did no finish. return an error */
|
||||
if (Buffer[i] == 0)
|
||||
{
|
||||
if (ReleaseMutex(hMutex) == FALSE)
|
||||
{
|
||||
Fail("ERROR: ReleaseMutex Failed.\n");
|
||||
}
|
||||
return OP_ERR;
|
||||
}
|
||||
|
||||
Buffer[i] = 0;
|
||||
}
|
||||
|
||||
if (ReleaseMutex(hMutex) == FALSE)
|
||||
{
|
||||
Fail("ERROR: ReleaseMutex Failed.\n");
|
||||
}
|
||||
return OP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* FillBuffer implements the fill operation for test buffer.
|
||||
*/
|
||||
int
|
||||
FillBuffer()
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
Fail("ERROR: WaitForSingleObject failed.\n");
|
||||
}
|
||||
|
||||
/* Check to see if the buffer is already completely full */
|
||||
for (i=0; i<MAIN_BUF_SIZE && Buffer[i] != 0; i++);
|
||||
if (i == MAIN_BUF_SIZE)
|
||||
{
|
||||
/* Its full so just return */
|
||||
if (ReleaseMutex(hMutex) == FALSE)
|
||||
{
|
||||
Fail("ERROR: ReleaseMutex Failed.\n");
|
||||
}
|
||||
return OP_NONE;
|
||||
}
|
||||
|
||||
/* Its not full so we must fill it. */
|
||||
for (i=0; i<MAIN_BUF_SIZE; i++)
|
||||
{
|
||||
/* Check for filled slots if we find one then the */
|
||||
/* empty operation did not finish. return an error */
|
||||
if (Buffer[i] == 1)
|
||||
{
|
||||
if (ReleaseMutex(hMutex) == FALSE)
|
||||
{
|
||||
Fail("ERROR: ReleaseMutex Failed.\n");
|
||||
}
|
||||
return OP_ERR;
|
||||
}
|
||||
|
||||
Buffer[i] = 1;
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
if (ReleaseMutex(hMutex) == FALSE)
|
||||
{
|
||||
Fail("ERROR: ReleaseMutex Failed.\n");
|
||||
}
|
||||
return OP_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Producer thread function.
|
||||
*/
|
||||
DWORD PALAPI Producer(LPVOID lpParam)
|
||||
{
|
||||
int n = 0;
|
||||
int ret;
|
||||
|
||||
while (n < NUM_OF_CYCLES)
|
||||
{
|
||||
if (bConErr == TRUE)
|
||||
{
|
||||
/* The consumer ran into an error so we'll stop */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = FillBuffer();
|
||||
|
||||
if (ret == OP_OK)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
else if (ret == OP_ERR)
|
||||
{
|
||||
bProdErr = TRUE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Consumer thread function.
|
||||
*/
|
||||
DWORD PALAPI Consumer( LPVOID lpParam )
|
||||
{
|
||||
int n = 0;
|
||||
int ret;
|
||||
|
||||
while (n < NUM_OF_CYCLES)
|
||||
{
|
||||
if (bProdErr == TRUE)
|
||||
{
|
||||
/* The consumer ran into an error so we'll stop */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = EmptyBuffer();
|
||||
|
||||
if (ret == OP_OK)
|
||||
{
|
||||
n++;
|
||||
}
|
||||
else if (ret == OP_ERR)
|
||||
{
|
||||
bConErr = TRUE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PALTEST(threading_CreateMutexW_ReleaseMutex_test1_paltest_createmutexw_releasemutex_test1, "threading/CreateMutexW_ReleaseMutex/test1/paltest_createmutexw_releasemutex_test1")
|
||||
{
|
||||
DWORD dwThreadId;
|
||||
DWORD dwWaitRet;
|
||||
|
||||
HANDLE hThread1; /* handle to consumer thread */
|
||||
HANDLE hThread2; /* handle to producer thread */
|
||||
HANDLE handleArray[2];
|
||||
|
||||
|
||||
if(0 != (PAL_Initialize(argc, argv)))
|
||||
{
|
||||
return ( FAIL );
|
||||
}
|
||||
|
||||
/* Initialize our error flags */
|
||||
bProdErr = FALSE;
|
||||
bConErr = FALSE;
|
||||
|
||||
/*
|
||||
* Initialize the Buffer to be empty
|
||||
*/
|
||||
memset(Buffer, 0, MAIN_BUF_SIZE);
|
||||
|
||||
/*
|
||||
* Create Mutex
|
||||
*/
|
||||
hMutex = CreateMutexW (NULL, FALSE, NULL);
|
||||
|
||||
if (NULL == hMutex)
|
||||
{
|
||||
Fail("hMutex = CreateMutexW() - returned NULL\n"
|
||||
"Failing Test.\nGetLastError returned %u\n", GetLastError());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create the Producer thread
|
||||
*/
|
||||
hThread1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Producer,
|
||||
0, 0, &dwThreadId);
|
||||
|
||||
if ( NULL == hThread1 )
|
||||
{
|
||||
CloseHandle(hMutex);
|
||||
|
||||
Fail("CreateThread() returned NULL. Failing test.\n"
|
||||
"GetLastError returned %u\n", GetLastError());
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the Consumer thread
|
||||
*/
|
||||
hThread2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Consumer,
|
||||
0, 0, &dwThreadId);
|
||||
|
||||
if ( NULL == hThread2 )
|
||||
{
|
||||
CloseHandle(hMutex);
|
||||
|
||||
/* Set the error flag and give thread1 some time to exit */
|
||||
bConErr = FALSE;
|
||||
Sleep(250);
|
||||
|
||||
Fail("CreateThread() returned NULL. Failing test.\n"
|
||||
"GetLastError returned %u\n", GetLastError());
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for both threads to complete (Max 45 Seconds)
|
||||
*/
|
||||
handleArray[0] = hThread1;
|
||||
handleArray[1] = hThread2;
|
||||
dwWaitRet = WaitForMultipleObjects (2, handleArray, TRUE, 450000);
|
||||
if (dwWaitRet == WAIT_FAILED)
|
||||
{
|
||||
Fail("ERROR: WaitForMultipleObjects failed.\n");
|
||||
}
|
||||
else if (dwWaitRet == WAIT_TIMEOUT)
|
||||
{
|
||||
/* Set the error flags and give the threads some time to exit */
|
||||
bProdErr = FALSE;
|
||||
bConErr = FALSE;
|
||||
Sleep(250);
|
||||
|
||||
Fail("ERROR: Timeout interval exceeded.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up
|
||||
*/
|
||||
if (CloseHandle(hThread1) == FALSE ||
|
||||
CloseHandle(hThread2) == FALSE ||
|
||||
CloseHandle(hMutex) == FALSE)
|
||||
{
|
||||
Fail("ERROR: CloseHandle failed.\n");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check our error flags
|
||||
*/
|
||||
if (bProdErr == TRUE || bConErr == TRUE)
|
||||
{
|
||||
Fail("ERROR: A collision occurred, so the mutex failed.\n");
|
||||
}
|
||||
|
||||
PAL_Terminate();
|
||||
return ( PASS );
|
||||
|
||||
}
|
|
@ -1,339 +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/test2/CreateMutexW.c
|
||||
**
|
||||
** Purpose: This test case tests the following things
|
||||
** - Creation of named Mutexes
|
||||
** - Creating multiple handles to a single named Mutex
|
||||
** - Ensuring that these handles work interchangeably
|
||||
** - Setting bInitialOwnerFlag to TRUE will cause the
|
||||
** initial call to a Wait function on the same Mutex
|
||||
** to actually wait.
|
||||
** - Waiting on a Mutex that a thread already owns does
|
||||
** not block.
|
||||
** - Create Named mutex with empty string ("")
|
||||
** - Create Named mutex with string of MAX_LONGPATH length
|
||||
** - Calling ReleaseMutex with invalid Mutex handles and
|
||||
** valid but unowned Mutexes.
|
||||
**
|
||||
** Dependencies: CreateThread
|
||||
** ReleaseMutex
|
||||
** WaitForSingleObject
|
||||
** CloseHandle
|
||||
** Sleep
|
||||
** memset
|
||||
**
|
||||
|
||||
**
|
||||
**=========================================================*/
|
||||
|
||||
#define UNICODE
|
||||
#include <palsuite.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 <palsuite.h>
|
||||
#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;
|
||||
}
|
|
@ -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
|
|
@ -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 <palsuite.h>
|
||||
#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;
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
|
||||
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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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 <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <minipal/utils.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 @@ cleanup2:
|
|||
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 )
|
||||
{
|
||||
|
|
|
@ -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 <palsuite.h>
|
||||
|
||||
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 );
|
||||
|
||||
}
|
|
@ -12,10 +12,7 @@ enum class SignalableObjectType
|
|||
AutoResetEvent,
|
||||
Semaphore,
|
||||
FullSemaphore,
|
||||
Mutex,
|
||||
UnlockedMutex,
|
||||
|
||||
Last = UnlockedMutex
|
||||
Last = FullSemaphore,
|
||||
};
|
||||
|
||||
enum class WaitableObjectType
|
||||
|
@ -29,10 +26,8 @@ enum class WaitableObjectType
|
|||
UnsignaledAutoResetEvent,
|
||||
Semaphore,
|
||||
EmptySemaphore,
|
||||
Mutex,
|
||||
LockedMutex,
|
||||
|
||||
Last = LockedMutex
|
||||
Last = EmptySemaphore
|
||||
};
|
||||
|
||||
void operator ++(SignalableObjectType &objectType)
|
||||
|
@ -93,12 +88,6 @@ HANDLE CreateObjectToSignal(SignalableObjectType objectType)
|
|||
case SignalableObjectType::FullSemaphore:
|
||||
return CreateSemaphoreExW(nullptr, 1, 1, nullptr, 0, 0);
|
||||
|
||||
case SignalableObjectType::Mutex:
|
||||
return CreateMutex(nullptr, true, nullptr);
|
||||
|
||||
case SignalableObjectType::UnlockedMutex:
|
||||
return CreateMutex(nullptr, false, nullptr);
|
||||
|
||||
default:
|
||||
TestAssert(false);
|
||||
}
|
||||
|
@ -121,10 +110,6 @@ void VerifySignal(HANDLE h, SignalableObjectType objectType)
|
|||
TestAssert(!ReleaseSemaphore(h, 1, nullptr));
|
||||
break;
|
||||
|
||||
case SignalableObjectType::Mutex:
|
||||
TestAssert(!ReleaseMutex(h));
|
||||
break;
|
||||
|
||||
default:
|
||||
TestAssert(false);
|
||||
}
|
||||
|
@ -163,12 +148,6 @@ HANDLE CreateObjectToWaitOn(WaitableObjectType objectType)
|
|||
case WaitableObjectType::EmptySemaphore:
|
||||
return CreateSemaphoreExW(nullptr, 0, 1, nullptr, 0, 0);
|
||||
|
||||
case WaitableObjectType::Mutex:
|
||||
return CreateMutex(nullptr, false, nullptr);
|
||||
|
||||
case WaitableObjectType::LockedMutex:
|
||||
return CreateMutex(nullptr, true, nullptr);
|
||||
|
||||
default:
|
||||
TestAssert(false);
|
||||
}
|
||||
|
@ -189,20 +168,6 @@ void VerifyWait(HANDLE h, WaitableObjectType objectType)
|
|||
TestAssert(WaitForSingleObject(h, 0) == WAIT_TIMEOUT);
|
||||
break;
|
||||
|
||||
case WaitableObjectType::Mutex:
|
||||
TestAssert(ReleaseMutex(h));
|
||||
TestAssert(!ReleaseMutex(h));
|
||||
TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0);
|
||||
break;
|
||||
|
||||
case WaitableObjectType::LockedMutex:
|
||||
TestAssert(ReleaseMutex(h));
|
||||
TestAssert(ReleaseMutex(h));
|
||||
TestAssert(!ReleaseMutex(h));
|
||||
TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0);
|
||||
TestAssert(WaitForSingleObject(h, 0) == WAIT_OBJECT_0);
|
||||
break;
|
||||
|
||||
default:
|
||||
TestAssert(false);
|
||||
}
|
||||
|
@ -225,17 +190,6 @@ void CloseObjectToWaitOn(HANDLE h, WaitableObjectType objectType)
|
|||
CloseHandle(h);
|
||||
break;
|
||||
|
||||
case WaitableObjectType::Mutex:
|
||||
ReleaseMutex(h);
|
||||
CloseHandle(h);
|
||||
break;
|
||||
|
||||
case WaitableObjectType::LockedMutex:
|
||||
ReleaseMutex(h);
|
||||
ReleaseMutex(h);
|
||||
CloseHandle(h);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -257,11 +211,6 @@ bool Verify(SignalableObjectType signalableObjectType, WaitableObjectType waitab
|
|||
TestAssert(errorCode == ERROR_TOO_MANY_POSTS);
|
||||
return false;
|
||||
|
||||
case SignalableObjectType::UnlockedMutex:
|
||||
TestAssert(waitResult == WAIT_FAILED);
|
||||
TestAssert(errorCode == ERROR_NOT_OWNER);
|
||||
return false;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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 <palsuite.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
|
@ -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 <palsuite.h>
|
||||
|
||||
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<argc; i++)
|
||||
{
|
||||
if (0 == strcmp(argv[i],"-event"))
|
||||
{
|
||||
bNamedEvent = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i],"-mutex"))
|
||||
{
|
||||
bMutex = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i],"-mutex_and_named_event"))
|
||||
{
|
||||
bMutexAndNamedEvent = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i],"-semaphore"))
|
||||
{
|
||||
bSemaphore = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i],"-exitcode") && i < argc-1 )
|
||||
{
|
||||
i++;
|
||||
iExitCode = atoi(argv[i]);
|
||||
Trace("[child] My exit code is %d\n", iExitCode);
|
||||
}
|
||||
|
||||
else if ('-' != *argv[i])
|
||||
{
|
||||
strncpy(szTestName, argv[i], 256);
|
||||
szTestName[255] = 0;
|
||||
iRet = MultiByteToWideChar(CP_ACP, 0, szTestName, strlen(szTestName)+1, wszTestName, 256);
|
||||
if (0 == iRet)
|
||||
{
|
||||
Fail("Failed to convert test string\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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("[child] Failed to convert strings\n");
|
||||
}
|
||||
|
||||
Trace("[child] TestName=%s Event: %S, Mutex: %S, Semaphore = %S\n",
|
||||
szTestName, wszEventName, wszMutexName, wszSemName);
|
||||
|
||||
hNamedEvent = OpenEventW(0, FALSE, wszEventName);
|
||||
if (NULL == hNamedEvent)
|
||||
{
|
||||
Fail("[child] OpenEventW failed [szEventName=%s GetLastError()=%u]\n",
|
||||
szEventName, GetLastError());
|
||||
}
|
||||
hMutex = OpenMutexW(0, FALSE, wszMutexName);
|
||||
if (NULL == hMutex)
|
||||
{
|
||||
Fail("[child] OpenMutexW failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
hSemaphore = CreateSemaphoreExW(NULL, 0, 256, wszSemName, 0, 0);
|
||||
if (NULL == hSemaphore)
|
||||
{
|
||||
Fail("[child] CreateSemaphore failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
|
||||
if (bMutex)
|
||||
{
|
||||
Trace("[child] Going to wait on mutex %s\n", szMutexName);
|
||||
dwRet = WaitForSingleObject(hMutex, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("[child] WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
Trace("[child] Setting event %s\n", szEventName);
|
||||
bRet = SetEvent(hNamedEvent);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("[child] SetEvent failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
// mutex will be abandoned
|
||||
}
|
||||
else if (bMutexAndNamedEvent)
|
||||
{
|
||||
dwRet = WaitForSingleObject(hMutex, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("[child] WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
Sleep(2000);
|
||||
|
||||
bRet = ReleaseMutex(hMutex);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("[child] ReleaseMutex failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
|
||||
bRet = SetEvent(hNamedEvent);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("[child] SetEvent failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
else if (bSemaphore)
|
||||
{
|
||||
LONG lPrevCount = 42;
|
||||
|
||||
|
||||
Trace("[child] Going to wait on event %s\n", szEventName);
|
||||
dwRet = WaitForSingleObject(hNamedEvent, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("[child] WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
Trace("[child] Releasing semaphore %s\n", szSemName);
|
||||
bRet = ReleaseSemaphore(hSemaphore, 10, &lPrevCount);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("ReleaseMutex failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
if (0 != lPrevCount)
|
||||
{
|
||||
Fail("Previous count from semaphore=%d, expected 0\n", lPrevCount);
|
||||
}
|
||||
}
|
||||
else if (bNamedEvent)
|
||||
{
|
||||
Sleep(1000);
|
||||
|
||||
bRet = SetEvent(hNamedEvent);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("[child] SetEvent failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
|
||||
Trace("[child] Done\n");
|
||||
|
||||
PAL_TerminateEx(iExitCode);
|
||||
return iExitCode;
|
||||
}
|
|
@ -1,718 +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
|
||||
**
|
||||
** Purpose: Test for WaitForMultipleObjectsEx in multiple
|
||||
** scenarios
|
||||
**
|
||||
**
|
||||
**=========================================================*/
|
||||
|
||||
#include <palsuite.h>
|
||||
#include <time.h>
|
||||
|
||||
#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<iCount; iCnt++)
|
||||
{
|
||||
if (g_bRandom)
|
||||
{
|
||||
int iRnd;
|
||||
|
||||
bMutex = 0;
|
||||
bEvent = 0;
|
||||
bNamedEvent = 0;
|
||||
bSemaphore = 0;
|
||||
bProcess = 0;
|
||||
bLocalWaitAll = 0;
|
||||
bRemoteWaitAll = 0;
|
||||
|
||||
iRnd = rand() % 7;
|
||||
switch(iRnd)
|
||||
{
|
||||
case 0:
|
||||
bMutex = 1;
|
||||
break;
|
||||
case 1:
|
||||
bEvent = 1;
|
||||
break;
|
||||
case 2:
|
||||
bNamedEvent = 1;
|
||||
break;
|
||||
case 3:
|
||||
bSemaphore = 1;
|
||||
break;
|
||||
case 4:
|
||||
bProcess = 1;
|
||||
break;
|
||||
case 5:
|
||||
bLocalWaitAll = 1;
|
||||
break;
|
||||
case 6:
|
||||
bRemoteWaitAll = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bEvent)
|
||||
{
|
||||
Trace("======================================================================\n");
|
||||
Trace("Local unnamed event test\n");
|
||||
Trace("----------------------------------------\n");
|
||||
hThread = CreateThread(NULL, 0, EventTestThread, (PVOID)hEvent, 0, &dwSlaveThreadTid);
|
||||
if (NULL == hThread)
|
||||
{
|
||||
Fail("Failed to create thread\n");
|
||||
}
|
||||
|
||||
hObjs[0] = hEvent[0];
|
||||
dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed\n");
|
||||
}
|
||||
|
||||
hObjs[0] = hThread;
|
||||
dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed\n");
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
Trace("Local unnamed event test done \n");
|
||||
Trace("======================================================================\n");
|
||||
}
|
||||
|
||||
if (bMutex)
|
||||
{
|
||||
Trace("======================================================================\n");
|
||||
Trace("Mutex with remote thread awakening test\n");
|
||||
Trace("----------------------------------------\n");
|
||||
|
||||
hThread = CreateThread(NULL, 0, MutexTestThread, (PVOID)hMutex, 0, &dwSlaveThreadTid);
|
||||
if (NULL == hThread)
|
||||
{
|
||||
Fail("Failed to create thread\n");
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
|
||||
hObjs[0] = hMutex;
|
||||
|
||||
for (i=0;i<10;i++)
|
||||
{
|
||||
dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [dwRet=%x GetLastError()=%d\n",
|
||||
dwRet, GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
hObjs[0] = hThread;
|
||||
dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
for (i=0;i<10;i++)
|
||||
{
|
||||
bRet = ReleaseMutex(hMutex);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("ReleaseMutex failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
Trace("Mutex with remote thread awakening test done\n");
|
||||
Trace("======================================================================\n");
|
||||
}
|
||||
|
||||
if (bNamedEvent)
|
||||
{
|
||||
Trace("======================================================================\n");
|
||||
Trace("Named event with remote thread awakening test\n");
|
||||
Trace("----------------------------------------\n");
|
||||
|
||||
ZeroMemory ( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory ( &pi, sizeof(pi) );
|
||||
|
||||
sprintf_s (szCmd, 128, "child6 -event %s", szTestName);
|
||||
szCmd[127] = 0;
|
||||
|
||||
LPWSTR szCmdW = convert(szCmd);
|
||||
bRet = CreateProcessW(NULL, szCmdW, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
||||
free(szCmdW);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("CreateProcessW failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
hObjs[0] = pi.hProcess;
|
||||
hObjs[1] = hNamedEvent;
|
||||
|
||||
dwRet = WaitForMultipleObjects(2, hObjs, FALSE, INFINITE);
|
||||
if (1 != dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [dwRet=%u GetLastError()=%u]\n",
|
||||
dwRet, GetLastError());
|
||||
}
|
||||
|
||||
dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
Trace("Named event with remote thread awakening test done\n");
|
||||
Trace("======================================================================\n");
|
||||
}
|
||||
|
||||
if (bSemaphore)
|
||||
{
|
||||
Trace("======================================================================\n");
|
||||
Trace("Semaphore with remote thread awakening test\n");
|
||||
Trace("----------------------------------------\n");
|
||||
|
||||
ZeroMemory ( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory ( &pi, sizeof(pi) );
|
||||
|
||||
sprintf_s (szCmd, 128, "child6 -semaphore %s", szTestName);
|
||||
szCmd[127] = 0;
|
||||
|
||||
LPWSTR szCmdW = convert(szCmd);
|
||||
bRet = CreateProcessW(NULL, szCmdW, NULL, NULL, FALSE,
|
||||
0, NULL, NULL, &si, &pi);
|
||||
free(szCmdW);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("CreateProcessW failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
|
||||
Trace("Setting event %s\n", szEventName);
|
||||
bRet = SetEvent(hNamedEvent);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("[child] SetEvent failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
Trace("Going to wait on semaphore %s\n", szSemName);
|
||||
|
||||
|
||||
hObjs[0] = pi.hProcess;
|
||||
hObjs[0] = hEvent[0];
|
||||
hObjs[1] = hSemaphore;
|
||||
for (i=0;i<10;i++)
|
||||
{
|
||||
dwRet = WaitForMultipleObjects(2, hObjs, FALSE, INFINITE);
|
||||
if (1 != dwRet)
|
||||
{
|
||||
Trace("WaitForMultipleObjects failed [tid=%u dwRet=%u GetLastError()=%u]\n",
|
||||
GetCurrentThreadId(), dwRet, GetLastError());
|
||||
DebugBreak();
|
||||
}
|
||||
}
|
||||
|
||||
dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
Trace("Semaphore with remote thread awakening test done\n");
|
||||
Trace("======================================================================\n");
|
||||
}
|
||||
|
||||
if (bProcess)
|
||||
{
|
||||
DWORD dwExitCode;
|
||||
|
||||
Trace("======================================================================\n");
|
||||
Trace("Process wait test\n");
|
||||
Trace("----------------------------------------\n");
|
||||
|
||||
iDesiredExitCode = rand() % 0xFF;
|
||||
|
||||
ZeroMemory ( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory ( &pi, sizeof(pi) );
|
||||
|
||||
sprintf_s (szCmd, 128, "child6 -mutex %s -exitcode %d", szTestName, iDesiredExitCode);
|
||||
szCmd[127] = 0;
|
||||
|
||||
LPWSTR szCmdW = convert(szCmd);
|
||||
bRet = CreateProcessW(NULL, szCmdW, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
||||
free(szCmdW);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("CreateProcessW failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
Trace("Going to wait on event %s\n", szEventName);
|
||||
dwRet = WaitForSingleObject(hNamedEvent, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
hObjs[0] = hEvent[0]; // dummy, this is a local event
|
||||
hObjs[1] = hMutex;
|
||||
|
||||
dwRet = WaitForMultipleObjects(2, hObjs, FALSE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
if (1 == dwRet || (1 + WAIT_ABANDONED_0) == dwRet)
|
||||
{
|
||||
bRet = ReleaseMutex(hMutex);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("ReleaseMutex failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
if (!GetExitCodeProcess(pi.hProcess, &dwExitCode))
|
||||
{
|
||||
Trace("GetExitCodeProcess call failed LastError:(%u)\n",
|
||||
GetLastError());
|
||||
dwExitCode = FAIL;
|
||||
}
|
||||
|
||||
if (iDesiredExitCode != dwExitCode)
|
||||
{
|
||||
Fail("Wrong return code: %u [%d]\n", dwExitCode, iDesiredExitCode);
|
||||
}
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
Trace("Process wait test done\n");
|
||||
Trace("======================================================================\n");
|
||||
}
|
||||
|
||||
if (bLocalWaitAll)
|
||||
{
|
||||
Trace("======================================================================\n");
|
||||
Trace("WaitAll with local thread awakening test\n");
|
||||
Trace("----------------------------------------\n");
|
||||
|
||||
hThread = CreateThread(NULL, 0, EventTestThread, (PVOID)hEvent, 0, &dwSlaveThreadTid);
|
||||
if (NULL == hThread)
|
||||
{
|
||||
Fail("CreateThread failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
dwRet = WaitForMultipleObjects(2, hEvent, TRUE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
hObjs[0] = hThread;
|
||||
dwRet = WaitForMultipleObjects(1, hObjs, FALSE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
Trace("WaitAll with local thread awakening test done\n");
|
||||
Trace("======================================================================\n");
|
||||
}
|
||||
|
||||
if (bRemoteWaitAll)
|
||||
{
|
||||
Trace("======================================================================\n");
|
||||
Trace("WaitAll with remote thread awakening test\n");
|
||||
Trace("----------------------------------------\n");
|
||||
|
||||
ZeroMemory ( &si, sizeof(si) );
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory ( &pi, sizeof(pi) );
|
||||
|
||||
sprintf_s (szCmd, 128, "child6 -mutex_and_named_event %s", szTestName);
|
||||
szCmd[127] = 0;
|
||||
|
||||
LPWSTR szCmdW = convert(szCmd);
|
||||
bRet = CreateProcessW(NULL, szCmdW, NULL, NULL, FALSE,
|
||||
0, NULL, NULL, &si, &pi);
|
||||
free(szCmdW);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("CreateProcessW failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
|
||||
hObjs[0] = hMutex;
|
||||
hObjs[1] = hNamedEvent;
|
||||
|
||||
dwRet = WaitForMultipleObjects(2, hObjs, TRUE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
bRet = ReleaseMutex(hMutex);
|
||||
if (FALSE == bRet)
|
||||
{
|
||||
Fail("ReleaseMutex failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [GetLastError()=%u]\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
Trace("WaitAll with remote thread awakening test done\n");
|
||||
Trace("======================================================================\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PALTEST(threading_WaitForMultipleObjectsEx_test6_paltest_waitformultipleobjectsex_test6, "threading/WaitForMultipleObjectsEx/test6/paltest_waitformultipleobjectsex_test6")
|
||||
{
|
||||
DWORD dwRet;
|
||||
DWORD dwSlaveThreadTid = 0;
|
||||
int i, iCnt;
|
||||
|
||||
if(0 != (PAL_Initialize(argc, argv)))
|
||||
{
|
||||
return ( FAIL );
|
||||
}
|
||||
|
||||
srand(time(NULL) * GetCurrentProcessId());
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
g_bMutex = 1;
|
||||
g_bEvent = 1;
|
||||
g_bNamedEvent = 1;
|
||||
g_bSemaphore = 1;
|
||||
g_bProcess = 1;
|
||||
g_bLocalWaitAll = 1;
|
||||
g_bRemoteWaitAll = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i=1;i<argc;i++)
|
||||
{
|
||||
if (0 == strcmp(argv[i], "-mutex"))
|
||||
{
|
||||
g_bMutex = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-event"))
|
||||
{
|
||||
g_bEvent = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-namedevent"))
|
||||
{
|
||||
g_bNamedEvent = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-semaphore"))
|
||||
{
|
||||
g_bSemaphore = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-process"))
|
||||
{
|
||||
g_bProcess = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-localwaitall"))
|
||||
{
|
||||
g_bLocalWaitAll = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-remotewaitall"))
|
||||
{
|
||||
g_bRemoteWaitAll = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-all"))
|
||||
{
|
||||
g_bMutex = 1;
|
||||
g_bEvent = 1;
|
||||
g_bNamedEvent = 1;
|
||||
g_bSemaphore = 1;
|
||||
g_bProcess = 1;
|
||||
g_bLocalWaitAll = 1;
|
||||
g_bRemoteWaitAll = 1;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "-random"))
|
||||
{
|
||||
g_bRandom = 1;
|
||||
}
|
||||
else if ((0 == strcmp(argv[i], "-count")) && (argc > 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<iThreads;i++)
|
||||
{
|
||||
hThreads[iCnt] = CreateThread(NULL, 0, TestThread, (VOID*)iCnt, 0, &dwSlaveThreadTid);
|
||||
if (NULL == hThreads[iCnt])
|
||||
{
|
||||
Trace("Failed to create thread\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
iCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == iCnt)
|
||||
{
|
||||
Fail("Can't create any thread\n");
|
||||
}
|
||||
|
||||
for (i=0; i<iCnt; i+=64)
|
||||
{
|
||||
dwRet = WaitForMultipleObjects(MIN(64, iCnt-i), &hThreads[i], TRUE, INFINITE);
|
||||
if (WAIT_FAILED == dwRet)
|
||||
{
|
||||
Fail("WaitForMultipleObjects failed [dwRet=%u GetLastError()=%u iCnt=%d i=%d]\n",
|
||||
dwRet, GetLastError(), iCnt, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (i=0; i<iCnt; i++)
|
||||
{
|
||||
CloseHandle(hThreads[i]);
|
||||
}
|
||||
|
||||
PAL_Terminate();
|
||||
return PASS;
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
/*=====================================================================
|
||||
**
|
||||
** Source: WFSOExMutex.c
|
||||
**
|
||||
** Purpose: Tests a child thread in the middle of a
|
||||
** WaitForSingleObjectEx call will be interrupted by QueueUserAPC
|
||||
** if the alert flag was set.
|
||||
**
|
||||
**
|
||||
**===================================================================*/
|
||||
|
||||
#include <palsuite.h>
|
||||
|
||||
/*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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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 <palsuite.h>
|
||||
|
||||
|
||||
#define NUMBER_OF_WORKER_THREADS 2
|
||||
|
||||
//Declaring Variables
|
||||
HANDLE hMutex_WFSOMutexTest = NULL;
|
||||
unsigned int globalcounter_WFSOMutexTest =0;
|
||||
int testReturnCode_WFSOMutexTest = PASS;
|
||||
|
||||
//Declaring Function Prototypes
|
||||
DWORD PALAPI WFSOMutexTest(LPVOID params);
|
||||
void incrementCounter_WFSOMutexTest(void);
|
||||
|
||||
|
||||
|
||||
PALTEST(threading_WaitForSingleObject_WFSOMutexTest_paltest_waitforsingleobject_wfsomutextest, "threading/WaitForSingleObject/WFSOMutexTest/paltest_waitforsingleobject_wfsomutextest")
|
||||
{
|
||||
|
||||
//Declare local variables
|
||||
int i =0;
|
||||
|
||||
// 2 dimensional array to hold thread handles for each worker thread
|
||||
HANDLE hThread[NUMBER_OF_WORKER_THREADS];
|
||||
DWORD dwThreadId=0;
|
||||
int returnCode = 0;
|
||||
|
||||
//Initialize PAL
|
||||
if(0 != (PAL_Initialize(argc, argv)))
|
||||
{
|
||||
return ( FAIL );
|
||||
}
|
||||
|
||||
//Create Mutex
|
||||
hMutex_WFSOMutexTest = CreateMutex(NULL, // no security attributes
|
||||
FALSE, // initially not owned
|
||||
NULL); // name of mutex
|
||||
|
||||
//Check for Mutex Creation
|
||||
|
||||
if (hMutex_WFSOMutexTest == NULL)
|
||||
{
|
||||
Fail("Create Mutex Failed, GetLastError: %d\n", GetLastError());
|
||||
}
|
||||
|
||||
|
||||
//Spawn 2 worker threads
|
||||
for (i=0;i<NUMBER_OF_WORKER_THREADS;i++)
|
||||
{
|
||||
//Create Thread
|
||||
|
||||
hThread[i] = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
WFSOMutexTest,
|
||||
NULL,
|
||||
0,
|
||||
&dwThreadId);
|
||||
|
||||
if ( NULL == hThread[i] )
|
||||
{
|
||||
Fail ( "CreateThread() returned NULL. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Test running */
|
||||
returnCode = WaitForMultipleObjects( NUMBER_OF_WORKER_THREADS, hThread, TRUE, 5000);
|
||||
if( WAIT_OBJECT_0 != returnCode )
|
||||
{
|
||||
Trace("Wait for Object(s) returned %d, and GetLastError value is %d\n", returnCode, GetLastError());
|
||||
testReturnCode_WFSOMutexTest = FAIL;
|
||||
}
|
||||
|
||||
//Close thread handles
|
||||
for (i=0;i<NUMBER_OF_WORKER_THREADS;i++)
|
||||
{
|
||||
|
||||
if (0==CloseHandle(hThread[i]))
|
||||
{
|
||||
Trace("Could not Close thread handle\n");
|
||||
Fail ( "GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
//Close Mutex Handle
|
||||
if (0==CloseHandle(hMutex_WFSOMutexTest))
|
||||
{
|
||||
Trace("Could not close mutex handle\n");
|
||||
Fail ( "GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
|
||||
|
||||
PAL_TerminateEx(testReturnCode_WFSOMutexTest);
|
||||
return ( testReturnCode_WFSOMutexTest );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void incrementCounter_WFSOMutexTest(void)
|
||||
{
|
||||
if (INT_MAX == globalcounter_WFSOMutexTest)
|
||||
{
|
||||
globalcounter_WFSOMutexTest = 0;
|
||||
}
|
||||
|
||||
globalcounter_WFSOMutexTest++;
|
||||
Trace("Global Counter Value: %d \n", globalcounter_WFSOMutexTest);
|
||||
}
|
||||
|
||||
|
||||
DWORD PALAPI WFSOMutexTest(LPVOID params)
|
||||
{
|
||||
|
||||
DWORD dwWaitResult;
|
||||
|
||||
// Request ownership of mutex.
|
||||
|
||||
dwWaitResult = WaitForSingleObject(
|
||||
hMutex_WFSOMutexTest, // handle to mutex
|
||||
5000L); // five-second time-out interval
|
||||
|
||||
switch (dwWaitResult)
|
||||
{
|
||||
// The thread got mutex ownership.
|
||||
case WAIT_OBJECT_0:
|
||||
{
|
||||
|
||||
incrementCounter_WFSOMutexTest();
|
||||
|
||||
//Release ownership of the mutex object.
|
||||
if (! ReleaseMutex(hMutex_WFSOMutexTest))
|
||||
{
|
||||
Fail ( "ReleaseMutex() returned NULL. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Cannot get mutex ownership due to time-out.
|
||||
case WAIT_TIMEOUT:
|
||||
{
|
||||
Fail ( "Cannot get mutex ownership due to time-out. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Got ownership of the abandoned mutex object.
|
||||
case WAIT_ABANDONED:
|
||||
{
|
||||
Fail ( "Got ownership of the abandoned mutex object. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -82,14 +82,6 @@ switch (dwWaitResult)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
// Got ownership of the abandoned process object.
|
||||
case WAIT_ABANDONED:
|
||||
{
|
||||
Fail ( "Got ownership of the abandoned Process object. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//Error condition
|
||||
case WAIT_FAILED:
|
||||
{
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
/*============================================================
|
||||
**
|
||||
** Source: WFSOThreadTest.c
|
||||
** Source: WFSOThreadTest.c
|
||||
**
|
||||
** Purpose: Test for WaitForSingleObjectTest.
|
||||
** Purpose: Test for WaitForSingleObjectTest.
|
||||
** Create One Thread and do some work
|
||||
** Use WFSO For the Thread to finish
|
||||
**
|
||||
** Use WFSO For the Thread to finish
|
||||
**
|
||||
** Test Passes if the above operations are successful
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**=========================================================*/
|
||||
|
@ -33,10 +33,10 @@ PALTEST(threading_WaitForSingleObject_WFSOThreadTest_paltest_waitforsingleobject
|
|||
{
|
||||
|
||||
//Declare local variables
|
||||
DWORD dwThreadId=0;
|
||||
DWORD dwWaitResult=0;
|
||||
DWORD dwThreadId=0;
|
||||
DWORD dwWaitResult=0;
|
||||
|
||||
//Initialize PAL
|
||||
//Initialize PAL
|
||||
if(0 != (PAL_Initialize(argc, argv)))
|
||||
{
|
||||
return ( FAIL );
|
||||
|
@ -51,101 +51,84 @@ PALTEST(threading_WaitForSingleObject_WFSOThreadTest_paltest_waitforsingleobject
|
|||
"GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Create Thread
|
||||
hThread_WFSOThreadTest = CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
incrementCounter,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
incrementCounter,
|
||||
NULL,
|
||||
0,
|
||||
&dwThreadId);
|
||||
|
||||
if ( NULL == hThread_WFSOThreadTest )
|
||||
if ( NULL == hThread_WFSOThreadTest )
|
||||
{
|
||||
Fail ( "CreateThread() returned NULL. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
|
||||
|
||||
//Wait For Thread to signal start
|
||||
//Wait For Thread to signal start
|
||||
dwWaitResult = WaitForSingleObject(hEvent_WFSOThreadTest,INFINITE);
|
||||
|
||||
switch (dwWaitResult)
|
||||
|
||||
switch (dwWaitResult)
|
||||
{
|
||||
// The thread wait was successful
|
||||
case WAIT_OBJECT_0:
|
||||
case WAIT_OBJECT_0:
|
||||
{
|
||||
|
||||
Trace ("Wait for Single Object (hEvent) was successful.\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Time-out.
|
||||
case WAIT_TIMEOUT:
|
||||
case WAIT_TIMEOUT:
|
||||
{
|
||||
Fail ( "Time -out. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Got ownership of the abandoned event object.
|
||||
case WAIT_ABANDONED:
|
||||
{
|
||||
Fail ( "Got ownership of the abandoned event object. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//Wait for Thread to finish
|
||||
dwWaitResult = WaitForSingleObject(
|
||||
|
||||
//Wait for Thread to finish
|
||||
dwWaitResult = WaitForSingleObject(
|
||||
hThread_WFSOThreadTest, //handle to thread
|
||||
5000L); //Wait Indefinitely
|
||||
|
||||
|
||||
switch (dwWaitResult)
|
||||
|
||||
switch (dwWaitResult)
|
||||
{
|
||||
// The thread wait was successful
|
||||
case WAIT_OBJECT_0:
|
||||
case WAIT_OBJECT_0:
|
||||
{
|
||||
|
||||
Trace("Wait for thread was successful\n");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Time-out.
|
||||
case WAIT_TIMEOUT:
|
||||
case WAIT_TIMEOUT:
|
||||
{
|
||||
Fail ( "Time -out. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Got ownership of the abandoned thread object.
|
||||
case WAIT_ABANDONED:
|
||||
{
|
||||
Fail ( "Got ownership of the abandoned thread object. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//Close Handles
|
||||
if (0==CloseHandle(hEvent_WFSOThreadTest))
|
||||
{
|
||||
Trace("Could not Close event handle\n");
|
||||
Fail ( "GetLastError returned %d\n", GetLastError());
|
||||
Trace("Could not Close event handle\n");
|
||||
Fail ( "GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
if (0==CloseHandle(hThread_WFSOThreadTest))
|
||||
{
|
||||
Trace("Could not Close thread handle\n");
|
||||
Fail ( "GetLastError returned %d\n", GetLastError());
|
||||
Trace("Could not Close thread handle\n");
|
||||
Fail ( "GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
|
||||
PAL_Terminate();
|
||||
|
@ -160,13 +143,13 @@ DWORD PALAPI incrementCounter(LPVOID params)
|
|||
if (0==SetEvent(hEvent_WFSOThreadTest))
|
||||
{
|
||||
Fail ( "SetEvent returned Zero. Failing test.\n"
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
"GetLastError returned %d\n", GetLastError());
|
||||
}
|
||||
|
||||
for (globalcounter_WFSOThreadTest=0;globalcounter_WFSOThreadTest<100000;globalcounter_WFSOThreadTest++);
|
||||
|
||||
//Sleep(5000);
|
||||
|
||||
|
||||
Trace("Global Counter Value: %d \n", globalcounter_WFSOThreadTest);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -447,6 +447,32 @@ extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle threa
|
|||
return res;
|
||||
}
|
||||
|
||||
extern "C" void QCALLTYPE ThreadNative_SetWaitSleepJoinState(QCall::ThreadHandle thread)
|
||||
{
|
||||
QCALL_CONTRACT;
|
||||
|
||||
BEGIN_QCALL;
|
||||
|
||||
// Set the state bits.
|
||||
thread->SetThreadState(Thread::TS_Interruptible);
|
||||
thread->SetThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin);
|
||||
|
||||
END_QCALL;
|
||||
}
|
||||
|
||||
extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHandle thread)
|
||||
{
|
||||
QCALL_CONTRACT;
|
||||
|
||||
BEGIN_QCALL;
|
||||
|
||||
// Clear the state bits.
|
||||
thread->ResetThreadState(Thread::TS_Interruptible);
|
||||
thread->ResetThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin);
|
||||
|
||||
END_QCALL;
|
||||
}
|
||||
|
||||
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
|
||||
|
||||
// Return whether the thread hosts an STA, is a member of the MTA or is not
|
||||
|
@ -899,3 +925,18 @@ extern "C" void QCALLTYPE ThreadNative_ResetAbort()
|
|||
pThread->UnmarkThreadForAbort(EEPolicy::TA_Safe);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" BOOL QCALLTYPE ThreadNative_CurrentThreadIsFinalizerThread()
|
||||
{
|
||||
QCALL_CONTRACT;
|
||||
|
||||
BOOL retval = FALSE;
|
||||
|
||||
BEGIN_QCALL;
|
||||
|
||||
retval = IsFinalizerThread() ? TRUE : FALSE;
|
||||
|
||||
END_QCALL;
|
||||
|
||||
return retval;
|
||||
}
|
|
@ -55,6 +55,8 @@ extern "C" void QCALLTYPE ThreadNative_PollGC();
|
|||
extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId();
|
||||
extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t);
|
||||
extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread);
|
||||
extern "C" void QCALLTYPE ThreadNative_SetWaitSleepJoinState(QCall::ThreadHandle thread);
|
||||
extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHandle thread);
|
||||
|
||||
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
|
||||
extern "C" INT32 QCALLTYPE ThreadNative_GetApartmentState(QCall::ObjectHandleOnStack t);
|
||||
|
@ -71,5 +73,7 @@ extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime);
|
|||
extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::ThreadHandle thread);
|
||||
#endif // FEATURE_COMINTEROP
|
||||
|
||||
extern "C" BOOL QCALLTYPE ThreadNative_CurrentThreadIsFinalizerThread();
|
||||
|
||||
#endif // _COMSYNCHRONIZABLE_H
|
||||
|
||||
|
|
|
@ -82,22 +82,3 @@ extern "C" INT32 QCALLTYPE WaitHandle_SignalAndWait(HANDLE waitHandleSignal, HAN
|
|||
END_QCALL;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
#ifdef TARGET_UNIX
|
||||
extern "C" INT32 QCALLTYPE WaitHandle_WaitOnePrioritized(HANDLE handle, INT32 timeoutMs)
|
||||
{
|
||||
QCALL_CONTRACT;
|
||||
|
||||
DWORD result = WAIT_FAILED;
|
||||
|
||||
BEGIN_QCALL;
|
||||
|
||||
_ASSERTE(handle != NULL);
|
||||
_ASSERTE(handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
result = PAL_WaitForSingleObjectPrioritized(handle, timeoutMs);
|
||||
|
||||
END_QCALL;
|
||||
return (INT32)result;
|
||||
}
|
||||
#endif // TARGET_UNIX
|
||||
|
|
|
@ -18,8 +18,4 @@ extern "C" INT32 QCALLTYPE WaitHandle_WaitOneCore(HANDLE handle, INT32 timeout,
|
|||
extern "C" INT32 QCALLTYPE WaitHandle_WaitMultipleIgnoringSyncContext(HANDLE *handleArray, INT32 numHandles, BOOL waitForAll, INT32 timeout);
|
||||
extern "C" INT32 QCALLTYPE WaitHandle_SignalAndWait(HANDLE waitHandleSignal, HANDLE waitHandleWait, INT32 timeout);
|
||||
|
||||
#ifdef TARGET_UNIX
|
||||
extern "C" INT32 QCALLTYPE WaitHandle_WaitOnePrioritized(HANDLE handle, INT32 timeoutMs);
|
||||
#endif // TARGET_UNIX
|
||||
|
||||
#endif // _COM_WAITABLE_HANDLE_H
|
||||
|
|
|
@ -969,6 +969,7 @@ DEFINE_CLASS(DIRECTONTHREADLOCALDATA, Threading, Thread+DirectOnThreadLocalData)
|
|||
DEFINE_CLASS(THREAD, Threading, Thread)
|
||||
DEFINE_METHOD(THREAD, START_CALLBACK, StartCallback, IM_RetVoid)
|
||||
DEFINE_METHOD(THREAD, POLLGC, PollGC, NoSig)
|
||||
DEFINE_METHOD(THREAD, ON_THREAD_EXITING, OnThreadExiting, IM_RetVoid)
|
||||
|
||||
#ifdef FEATURE_OBJCMARSHAL
|
||||
DEFINE_CLASS(AUTORELEASEPOOL, Threading, AutoreleasePool)
|
||||
|
|
|
@ -1309,6 +1309,9 @@ private:
|
|||
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.
|
||||
|
|
|
@ -289,6 +289,8 @@ static const Entry s_QCall[] =
|
|||
DllImportEntry(ThreadNative_GetCurrentOSThreadId)
|
||||
DllImportEntry(ThreadNative_Initialize)
|
||||
DllImportEntry(ThreadNative_GetThreadState)
|
||||
DllImportEntry(ThreadNative_SetWaitSleepJoinState)
|
||||
DllImportEntry(ThreadNative_ClearWaitSleepJoinState)
|
||||
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
|
||||
DllImportEntry(ThreadNative_GetApartmentState)
|
||||
DllImportEntry(ThreadNative_SetApartmentState)
|
||||
|
@ -303,12 +305,10 @@ static const Entry s_QCall[] =
|
|||
#ifdef FEATURE_COMINTEROP
|
||||
DllImportEntry(ThreadNative_DisableComObjectEagerCleanup)
|
||||
#endif // FEATURE_COMINTEROP
|
||||
DllImportEntry(ThreadNative_CurrentThreadIsFinalizerThread)
|
||||
DllImportEntry(WaitHandle_WaitOneCore)
|
||||
DllImportEntry(WaitHandle_WaitMultipleIgnoringSyncContext)
|
||||
DllImportEntry(WaitHandle_SignalAndWait)
|
||||
#ifdef TARGET_UNIX
|
||||
DllImportEntry(WaitHandle_WaitOnePrioritized)
|
||||
#endif // TARGET_UNIX
|
||||
DllImportEntry(ClrConfig_GetConfigBoolValue)
|
||||
DllImportEntry(Buffer_Clear)
|
||||
DllImportEntry(Buffer_MemMove)
|
||||
|
@ -484,24 +484,12 @@ static const Entry s_QCall[] =
|
|||
#endif
|
||||
#if defined(TARGET_UNIX)
|
||||
DllImportEntry(CloseHandle)
|
||||
DllImportEntry(CreateEventExW)
|
||||
DllImportEntry(CreateMutexExW)
|
||||
DllImportEntry(CreateSemaphoreExW)
|
||||
DllImportEntry(FormatMessageW)
|
||||
DllImportEntry(FreeEnvironmentStringsW)
|
||||
DllImportEntry(GetEnvironmentStringsW)
|
||||
DllImportEntry(GetEnvironmentVariableW)
|
||||
DllImportEntry(OpenEventW)
|
||||
DllImportEntry(OpenMutexW)
|
||||
DllImportEntry(OpenSemaphoreW)
|
||||
DllImportEntry(OutputDebugStringW)
|
||||
DllImportEntry(PAL_CreateMutexW)
|
||||
DllImportEntry(PAL_OpenMutexW)
|
||||
DllImportEntry(ReleaseMutex)
|
||||
DllImportEntry(ReleaseSemaphore)
|
||||
DllImportEntry(ResetEvent)
|
||||
DllImportEntry(SetEnvironmentVariableW)
|
||||
DllImportEntry(SetEvent)
|
||||
#endif
|
||||
#if defined(TARGET_X86) || defined(TARGET_AMD64)
|
||||
DllImportEntry(X86BaseCpuId)
|
||||
|
|
|
@ -2727,6 +2727,15 @@ void Thread::CooperativeCleanup()
|
|||
|
||||
GCX_COOP();
|
||||
|
||||
if (!IsGCSpecial())
|
||||
{
|
||||
// Allow managed subsystems to clean up on thread exit.
|
||||
PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__ON_THREAD_EXITING);
|
||||
DECLARE_ARGHOLDER_ARRAY(args, 1);
|
||||
args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(GetExposedObject());
|
||||
CALL_MANAGED_METHOD_NORET(args);
|
||||
}
|
||||
|
||||
// Clear any outstanding stale EH state that maybe still active on the thread.
|
||||
#ifdef FEATURE_EH_FUNCLETS
|
||||
ExInfo::PopTrackers((void*)-1);
|
||||
|
|
|
@ -907,8 +907,8 @@ public:
|
|||
// 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<ThreadState> m_State; // Bits for the state of the thread
|
||||
|
||||
Volatile<ThreadState> 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.
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static partial class Sys
|
||||
{
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetPageSize")]
|
||||
internal static partial int GetPageSize();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
internal static partial class Interop
|
||||
{
|
||||
internal static unsafe partial class Sys
|
||||
{
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_Init", SetLastError = true)]
|
||||
internal static partial int LowLevelCrossProcessMutex_Init(void* mutex);
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_Acquire", SetLastError = true)]
|
||||
internal static partial int LowLevelCrossProcessMutex_Acquire(void* mutex, int timeoutMilliseconds);
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_Release", SetLastError = true)]
|
||||
internal static partial int LowLevelCrossProcessMutex_Release(void* mutex);
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_Destroy", SetLastError = true)]
|
||||
internal static partial int LowLevelCrossProcessMutex_Destroy(void* mutex);
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_Size")]
|
||||
[SuppressGCTransition]
|
||||
internal static partial int LowLevelCrossProcessMutex_Size();
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_GetOwnerProcessAndThreadId", SetLastError = true)]
|
||||
[SuppressGCTransition]
|
||||
internal static partial void LowLevelCrossProcessMutex_GetOwnerProcessAndThreadId(void* mutex, out uint processId, out uint threadId);
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_SetOwnerProcessAndThreadId", SetLastError = true)]
|
||||
[SuppressGCTransition]
|
||||
internal static partial void LowLevelCrossProcessMutex_SetOwnerProcessAndThreadId(void* mutex, uint processId, uint threadId);
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_IsAbandoned", SetLastError = true)]
|
||||
[SuppressGCTransition]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static partial bool LowLevelCrossProcessMutex_IsAbandoned(void* mutex);
|
||||
|
||||
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_LowLevelCrossProcessMutex_SetAbandoned", SetLastError = true)]
|
||||
[SuppressGCTransition]
|
||||
internal static partial void LowLevelCrossProcessMutex_SetAbandoned(void* mutex, [MarshalAs(UnmanagedType.U1)] bool abandoned);
|
||||
}
|
||||
}
|
|
@ -4378,5 +4378,31 @@
|
|||
</data>
|
||||
<data name="PlatformNotSupported_DynamicEntrypoint" xml:space="preserve">
|
||||
<value>Dynamic entrypoint allocation is not supported in the current environment.</value>
|
||||
</data> <data name="Arg_InvalidOperationException_CannotReleaseUnownedMutex" xml:space="preserve">
|
||||
<value>Cannot release a lock that is not owned by the current thread.</value>
|
||||
</data>
|
||||
<data name="Arg_FailedToInitializePThreadMutex" xml:space="preserve">
|
||||
<value>Failed to initialize pthread mutex</value>
|
||||
</data>
|
||||
<data name="IO_SharedMemory_FileNotOwnedByUid" xml:space="preserve">
|
||||
<value>The file '{0}' is not owned by the current user with UID {1}.</value>
|
||||
</data>
|
||||
<data name="IO_SharedMemory_FilePermissionsIncorrect" xml:space="preserve">
|
||||
<value>The file '{0}' does not have the expected permissions for user scope: {1}.</value>
|
||||
</data>
|
||||
<data name="IO_SharedMemory_PathExistsButNotDirectory" xml:space="preserve">
|
||||
<value>The path '{0}' exists but is not a directory.</value>
|
||||
</data>
|
||||
<data name="IO_SharedMemory_DirectoryPermissionsIncorrect" xml:space="preserve">
|
||||
<value>The directory '{0}' does not have the expected owner or permissions for system scope: {1}, {2}.</value>
|
||||
</data>
|
||||
<data name="IO_SharedMemory_DirectoryNotOwnedByUid" xml:space="preserve">
|
||||
<value>The directory '{0}' is not owned by the current user with UID {1}.</value>
|
||||
</data>
|
||||
<data name="IO_SharedMemory_DirectoryPermissionsIncorrectUserScope" xml:space="preserve">
|
||||
<value>The directory '{0}' does not have the expected permissions for user scope: {1}.</value>
|
||||
</data>
|
||||
<data name="IO_SharedMemory_DirectoryOwnerPermissionsIncorrect" xml:space="preserve">
|
||||
<value>The directory '{0}' does not have the expected owner permissions: {1}.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<Is64Bit Condition="'$(Platform)' == 'arm64' or '$(Platform)' == 'x64' or '$(Platform)' == 's390x' or '$(Platform)' == 'loongarch64' or '$(Platform)' == 'ppc64le' or '$(Platform)' == 'riscv64'">true</Is64Bit>
|
||||
<UseMinimalGlobalizationData Condition="'$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true'">true</UseMinimalGlobalizationData>
|
||||
<EnableLibraryImportGenerator>true</EnableLibraryImportGenerator>
|
||||
<FeatureCrossProcessMutex Condition="'$(IsMobileLike)' != 'true'">true</FeatureCrossProcessMutex>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DefineConstants Condition="'$(IsBigEndian)' == 'true'">$(DefineConstants);BIGENDIAN</DefineConstants>
|
||||
|
@ -49,6 +50,9 @@
|
|||
<DefineConstants Condition="'$(TargetsSolaris)' == 'true'">$(DefineConstants);TARGET_SOLARIS</DefineConstants>
|
||||
<DefineConstants Condition="'$(TargetsHaiku)' == 'true'">$(DefineConstants);TARGET_HAIKU</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DefineConstants Condition="'$(FeatureCrossProcessMutex)' == 'true'">$(DefineConstants);FEATURE_CROSS_PROCESS_MUTEX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ApiCompatSuppressionFile Include="$(MSBuildThisFileDirectory)CompatibilitySuppressions.xml" />
|
||||
</ItemGroup>
|
||||
|
@ -1767,6 +1771,15 @@
|
|||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CreateFile.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.CreateFile.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.EventWaitHandle.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.EventWaitHandle.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.Mutex.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.Mutex.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.Semaphore.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.Semaphore.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\Interop\Windows\Kernel32\Interop.Timer.cs">
|
||||
<Link>Interop\Windows\Kernel32\Interop.Timer.cs</Link>
|
||||
</Compile>
|
||||
|
@ -2225,6 +2238,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.OverlappedValueTaskSource.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeRegistryHandle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeThreadPoolIOHandle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\AppDomain.Windows.cs" />
|
||||
|
@ -2263,10 +2277,12 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\RuntimeInformation.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StandardOleMarshalObject.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\EventWaitHandle.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Mutex.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\RegisteredWaitHandle.WindowsThreadPool.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Thread.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Semaphore.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.WindowsThreadPool.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPool.Windows.cs" />
|
||||
|
@ -2291,16 +2307,12 @@
|
|||
</ItemGroup>
|
||||
<!-- CoreCLR uses PAL layer that emulates Windows API on Unix. This is bridge for that PAL layer. See issue dotnet/runtime/#31721. -->
|
||||
<ItemGroup Condition="'$(TargetsWindows)' == 'true' or '$(FeatureCoreCLR)'=='true'">
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.Windows.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CloseHandle.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.CloseHandle.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.Constants.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.Constants.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.EventWaitHandle.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.EventWaitHandle.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetEnvironmentVariable.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.GetEnvironmentVariable.cs</Link>
|
||||
</Compile>
|
||||
|
@ -2313,12 +2325,6 @@
|
|||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.FormatMessage.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.FormatMessage.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.Mutex.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.Mutex.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.Semaphore.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.Semaphore.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetEnvironmentVariable.cs">
|
||||
<Link>Common\Interop\Windows\Kernel32\Interop.SetEnvironmentVariable.cs</Link>
|
||||
</Compile>
|
||||
|
@ -2329,8 +2335,6 @@
|
|||
<Link>Common\System\Memory\FixedBufferExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Variables.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Semaphore.Windows.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\EventWaitHandle.Windows.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true'">
|
||||
<Compile Include="$(CommonPath)Interop\Unix\Interop.Errors.cs">
|
||||
|
@ -2405,6 +2409,9 @@
|
|||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetOSArchitecture.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.GetOSArchitecture.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetPageSize.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.GetPageSize.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetProcessPath.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.GetProcessPath.cs</Link>
|
||||
</Compile>
|
||||
|
@ -2459,6 +2466,12 @@
|
|||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MksTemps.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.MksTemps.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MMap.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.MMap.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MUnmap.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.MUnmap.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.MountPoints.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.MountPoints.cs</Link>
|
||||
</Compile>
|
||||
|
@ -2620,6 +2633,9 @@
|
|||
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetPid.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.GetPid.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetSid.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.GetSid.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(CommonPath)Interop\FreeBSD\Interop.Process.GetProcInfo.cs" Condition="'$(TargetsFreeBSD)' == 'true'" Link="Common\Interop\FreeBSD\Interop.Process.GetProcInfo.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\BSD\System.Native\Interop.Sysctl.cs" Condition="'$(TargetsFreeBSD)' == 'true'" Link="Common\Interop\BSD\System.Native\Interop.Sysctl.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\Linux\procfs\Interop.ProcFsStat.TryReadStatusFile.cs" Condition="'$(TargetsLinux)' == 'true'" Link="Common\Interop\Linux\Interop.ProcFsStat.TryReadStatusFile.cs" />
|
||||
|
@ -2791,6 +2807,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\PortableThreadPool.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelLifoSemaphore.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelLifoSemaphore.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelLifoSemaphore.Unix.cs" Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true'" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\PreAllocatedOverlapped.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\PreAllocatedOverlapped.Unix.cs" Condition="'$(TargetsUnix)' == 'true' or ('$(TargetsBrowser)' == 'true' and '$(FeatureWasmManagedThreads)' == 'true')" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\PreAllocatedOverlapped.Portable.cs" Condition="('$(TargetsBrowser)' != 'true' and '$(TargetsWasi)' != 'true') or '$(FeatureWasmManagedThreads)' == 'true'" />
|
||||
|
@ -2822,7 +2839,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ObjectiveC\ObjectiveCMarshal.PlatformNotSupported.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ObjectiveC\ObjectiveCTrackedTypeAttribute.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(FeatureCoreCLR)' != 'true' and ('$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true')">
|
||||
<ItemGroup Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true'">
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.Unix.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\EventWaitHandle.Unix.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Mutex.Unix.cs" />
|
||||
|
@ -2833,6 +2850,13 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\WaitSubsystem.Unix.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\WaitSubsystem.WaitableObject.Unix.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetsUnix)' == 'true' and '$(FeatureCrossProcessMutex)' == 'true'">
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\IO\SharedMemoryManager.Unix.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\NamedMutex.Unix.cs" />
|
||||
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.LowLevelCrossProcessMutex.cs">
|
||||
<Link>Common\Interop\Unix\System.Native\Interop.LowLevelCrossProcessMutex.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(FeatureCoreCLR)' != 'true' and '$(TargetsWindows)' == 'true'">
|
||||
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\WaitHandle.Windows.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -56,6 +56,11 @@ namespace System.IO
|
|||
|
||||
// In initialization conditions, however, the "HOME" environment variable may
|
||||
// not yet be set. For such cases, consult with the password entry.
|
||||
return GetHomeDirectoryFromPasswd();
|
||||
}
|
||||
|
||||
internal static string GetHomeDirectoryFromPasswd()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
// First try with a buffer that should suffice for 99% of cases.
|
||||
|
@ -65,8 +70,8 @@ namespace System.IO
|
|||
// what to do.
|
||||
const int BufLen = Interop.Sys.Passwd.InitialBufferSize;
|
||||
byte* stackBuf = stackalloc byte[BufLen];
|
||||
if (TryGetHomeDirectoryFromPasswd(stackBuf, BufLen, out userHomeDirectory))
|
||||
return userHomeDirectory;
|
||||
if (TryGetHomeDirectoryFromPasswd(stackBuf, BufLen, out string? userHomeDirectory))
|
||||
return userHomeDirectory!;
|
||||
|
||||
// Fallback to heap allocations if necessary, growing the buffer until
|
||||
// we succeed. TryGetHomeDirectory will throw if there's an unexpected error.
|
||||
|
@ -78,7 +83,7 @@ namespace System.IO
|
|||
fixed (byte* buf = &heapBuf[0])
|
||||
{
|
||||
if (TryGetHomeDirectoryFromPasswd(buf, heapBuf.Length, out userHomeDirectory))
|
||||
return userHomeDirectory;
|
||||
return userHomeDirectory!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,835 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using System.Threading;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
internal readonly unsafe struct SharedMemoryId
|
||||
{
|
||||
private const string UserUnscopedRuntimeTempDirectoryName = ".dotnet";
|
||||
|
||||
private const string UserScopedRuntimeTempDirectoryName = ".dotnet-uid";
|
||||
|
||||
private const string SharedMemoryGlobalDirectoryName = "global";
|
||||
|
||||
private const string SharedMemorySessionDirectoryName = "session";
|
||||
private static int SessionId { get; } = Interop.Sys.GetSid(Environment.ProcessId);
|
||||
|
||||
public SharedMemoryId(string name, bool isUserScope)
|
||||
{
|
||||
if (name.StartsWith("Global\\", StringComparison.Ordinal))
|
||||
{
|
||||
IsSessionScope = false;
|
||||
name = name.Substring("Global\\".Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
IsSessionScope = true;
|
||||
if (name.StartsWith("Local\\", StringComparison.Ordinal))
|
||||
{
|
||||
name = name.Substring("Local\\".Length);
|
||||
}
|
||||
}
|
||||
|
||||
Name = name;
|
||||
|
||||
if (name.ContainsAny(['\\', '/']))
|
||||
{
|
||||
throw new ArgumentException($"Name '{name}' cannot contain path separators after prefixes.", nameof(name));
|
||||
}
|
||||
|
||||
IsUserScope = isUserScope;
|
||||
Uid = IsUserScope ? Interop.Sys.GetEUid() : 0;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public bool IsSessionScope { get; }
|
||||
public bool IsUserScope { get; }
|
||||
public uint Uid { get; }
|
||||
|
||||
internal readonly string GetRuntimeTempDirectoryName()
|
||||
{
|
||||
if (IsUserScope)
|
||||
{
|
||||
return $"{UserScopedRuntimeTempDirectoryName}{Uid}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return UserUnscopedRuntimeTempDirectoryName;
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly string GetSessionDirectoryName()
|
||||
{
|
||||
if (IsSessionScope)
|
||||
{
|
||||
return $"{SharedMemorySessionDirectoryName}{SessionId}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return SharedMemoryGlobalDirectoryName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum SharedMemoryType : byte
|
||||
{
|
||||
Mutex
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct SharedMemorySharedDataHeader
|
||||
{
|
||||
private struct SharedMemoryAndVersion
|
||||
{
|
||||
public SharedMemoryType Type;
|
||||
public byte Version;
|
||||
}
|
||||
|
||||
[FieldOffset(0)]
|
||||
private SharedMemoryAndVersion _data;
|
||||
|
||||
[FieldOffset(0)]
|
||||
private ulong _raw;
|
||||
|
||||
public readonly SharedMemoryType Type => _data.Type;
|
||||
public readonly byte Version => _data.Version;
|
||||
|
||||
public SharedMemorySharedDataHeader(SharedMemoryType type, byte version)
|
||||
{
|
||||
_data = new SharedMemoryAndVersion
|
||||
{
|
||||
Type = type,
|
||||
Version = version
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal interface ISharedMemoryProcessData
|
||||
{
|
||||
void Close(bool releaseSharedData);
|
||||
}
|
||||
|
||||
internal sealed unsafe class SharedMemoryProcessDataHeader<TSharedMemoryProcessData>
|
||||
where TSharedMemoryProcessData : class, ISharedMemoryProcessData
|
||||
{
|
||||
internal readonly SharedMemoryId _id;
|
||||
internal TSharedMemoryProcessData? _processData;
|
||||
private readonly SafeFileHandle _fileHandle;
|
||||
private readonly SharedMemorySharedDataHeader* _sharedDataHeader;
|
||||
private readonly nuint _sharedDataTotalByteCount;
|
||||
private int _referenceCount = 1;
|
||||
|
||||
public SharedMemoryProcessDataHeader(SharedMemoryId id, SafeFileHandle fileHandle, SharedMemorySharedDataHeader* sharedDataHeader, nuint sharedDataTotalByteCount)
|
||||
{
|
||||
_id = id;
|
||||
_fileHandle = fileHandle;
|
||||
_sharedDataHeader = sharedDataHeader;
|
||||
_sharedDataTotalByteCount = sharedDataTotalByteCount;
|
||||
_processData = null; // Will be initialized later
|
||||
SharedMemoryManager<TSharedMemoryProcessData>.Instance.AddProcessDataHeader(this);
|
||||
}
|
||||
|
||||
public static void* GetDataPointer(SharedMemoryProcessDataHeader<TSharedMemoryProcessData>? processDataHeader)
|
||||
{
|
||||
return processDataHeader is null
|
||||
? null
|
||||
: (void*)((byte*)processDataHeader._sharedDataHeader + sizeof(SharedMemorySharedDataHeader));
|
||||
}
|
||||
|
||||
internal static SharedMemoryProcessDataHeader<TSharedMemoryProcessData>? CreateOrOpen(
|
||||
string name,
|
||||
bool isUserScope,
|
||||
SharedMemorySharedDataHeader requiredSharedDataHeader,
|
||||
nuint sharedMemoryDataSize,
|
||||
bool createIfNotExist,
|
||||
bool acquireLockIfCreated,
|
||||
out bool created,
|
||||
out AutoReleaseFileLock creationDeletionLockFileHandle)
|
||||
{
|
||||
created = false;
|
||||
|
||||
AutoReleaseFileLock placeholderAutoReleaseLock = new AutoReleaseFileLock(new SafeFileHandle());
|
||||
|
||||
creationDeletionLockFileHandle = placeholderAutoReleaseLock;
|
||||
SharedMemoryId id = new(name, isUserScope);
|
||||
|
||||
nuint sharedDataUsedByteCount = (nuint)sizeof(SharedMemorySharedDataHeader) + sharedMemoryDataSize;
|
||||
nuint sharedDataTotalByteCount = AlignUp(sharedDataUsedByteCount, (nuint)Interop.Sys.GetPageSize());
|
||||
|
||||
SharedMemoryProcessDataHeader<TSharedMemoryProcessData>? processDataHeader = SharedMemoryManager<TSharedMemoryProcessData>.Instance.FindProcessDataHeader(id);
|
||||
|
||||
if (processDataHeader is not null)
|
||||
{
|
||||
Debug.Assert(processDataHeader._sharedDataTotalByteCount == sharedDataTotalByteCount);
|
||||
processDataHeader.IncrementRefCount();
|
||||
return processDataHeader;
|
||||
}
|
||||
|
||||
creationDeletionLockFileHandle = SharedMemoryManager<TSharedMemoryProcessData>.Instance.AcquireCreationDeletionLockForId(id);
|
||||
|
||||
string sessionDirectory = Path.Combine(
|
||||
SharedMemoryHelpers.SharedFilesPath,
|
||||
id.GetRuntimeTempDirectoryName(),
|
||||
SharedMemoryManager<TSharedMemoryProcessData>.SharedMemorySharedMemoryDirectoryName,
|
||||
id.GetSessionDirectoryName()
|
||||
);
|
||||
|
||||
if (!SharedMemoryHelpers.EnsureDirectoryExists(sessionDirectory, id, isGlobalLockAcquired: true, createIfNotExist))
|
||||
{
|
||||
Debug.Assert(!createIfNotExist);
|
||||
return null;
|
||||
}
|
||||
|
||||
string sharedMemoryFilePath = Path.Combine(sessionDirectory, id.Name);
|
||||
|
||||
SafeFileHandle fileHandle = SharedMemoryHelpers.CreateOrOpenFile(sharedMemoryFilePath, id, createIfNotExist, out bool createdFile);
|
||||
if (fileHandle.IsInvalid)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
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(fileHandle, nonBlocking: true))
|
||||
{
|
||||
// The shared memory file is not being used, flag it as created so that its contents will be reinitialized
|
||||
Interop.Sys.FLock(fileHandle, Interop.Sys.LockOperations.LOCK_UN);
|
||||
if (!createIfNotExist)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
createdFile = true;
|
||||
clearContents = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (createdFile)
|
||||
{
|
||||
SetFileSize(fileHandle, sharedMemoryFilePath, (long)sharedDataTotalByteCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Interop.Sys.FStat(fileHandle, out Interop.Sys.FileStatus fileStatus) != 0)
|
||||
{
|
||||
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
|
||||
throw Interop.GetExceptionForIoErrno(error, sharedMemoryFilePath);
|
||||
}
|
||||
|
||||
if (fileStatus.Size < (long)sharedDataUsedByteCount)
|
||||
{
|
||||
throw new InvalidOperationException("Header mismatch");
|
||||
}
|
||||
|
||||
SetFileSize(fileHandle, sharedMemoryFilePath, (long)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(fileHandle, nonBlocking: true, exclusive: false))
|
||||
{
|
||||
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
|
||||
throw Interop.GetExceptionForIoErrno(errorInfo, sharedMemoryFilePath);
|
||||
}
|
||||
|
||||
using AutoReleaseFileLock autoReleaseFileLock = new(fileHandle);
|
||||
|
||||
using MemoryMappedFileHolder memory = SharedMemoryHelpers.MemoryMapFile(fileHandle, sharedDataTotalByteCount);
|
||||
|
||||
SharedMemorySharedDataHeader* sharedDataHeader = (SharedMemorySharedDataHeader*)memory.Pointer;
|
||||
if (createdFile)
|
||||
{
|
||||
if (clearContents)
|
||||
{
|
||||
NativeMemory.Clear(memory.Pointer, sharedDataTotalByteCount);
|
||||
}
|
||||
*sharedDataHeader = requiredSharedDataHeader;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sharedDataHeader->Type != requiredSharedDataHeader.Type ||
|
||||
sharedDataHeader->Version != requiredSharedDataHeader.Version)
|
||||
{
|
||||
throw new InvalidOperationException("Header mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
if (!createdFile)
|
||||
{
|
||||
creationDeletionLockFileHandle.Dispose();
|
||||
// Reset to the placeholder value to avoid returning a pre-disposed lock.
|
||||
creationDeletionLockFileHandle = placeholderAutoReleaseLock;
|
||||
}
|
||||
|
||||
processDataHeader = new SharedMemoryProcessDataHeader<TSharedMemoryProcessData>(
|
||||
id,
|
||||
fileHandle,
|
||||
sharedDataHeader,
|
||||
sharedDataTotalByteCount
|
||||
);
|
||||
|
||||
autoReleaseFileLock.SuppressRelease();
|
||||
memory.SuppressRelease();
|
||||
|
||||
if (createdFile)
|
||||
{
|
||||
created = true;
|
||||
}
|
||||
|
||||
return processDataHeader;
|
||||
|
||||
static nuint AlignUp(nuint value, nuint alignment)
|
||||
{
|
||||
nuint alignMask = alignment - 1;
|
||||
return (nuint)((value + alignMask) & ~alignMask);
|
||||
}
|
||||
|
||||
static void SetFileSize(SafeFileHandle fd, string path, long size)
|
||||
{
|
||||
if (Interop.Sys.FTruncate(fd, size) < 0)
|
||||
{
|
||||
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
|
||||
if (errorInfo.Error is not Interop.Error.EBADF and not Interop.Error.EINVAL)
|
||||
{
|
||||
// We know the file descriptor is valid and we know the size argument to FTruncate is correct,
|
||||
// so if EBADF or EINVAL is returned, it means we're dealing with a special file that can't be
|
||||
// truncated. Ignore the error in such cases; in all others, throw.
|
||||
throw Interop.GetExceptionForIoErrno(errorInfo, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void IncrementRefCount()
|
||||
{
|
||||
Debug.Assert(_referenceCount > 0, "Ref count should not be negative.");
|
||||
_referenceCount++;
|
||||
}
|
||||
|
||||
public void DecrementRefCount()
|
||||
{
|
||||
Debug.Assert(_referenceCount > 0, "Ref count should not be negative.");
|
||||
_referenceCount--;
|
||||
if (_referenceCount == 0)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
SharedMemoryManager<NamedMutexProcessDataBase>.Instance.VerifyCreationDeletionProcessLockIsLocked();
|
||||
SharedMemoryManager<TSharedMemoryProcessData>.Instance.RemoveProcessDataHeader(this);
|
||||
|
||||
using AutoReleaseFileLock autoReleaseFileLock = SharedMemoryManager<TSharedMemoryProcessData>.Instance.AcquireCreationDeletionLockForId(_id);
|
||||
|
||||
bool releaseSharedData = false;
|
||||
|
||||
try
|
||||
{
|
||||
Interop.Sys.FLock(_fileHandle, Interop.Sys.LockOperations.LOCK_UN);
|
||||
if (SharedMemoryHelpers.TryAcquireFileLock(_fileHandle, nonBlocking: true, exclusive: true))
|
||||
{
|
||||
// There's no one else using this mutex.
|
||||
// We can delete our shared data.
|
||||
releaseSharedData = true;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore the error, just don't release shared data.
|
||||
}
|
||||
|
||||
_processData?.Close(releaseSharedData);
|
||||
_processData = null;
|
||||
Interop.Sys.MUnmap((nint)_sharedDataHeader, _sharedDataTotalByteCount);
|
||||
_fileHandle.Dispose();
|
||||
|
||||
if (releaseSharedData)
|
||||
{
|
||||
string sessionDirectoryPath = Path.Combine(
|
||||
SharedMemoryHelpers.SharedFilesPath,
|
||||
_id.GetRuntimeTempDirectoryName(),
|
||||
SharedMemoryManager<TSharedMemoryProcessData>.SharedMemorySharedMemoryDirectoryName,
|
||||
_id.GetSessionDirectoryName()
|
||||
);
|
||||
|
||||
string sharedMemoryFilePath = Path.Combine(sessionDirectoryPath, _id.Name);
|
||||
|
||||
// Directly call the underlying functions here as this is best-effort.
|
||||
// If we fail to delete, we don't want an exception.
|
||||
Interop.Sys.Unlink(sharedMemoryFilePath);
|
||||
|
||||
Interop.Sys.RmDir(sessionDirectoryPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class SharedMemoryHelpers
|
||||
{
|
||||
private const UnixFileMode PermissionsMask_OwnerUser_ReadWrite = UnixFileMode.UserRead | UnixFileMode.UserWrite;
|
||||
private const UnixFileMode PermissionsMask_OwnerUser_ReadWriteExecute = PermissionsMask_OwnerUser_ReadWrite | UnixFileMode.UserExecute;
|
||||
private const UnixFileMode PermissionsMask_NonOwnerUsers_Write = UnixFileMode.GroupWrite | UnixFileMode.OtherWrite;
|
||||
private const UnixFileMode PermissionsMask_AllUsers_ReadWrite = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.OtherRead | UnixFileMode.OtherWrite;
|
||||
private const UnixFileMode PermissionsMask_AllUsers_ReadWriteExecute = PermissionsMask_AllUsers_ReadWrite | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute;
|
||||
private const UnixFileMode PermissionsMask_Sticky = UnixFileMode.StickyBit;
|
||||
|
||||
private const string SharedMemoryUniqueTempNameTemplate = ".dotnet.XXXXXX";
|
||||
|
||||
// See https://developer.apple.com/documentation/Foundation/FileManager/containerURL(forSecurityApplicationGroupIdentifier:)#App-Groups-in-macOS for details on this path.
|
||||
private const string ApplicationContainerBasePathSuffix = "/Library/Group Containers/";
|
||||
|
||||
public static string SharedFilesPath { get; } = InitalizeSharedFilesPath();
|
||||
private static string InitalizeSharedFilesPath()
|
||||
{
|
||||
if (OperatingSystem.IsApplePlatform())
|
||||
{
|
||||
string? applicationGroupId = Environment.GetEnvironmentVariable("DOTNET_SHARED_MEMORY_APPLICATION_GROUP_ID");
|
||||
if (applicationGroupId is not null)
|
||||
{
|
||||
string sharedFilesPath = Path.Combine(
|
||||
PersistedFiles.GetHomeDirectoryFromPasswd(),
|
||||
ApplicationContainerBasePathSuffix,
|
||||
applicationGroupId
|
||||
);
|
||||
|
||||
if (File.Exists(sharedFilesPath))
|
||||
{
|
||||
// If the path exists and is a file, throw an exception.
|
||||
// If it's a directory, or does not exist, callers can correctly handle it.
|
||||
throw new DirectoryNotFoundException();
|
||||
}
|
||||
|
||||
return sharedFilesPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsAndroid())
|
||||
{
|
||||
return "/data/local/tmp/";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "/tmp/";
|
||||
}
|
||||
}
|
||||
|
||||
internal static SafeFileHandle CreateOrOpenFile(string sharedMemoryFilePath, SharedMemoryId id, bool createIfNotExist, out bool createdFile)
|
||||
{
|
||||
SafeFileHandle fd = Interop.Sys.Open(sharedMemoryFilePath, Interop.Sys.OpenFlags.O_RDWR | Interop.Sys.OpenFlags.O_CLOEXEC, 0);
|
||||
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
|
||||
if (!fd.IsInvalid)
|
||||
{
|
||||
if (id.IsUserScope)
|
||||
{
|
||||
if (Interop.Sys.FStat(fd, out Interop.Sys.FileStatus fileStatus) != 0)
|
||||
{
|
||||
error = Interop.Sys.GetLastErrorInfo();
|
||||
fd.Dispose();
|
||||
throw Interop.GetExceptionForIoErrno(error, sharedMemoryFilePath);
|
||||
}
|
||||
|
||||
if (fileStatus.Uid != id.Uid)
|
||||
{
|
||||
fd.Dispose();
|
||||
throw new IOException(SR.Format(SR.IO_SharedMemory_FileNotOwnedByUid, sharedMemoryFilePath, id.Uid));
|
||||
}
|
||||
|
||||
if ((fileStatus.Mode & (int)PermissionsMask_AllUsers_ReadWriteExecute) != (int)PermissionsMask_OwnerUser_ReadWrite)
|
||||
{
|
||||
fd.Dispose();
|
||||
throw new IOException(SR.Format(SR.IO_SharedMemory_FilePermissionsIncorrect, sharedMemoryFilePath, PermissionsMask_OwnerUser_ReadWrite));
|
||||
}
|
||||
}
|
||||
createdFile = false;
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (error.Error == Interop.Error.ENAMETOOLONG)
|
||||
{
|
||||
throw new ArgumentException(SR.Arg_ArgumentException, "name");
|
||||
}
|
||||
|
||||
Debug.Assert(error.Error == Interop.Error.ENOENT);
|
||||
if (!createIfNotExist)
|
||||
{
|
||||
createdFile = false;
|
||||
return fd;
|
||||
}
|
||||
|
||||
fd.Dispose();
|
||||
|
||||
UnixFileMode permissionsMask = id.IsUserScope
|
||||
? PermissionsMask_OwnerUser_ReadWrite
|
||||
: PermissionsMask_AllUsers_ReadWrite;
|
||||
|
||||
fd = Interop.Sys.Open(
|
||||
sharedMemoryFilePath,
|
||||
Interop.Sys.OpenFlags.O_RDWR | Interop.Sys.OpenFlags.O_CLOEXEC | Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL,
|
||||
(int)permissionsMask);
|
||||
|
||||
if (fd.IsInvalid)
|
||||
{
|
||||
error = Interop.Sys.GetLastErrorInfo();
|
||||
throw Interop.GetExceptionForIoErrno(error, sharedMemoryFilePath);
|
||||
}
|
||||
|
||||
int result = Interop.Sys.FChMod(fd, (int)permissionsMask);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
error = Interop.Sys.GetLastErrorInfo();
|
||||
fd.Dispose();
|
||||
Interop.Sys.Unlink(sharedMemoryFilePath);
|
||||
throw Interop.GetExceptionForIoErrno(error, sharedMemoryFilePath);
|
||||
}
|
||||
|
||||
createdFile = true;
|
||||
return fd;
|
||||
}
|
||||
|
||||
internal static bool EnsureDirectoryExists(string directoryPath, SharedMemoryId id, bool isGlobalLockAcquired, bool createIfNotExist = true, bool isSystemDirectory = false)
|
||||
{
|
||||
UnixFileMode permissionsMask = id.IsUserScope
|
||||
? PermissionsMask_OwnerUser_ReadWriteExecute
|
||||
: PermissionsMask_AllUsers_ReadWriteExecute;
|
||||
|
||||
int statResult = Interop.Sys.Stat(directoryPath, out Interop.Sys.FileStatus fileStatus);
|
||||
|
||||
if (statResult != 0 && Interop.Sys.GetLastError() == Interop.Error.ENOENT)
|
||||
{
|
||||
if (!createIfNotExist)
|
||||
{
|
||||
// The directory does not exist and we are not allowed to create it.
|
||||
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)
|
||||
{
|
||||
#pragma warning disable CA1416 // Validate platform compatibility. This file is only included on Unix platforms.
|
||||
Directory.CreateDirectory(directoryPath, permissionsMask);
|
||||
#pragma warning restore CA1416 // Validate platform compatibility
|
||||
|
||||
try
|
||||
{
|
||||
FileSystem.SetUnixFileMode(directoryPath, permissionsMask);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Directory.Delete(directoryPath);
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string tempPath = Path.Combine(SharedFilesPath, SharedMemoryUniqueTempNameTemplate);
|
||||
|
||||
unsafe
|
||||
{
|
||||
byte* tempPathPtr = Utf8StringMarshaller.ConvertToUnmanaged(tempPath);
|
||||
if (Interop.Sys.MkdTemp(tempPathPtr) == null)
|
||||
{
|
||||
Utf8StringMarshaller.Free(tempPathPtr);
|
||||
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
|
||||
throw Interop.GetExceptionForIoErrno(error, tempPath);
|
||||
}
|
||||
// Convert the path back to get the substituted path.
|
||||
tempPath = Utf8StringMarshaller.ConvertToManaged(tempPathPtr)!;
|
||||
Utf8StringMarshaller.Free(tempPathPtr);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileSystem.SetUnixFileMode(tempPath, permissionsMask);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Directory.Delete(tempPath);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (Interop.Sys.Rename(tempPath, directoryPath) == 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.
|
||||
Directory.Delete(tempPath);
|
||||
statResult = Interop.Sys.Stat(directoryPath, out fileStatus);
|
||||
}
|
||||
|
||||
// If the path exists, check that it's a directory
|
||||
if (statResult != 0 || (fileStatus.Mode & Interop.Sys.FileTypes.S_IFDIR) == 0)
|
||||
{
|
||||
if (statResult != 0)
|
||||
{
|
||||
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
|
||||
if (error.Error != Interop.Error.ENOENT)
|
||||
{
|
||||
throw Interop.GetExceptionForIoErrno(error, directoryPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException(SR.Format(SR.IO_SharedMemory_PathExistsButNotDirectory, directoryPath));
|
||||
}
|
||||
}
|
||||
|
||||
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 ((fileStatus.Mode & (int)permissionsMask) == (int)permissionsMask
|
||||
&& (
|
||||
!id.IsUserScope ||
|
||||
(fileStatus.Mode & (int)PermissionsMask_Sticky) == (int)PermissionsMask_Sticky ||
|
||||
(fileStatus.Uid == id.Uid && (fileStatus.Mode & (int)PermissionsMask_NonOwnerUsers_Write) == 0)
|
||||
))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new IOException(SR.Format(SR.IO_SharedMemory_DirectoryPermissionsIncorrect, directoryPath, fileStatus.Uid, Convert.ToString(fileStatus.Mode, 8)));
|
||||
}
|
||||
|
||||
// For non-system directories (such as SharedFilesPath/UserUnscopedRuntimeTempDirectoryName),
|
||||
// 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 && fileStatus.Uid != id.Uid)
|
||||
{
|
||||
throw new IOException(SR.Format(SR.IO_SharedMemory_DirectoryNotOwnedByUid, directoryPath, id.Uid));
|
||||
}
|
||||
|
||||
// Verify the permissions, or try to change them if possible
|
||||
if ((fileStatus.Mode & (int)PermissionsMask_AllUsers_ReadWriteExecute) == (int)permissionsMask
|
||||
|| (createIfNotExist && Interop.Sys.ChMod(directoryPath, (int)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)
|
||||
{
|
||||
throw new IOException(SR.Format(SR.IO_SharedMemory_DirectoryPermissionsIncorrectUserScope, directoryPath, Convert.ToString(fileStatus.Mode, 8)));
|
||||
}
|
||||
|
||||
|
||||
// For user-unscoped directories, as a last resort, check that at least the owner user has full access.
|
||||
permissionsMask = PermissionsMask_OwnerUser_ReadWriteExecute;
|
||||
if ((fileStatus.Mode & (int)permissionsMask) != (int)permissionsMask)
|
||||
{
|
||||
throw new IOException(SR.Format(SR.IO_SharedMemory_DirectoryOwnerPermissionsIncorrect, directoryPath, Convert.ToString(fileStatus.Mode, 8)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static MemoryMappedFileHolder MemoryMapFile(SafeFileHandle fileHandle, nuint sharedDataTotalByteCount)
|
||||
{
|
||||
nint addr = Interop.Sys.MMap(
|
||||
0,
|
||||
sharedDataTotalByteCount,
|
||||
Interop.Sys.MemoryMappedProtections.PROT_READ | Interop.Sys.MemoryMappedProtections.PROT_WRITE,
|
||||
Interop.Sys.MemoryMappedFlags.MAP_SHARED,
|
||||
fileHandle,
|
||||
0);
|
||||
|
||||
if (addr == -1)
|
||||
{
|
||||
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
|
||||
throw Interop.GetExceptionForIoErrno(errorInfo, "Failed to memory map the file");
|
||||
}
|
||||
|
||||
return new MemoryMappedFileHolder(addr, sharedDataTotalByteCount);
|
||||
}
|
||||
|
||||
internal static bool TryAcquireFileLock(SafeFileHandle sharedLockFileHandle, bool nonBlocking, bool exclusive = true)
|
||||
{
|
||||
Interop.Sys.LockOperations lockOperation = exclusive ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH;
|
||||
if (nonBlocking)
|
||||
{
|
||||
lockOperation |= Interop.Sys.LockOperations.LOCK_NB;
|
||||
}
|
||||
int result = Interop.Sys.FLock(sharedLockFileHandle, lockOperation);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
|
||||
if (errorInfo.Error == Interop.Error.EWOULDBLOCK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
throw Interop.GetExceptionForIoErrno(errorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe ref struct MemoryMappedFileHolder(nint addr, nuint length)
|
||||
{
|
||||
private bool _suppressed;
|
||||
|
||||
public void SuppressRelease()
|
||||
{
|
||||
_suppressed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_suppressed)
|
||||
{
|
||||
Interop.Sys.MUnmap(addr, length);
|
||||
}
|
||||
}
|
||||
|
||||
public void* Pointer => (void*)addr;
|
||||
}
|
||||
|
||||
internal unsafe ref struct AutoReleaseFileLock(SafeFileHandle fd)
|
||||
{
|
||||
private bool _suppressed;
|
||||
|
||||
public void SuppressRelease()
|
||||
{
|
||||
_suppressed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_suppressed && !fd.IsInvalid)
|
||||
{
|
||||
Interop.Sys.FLock(fd, Interop.Sys.LockOperations.LOCK_UN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SharedMemoryManager<TSharedMemoryProcessData>
|
||||
where TSharedMemoryProcessData : class, ISharedMemoryProcessData
|
||||
{
|
||||
internal static SharedMemoryManager<TSharedMemoryProcessData> Instance { get; } = new SharedMemoryManager<TSharedMemoryProcessData>();
|
||||
|
||||
internal const string SharedMemorySharedMemoryDirectoryName = "shm";
|
||||
|
||||
private readonly LowLevelLock _creationDeletionProcessLock = new();
|
||||
private SafeFileHandle? _creationDeletionLockFileHandle;
|
||||
private readonly Dictionary<uint, SafeFileHandle> _uidToFileHandleMap = [];
|
||||
|
||||
public WaitSubsystem.LockHolder AcquireCreationDeletionProcessLock()
|
||||
{
|
||||
return new WaitSubsystem.LockHolder(_creationDeletionProcessLock);
|
||||
}
|
||||
|
||||
public void VerifyCreationDeletionProcessLockIsLocked()
|
||||
{
|
||||
_creationDeletionProcessLock.VerifyIsLocked();
|
||||
}
|
||||
|
||||
public AutoReleaseFileLock AcquireCreationDeletionLockForId(SharedMemoryId id)
|
||||
{
|
||||
_creationDeletionProcessLock.VerifyIsLocked();
|
||||
SafeFileHandle? fd = id.IsUserScope ? GetUserScopeCreationDeletionLockFileHandle(id.Uid) : _creationDeletionLockFileHandle;
|
||||
if (fd is null)
|
||||
{
|
||||
if (!SharedMemoryHelpers.EnsureDirectoryExists(SharedMemoryHelpers.SharedFilesPath, id, isGlobalLockAcquired: false, createIfNotExist: false, isSystemDirectory: true))
|
||||
{
|
||||
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
|
||||
throw Interop.GetExceptionForIoErrno(error, SharedMemoryHelpers.SharedFilesPath);
|
||||
}
|
||||
string runtimeTempDirectory = Path.Combine(
|
||||
SharedMemoryHelpers.SharedFilesPath,
|
||||
id.GetRuntimeTempDirectoryName());
|
||||
|
||||
SharedMemoryHelpers.EnsureDirectoryExists(runtimeTempDirectory, id, isGlobalLockAcquired: false);
|
||||
|
||||
string sharedMemoryDirectory = Path.Combine(
|
||||
runtimeTempDirectory,
|
||||
SharedMemorySharedMemoryDirectoryName);
|
||||
|
||||
SharedMemoryHelpers.EnsureDirectoryExists(sharedMemoryDirectory, id, isGlobalLockAcquired: false);
|
||||
|
||||
fd = Interop.Sys.Open(sharedMemoryDirectory, Interop.Sys.OpenFlags.O_RDONLY, 0);
|
||||
if (fd.IsInvalid)
|
||||
{
|
||||
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
|
||||
fd.Dispose();
|
||||
throw Interop.GetExceptionForIoErrno(error, sharedMemoryDirectory);
|
||||
}
|
||||
|
||||
if (id.IsUserScope)
|
||||
{
|
||||
_uidToFileHandleMap.Add(id.Uid, fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
_creationDeletionLockFileHandle = fd;
|
||||
}
|
||||
}
|
||||
|
||||
bool acquired = SharedMemoryHelpers.TryAcquireFileLock(fd, nonBlocking: true, exclusive: true);
|
||||
Debug.Assert(acquired);
|
||||
return new AutoReleaseFileLock(fd);
|
||||
|
||||
SafeFileHandle? GetUserScopeCreationDeletionLockFileHandle(uint uid)
|
||||
{
|
||||
_uidToFileHandleMap.TryGetValue(uid, out SafeFileHandle? fileHandle);
|
||||
return fileHandle;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<SharedMemoryId, SharedMemoryProcessDataHeader<TSharedMemoryProcessData>> _processDataHeaders = [];
|
||||
|
||||
public void AddProcessDataHeader(SharedMemoryProcessDataHeader<TSharedMemoryProcessData> processDataHeader)
|
||||
{
|
||||
VerifyCreationDeletionProcessLockIsLocked();
|
||||
_processDataHeaders[processDataHeader._id] = processDataHeader;
|
||||
}
|
||||
|
||||
public void RemoveProcessDataHeader(SharedMemoryProcessDataHeader<TSharedMemoryProcessData> processDataHeader)
|
||||
{
|
||||
VerifyCreationDeletionProcessLockIsLocked();
|
||||
_processDataHeaders.Remove(processDataHeader._id);
|
||||
}
|
||||
|
||||
public SharedMemoryProcessDataHeader<TSharedMemoryProcessData>? FindProcessDataHeader(SharedMemoryId id)
|
||||
{
|
||||
_processDataHeaders.TryGetValue(id, out SharedMemoryProcessDataHeader<TSharedMemoryProcessData>? header);
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,9 @@ namespace System.Threading
|
|||
/// </summary>
|
||||
internal sealed partial class LowLevelLifoSemaphore : IDisposable
|
||||
{
|
||||
private WaitSubsystem.WaitableObject _semaphore;
|
||||
// Declared nullable even though it is initialized in Create
|
||||
// as Roslyn doesn't see that it's set in Create and Create is called from all constructors.
|
||||
private WaitSubsystem.WaitableObject? _semaphore;
|
||||
|
||||
private void Create(int maximumSignalCount)
|
||||
{
|
||||
|
@ -24,12 +26,12 @@ namespace System.Threading
|
|||
|
||||
private bool WaitCore(int timeoutMs)
|
||||
{
|
||||
return WaitSubsystem.Wait(_semaphore, timeoutMs, false, true) == WaitHandle.WaitSuccess;
|
||||
return WaitSubsystem.Wait(_semaphore!, timeoutMs, false, true) == WaitHandle.WaitSuccess;
|
||||
}
|
||||
|
||||
private void ReleaseCore(int count)
|
||||
{
|
||||
WaitSubsystem.ReleaseSemaphore(_semaphore, count);
|
||||
WaitSubsystem.ReleaseSemaphore(_semaphore!, count);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -94,10 +94,6 @@ namespace System.Threading
|
|||
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)
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace System.Threading
|
|||
{
|
||||
name = BuildNameForOptions(name, options);
|
||||
|
||||
SafeWaitHandle? safeWaitHandle = WaitSubsystem.CreateNamedMutex(initiallyOwned, name, out createdNew);
|
||||
SafeWaitHandle? safeWaitHandle = WaitSubsystem.CreateNamedMutex(initiallyOwned, name, isUserScope: options.WasSpecified && options.CurrentUserOnly, out createdNew);
|
||||
if (safeWaitHandle == null)
|
||||
{
|
||||
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
|
||||
|
@ -44,7 +44,7 @@ namespace System.Threading
|
|||
|
||||
name = BuildNameForOptions(name, options);
|
||||
|
||||
OpenExistingResult status = WaitSubsystem.OpenNamedMutex(name, out SafeWaitHandle? safeWaitHandle);
|
||||
OpenExistingResult status = WaitSubsystem.OpenNamedMutex(name, isUserScope: options.WasSpecified && options.CurrentUserOnly, out SafeWaitHandle? safeWaitHandle);
|
||||
result = status == OpenExistingResult.Success ? new Mutex(safeWaitHandle!) : null;
|
||||
return status;
|
||||
}
|
||||
|
@ -62,11 +62,6 @@ namespace System.Threading
|
|||
name = name.Substring(NamedWaitHandleOptionsInternal.CurrentSessionPrefix.Length);
|
||||
}
|
||||
|
||||
if (options.WasSpecified && options.CurrentUserOnly)
|
||||
{
|
||||
name = @"User\" + name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,713 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace System.Threading
|
||||
{
|
||||
internal sealed class NamedMutexOwnershipChain(Thread thread)
|
||||
{
|
||||
private readonly Thread _thread = thread;
|
||||
private NamedMutexProcessDataBase? _head;
|
||||
|
||||
public void Add(NamedMutexProcessDataBase namedMutex)
|
||||
{
|
||||
SharedMemoryManager<NamedMutexProcessDataBase>.Instance.VerifyCreationDeletionProcessLockIsLocked();
|
||||
namedMutex.NextOwnedNamedMutex = _head;
|
||||
_head = namedMutex;
|
||||
}
|
||||
|
||||
public void Remove(NamedMutexProcessDataBase namedMutex)
|
||||
{
|
||||
SharedMemoryManager<NamedMutexProcessDataBase>.Instance.VerifyCreationDeletionProcessLockIsLocked();
|
||||
if (_head == namedMutex)
|
||||
{
|
||||
_head = namedMutex.NextOwnedNamedMutex;
|
||||
}
|
||||
else
|
||||
{
|
||||
NamedMutexProcessDataBase? previous = _head;
|
||||
while (previous?.NextOwnedNamedMutex != namedMutex)
|
||||
{
|
||||
previous = previous?.NextOwnedNamedMutex;
|
||||
}
|
||||
|
||||
if (previous is not null)
|
||||
{
|
||||
previous.NextOwnedNamedMutex = namedMutex.NextOwnedNamedMutex;
|
||||
}
|
||||
}
|
||||
|
||||
namedMutex.NextOwnedNamedMutex = null;
|
||||
}
|
||||
|
||||
public void Abandon()
|
||||
{
|
||||
WaitSubsystem.LockHolder scope = SharedMemoryManager<NamedMutexProcessDataBase>.Instance.AcquireCreationDeletionProcessLock();
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
NamedMutexProcessDataBase? namedMutex = _head;
|
||||
if (namedMutex == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
namedMutex.Abandon(this, _thread);
|
||||
Debug.Assert(_head != namedMutex);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class NamedMutexProcessDataBase(SharedMemoryProcessDataHeader<NamedMutexProcessDataBase> header) : ISharedMemoryProcessData
|
||||
{
|
||||
private const byte SyncSystemVersion = 1;
|
||||
protected const int PollLoopMaximumSleepMilliseconds = 100;
|
||||
protected const uint InvalidProcessId = unchecked((uint)-1);
|
||||
protected const uint InvalidThreadId = unchecked((uint)-1);
|
||||
|
||||
// Use PThread mutex-backed named mutexes on all platforms except Apple platforms.
|
||||
// macOS has support for the features we need in the pthread mutexes on arm64
|
||||
// but not in the Rosetta 2 x64 emulation layer.
|
||||
// Until Rosetta 2 is removed, we need to use the non-PThread mutexes on Apple platforms.
|
||||
// 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.
|
||||
private static bool UsePThreadMutexes => !OperatingSystem.IsApplePlatform() && !OperatingSystem.IsFreeBSD();
|
||||
|
||||
private readonly SharedMemoryProcessDataHeader<NamedMutexProcessDataBase> _processDataHeader = header;
|
||||
protected nuint _lockCount;
|
||||
private Thread? _lockOwnerThread;
|
||||
|
||||
public SharedMemoryId Id => _processDataHeader._id;
|
||||
|
||||
public NamedMutexProcessDataBase? NextOwnedNamedMutex { get; set; }
|
||||
|
||||
public bool IsLockOwnedByCurrentThread => IsLockOwnedByThreadInThisProcess(Thread.CurrentThread);
|
||||
protected abstract bool IsLockOwnedByThreadInThisProcess(Thread thread);
|
||||
public bool IsLockOwnedByAnyThreadInThisProcess => _lockOwnerThread is not null;
|
||||
|
||||
protected abstract void SetLockOwnerToCurrentThread();
|
||||
|
||||
public MutexTryAcquireLockResult TryAcquireLock(WaitSubsystem.ThreadWaitInfo waitInfo, int timeoutMilliseconds, ref WaitSubsystem.LockHolder holder)
|
||||
{
|
||||
SharedMemoryManager<NamedMutexProcessDataBase>.Instance.VerifyCreationDeletionProcessLockIsLocked();
|
||||
holder.Dispose();
|
||||
MutexTryAcquireLockResult result = AcquireLockCore(timeoutMilliseconds);
|
||||
|
||||
if (result == MutexTryAcquireLockResult.AcquiredLockRecursively)
|
||||
{
|
||||
return MutexTryAcquireLockResult.AcquiredLock;
|
||||
}
|
||||
|
||||
if (result == MutexTryAcquireLockResult.TimedOut)
|
||||
{
|
||||
// If the lock was not acquired, we don't have any more work to do.
|
||||
return result;
|
||||
}
|
||||
|
||||
holder = SharedMemoryManager<NamedMutexProcessDataBase>.Instance.AcquireCreationDeletionProcessLock();
|
||||
SetLockOwnerToCurrentThread();
|
||||
_lockCount = 1;
|
||||
_lockOwnerThread = waitInfo.Thread;
|
||||
// Add the ref count for the thread's wait info.
|
||||
_processDataHeader.IncrementRefCount();
|
||||
waitInfo.NamedMutexOwnershipChain.Add(this);
|
||||
|
||||
if (IsAbandoned)
|
||||
{
|
||||
IsAbandoned = false;
|
||||
result = MutexTryAcquireLockResult.AcquiredLockButMutexWasAbandoned;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ReleaseLock()
|
||||
{
|
||||
if (!IsLockOwnedByCurrentThread)
|
||||
{
|
||||
throw new InvalidOperationException(SR.Arg_InvalidOperationException_CannotReleaseUnownedMutex);
|
||||
}
|
||||
|
||||
--_lockCount;
|
||||
if (_lockCount != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WaitSubsystem.LockHolder scope = SharedMemoryManager<NamedMutexProcessDataBase>.Instance.AcquireCreationDeletionProcessLock();
|
||||
try
|
||||
{
|
||||
Thread.CurrentThread.WaitInfo.NamedMutexOwnershipChain.Remove(this);
|
||||
_lockOwnerThread = null;
|
||||
ReleaseLockCore();
|
||||
// Remove the refcount from the thread's wait info.
|
||||
_processDataHeader.DecrementRefCount();
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Abandon(NamedMutexOwnershipChain chain, Thread abandonedThread)
|
||||
{
|
||||
SharedMemoryManager<NamedMutexProcessDataBase>.Instance.VerifyCreationDeletionProcessLockIsLocked();
|
||||
|
||||
// This method can be called from one of two threads:
|
||||
// 1. The dead thread that owns the mutex.
|
||||
// In the first case, we know that no one else can acquire the lock, so we can safely
|
||||
// set the lock count to 0 and abandon the mutex.
|
||||
// 2. The finalizer thread after the owning thread has died.
|
||||
// In this case, its possible for the mutex to have been acquired by another thread
|
||||
// after the owning thread died if it is backed by a pthread mutex.
|
||||
// A pthread mutex doesn't need our Abandon() call to be abandoned, it will be automatically abandoned
|
||||
// when the owning thread dies.
|
||||
// In this case, we don't want to do anything. Our named mutex implementation will handle the abandonment
|
||||
// as part of acquisition.
|
||||
Debug.Assert(IsLockOwnedByCurrentThread || (!abandonedThread.IsAlive && Thread.CurrentThreadIsFinalizerThread()),
|
||||
"Abandon can only be called from the thread that owns the lock or from the finalizer thread when the owning thread is dead.");
|
||||
|
||||
if (!IsLockOwnedByThreadInThisProcess(abandonedThread))
|
||||
{
|
||||
Debug.Assert(Thread.CurrentThreadIsFinalizerThread());
|
||||
// Lock is owned by a different thread already, so we don't need to do anything.
|
||||
// Just remove it from the list of owned named mutexes.
|
||||
chain.Remove(this);
|
||||
return;
|
||||
}
|
||||
|
||||
IsAbandoned = true;
|
||||
|
||||
_lockCount = 0;
|
||||
|
||||
Debug.Assert(_lockOwnerThread is not null);
|
||||
|
||||
ReleaseLockCore();
|
||||
|
||||
chain.Remove(this);
|
||||
_lockOwnerThread = null;
|
||||
// Remove the refcount from the thread's wait info.
|
||||
_processDataHeader.DecrementRefCount();
|
||||
}
|
||||
|
||||
protected abstract MutexTryAcquireLockResult AcquireLockCore(int timeoutMilliseconds);
|
||||
|
||||
protected abstract bool IsAbandoned { get; set; }
|
||||
|
||||
protected abstract void ReleaseLockCore();
|
||||
|
||||
internal static unsafe SharedMemoryProcessDataHeader<NamedMutexProcessDataBase>? CreateOrOpen(string name, bool isUserScope, bool createIfNotExist, bool acquireLockIfCreated, out bool created)
|
||||
{
|
||||
WaitSubsystem.LockHolder creationDeletionProcessLock = SharedMemoryManager<NamedMutexProcessDataBase>.Instance.AcquireCreationDeletionProcessLock();
|
||||
try
|
||||
{
|
||||
|
||||
SharedMemoryProcessDataHeader<NamedMutexProcessDataBase>? processDataHeader = SharedMemoryProcessDataHeader<NamedMutexProcessDataBase>.CreateOrOpen(
|
||||
name,
|
||||
isUserScope,
|
||||
new SharedMemorySharedDataHeader(SharedMemoryType.Mutex, SyncSystemVersion),
|
||||
UsePThreadMutexes ? NamedMutexProcessDataWithPThreads.SharedDataSize : (nuint)sizeof(NamedMutexProcessDataNoPThreads.SharedData),
|
||||
createIfNotExist,
|
||||
acquireLockIfCreated,
|
||||
out created,
|
||||
out AutoReleaseFileLock creationDeletionLockFileScope);
|
||||
|
||||
if (processDataHeader is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (creationDeletionLockFileScope)
|
||||
{
|
||||
if (created)
|
||||
{
|
||||
InitializeSharedData(SharedMemoryProcessDataHeader<NamedMutexProcessDataBase>.GetDataPointer(processDataHeader));
|
||||
}
|
||||
|
||||
if (processDataHeader._processData is null)
|
||||
{
|
||||
if (UsePThreadMutexes)
|
||||
{
|
||||
processDataHeader._processData = new NamedMutexProcessDataWithPThreads(processDataHeader);
|
||||
}
|
||||
else
|
||||
{
|
||||
processDataHeader._processData = new NamedMutexProcessDataNoPThreads(processDataHeader, created);
|
||||
}
|
||||
|
||||
if (created && acquireLockIfCreated)
|
||||
{
|
||||
MutexTryAcquireLockResult acquireResult = processDataHeader._processData.TryAcquireLock(Thread.CurrentThread.WaitInfo, timeoutMilliseconds: 0, ref creationDeletionProcessLock);
|
||||
Debug.Assert(acquireResult == MutexTryAcquireLockResult.AcquiredLock);
|
||||
}
|
||||
}
|
||||
|
||||
return processDataHeader;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
creationDeletionProcessLock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void InitializeSharedData(void* v)
|
||||
{
|
||||
if (UsePThreadMutexes)
|
||||
{
|
||||
if (Interop.Sys.LowLevelCrossProcessMutex_Init(v) != 0)
|
||||
{
|
||||
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
|
||||
throw Interop.GetExceptionForIoErrno(errorInfo, SR.Arg_FailedToInitializePThreadMutex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NamedMutexProcessDataNoPThreads.SharedData* sharedData = (NamedMutexProcessDataNoPThreads.SharedData*)v;
|
||||
sharedData->LockOwnerProcessId = InvalidProcessId;
|
||||
sharedData->LockOwnerThreadId = InvalidThreadId;
|
||||
sharedData->IsAbandoned = false;
|
||||
sharedData->TimedWaiterCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Close(bool releaseSharedData)
|
||||
{
|
||||
if (IsLockOwnedByCurrentThread)
|
||||
{
|
||||
Thread.CurrentThread.WaitInfo.NamedMutexOwnershipChain.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed unsafe class NamedMutexProcessDataWithPThreads(SharedMemoryProcessDataHeader<NamedMutexProcessDataBase> processDataHeader) : NamedMutexProcessDataBase(processDataHeader)
|
||||
{
|
||||
public static nuint SharedDataSize { get; } = (nuint)Interop.Sys.LowLevelCrossProcessMutex_Size();
|
||||
private readonly void* _sharedData = SharedMemoryProcessDataHeader<NamedMutexProcessDataBase>.GetDataPointer(processDataHeader);
|
||||
|
||||
protected override bool IsLockOwnedByThreadInThisProcess(Thread thread)
|
||||
{
|
||||
Interop.Sys.LowLevelCrossProcessMutex_GetOwnerProcessAndThreadId(_sharedData, out uint ownerProcessId, out uint ownerThreadId);
|
||||
return ownerProcessId == (uint)Environment.ProcessId &&
|
||||
ownerThreadId == (uint)thread.ManagedThreadId;
|
||||
}
|
||||
|
||||
protected override void SetLockOwnerToCurrentThread()
|
||||
{
|
||||
Interop.Sys.LowLevelCrossProcessMutex_SetOwnerProcessAndThreadId(
|
||||
_sharedData,
|
||||
(uint)Environment.ProcessId,
|
||||
(uint)Thread.CurrentThread.ManagedThreadId);
|
||||
}
|
||||
|
||||
protected override bool IsAbandoned
|
||||
{
|
||||
get => Interop.Sys.LowLevelCrossProcessMutex_IsAbandoned(_sharedData);
|
||||
set => Interop.Sys.LowLevelCrossProcessMutex_SetAbandoned(_sharedData, value);
|
||||
}
|
||||
|
||||
protected override MutexTryAcquireLockResult AcquireLockCore(int timeoutMilliseconds)
|
||||
{
|
||||
Interop.Error lockResult = (Interop.Error)Interop.Sys.LowLevelCrossProcessMutex_Acquire(_sharedData, timeoutMilliseconds);
|
||||
|
||||
MutexTryAcquireLockResult result = lockResult switch
|
||||
{
|
||||
Interop.Error.SUCCESS => MutexTryAcquireLockResult.AcquiredLock,
|
||||
Interop.Error.EBUSY => MutexTryAcquireLockResult.TimedOut,
|
||||
Interop.Error.ETIMEDOUT => MutexTryAcquireLockResult.TimedOut,
|
||||
Interop.Error.EOWNERDEAD => MutexTryAcquireLockResult.AcquiredLockButMutexWasAbandoned,
|
||||
Interop.Error.EAGAIN => throw new OutOfMemoryException(),
|
||||
_ => throw new Win32Exception((int)lockResult)
|
||||
};
|
||||
|
||||
if (result == MutexTryAcquireLockResult.TimedOut)
|
||||
{
|
||||
return MutexTryAcquireLockResult.TimedOut;
|
||||
}
|
||||
|
||||
if (result == MutexTryAcquireLockResult.AcquiredLockButMutexWasAbandoned)
|
||||
{
|
||||
// If the underlying pthread mutex was abandoned, treat this as an abandoned mutex.
|
||||
// Its possible that the underlying pthread mutex was abandoned, but the shared data and process data
|
||||
// is out of sync. This can happen if the dead thread did not call Abandon() before it exited
|
||||
// and is relying on the finalizer to clean it up.
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_lockCount != 0)
|
||||
{
|
||||
Debug.Assert(IsLockOwnedByCurrentThread);
|
||||
Debug.Assert(!IsAbandoned);
|
||||
try
|
||||
{
|
||||
checked
|
||||
{
|
||||
_lockCount++;
|
||||
}
|
||||
return MutexTryAcquireLockResult.AcquiredLockRecursively;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// The lock is released upon acquiring a recursive lock from the thread that already owns the lock
|
||||
Interop.Sys.LowLevelCrossProcessMutex_Release(_sharedData);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void ReleaseLockCore()
|
||||
{
|
||||
Debug.Assert(_lockCount == 0);
|
||||
Interop.Sys.LowLevelCrossProcessMutex_SetOwnerProcessAndThreadId(
|
||||
_sharedData,
|
||||
InvalidProcessId,
|
||||
InvalidThreadId);
|
||||
|
||||
Interop.Sys.LowLevelCrossProcessMutex_Release(_sharedData);
|
||||
}
|
||||
|
||||
public override void Close(bool releaseSharedData)
|
||||
{
|
||||
base.Close(releaseSharedData);
|
||||
|
||||
if (releaseSharedData)
|
||||
{
|
||||
// Release the pthread mutex.
|
||||
Interop.Sys.LowLevelCrossProcessMutex_Destroy(_sharedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed unsafe class NamedMutexProcessDataNoPThreads : NamedMutexProcessDataBase
|
||||
{
|
||||
private const string SharedMemoryLockFilesDirectoryName = "lockfiles";
|
||||
private readonly Mutex _processLevelMutex = new Mutex(initiallyOwned: false);
|
||||
private readonly SafeFileHandle _sharedLockFileHandle;
|
||||
|
||||
private readonly SharedData* _sharedData;
|
||||
|
||||
public NamedMutexProcessDataNoPThreads(SharedMemoryProcessDataHeader<NamedMutexProcessDataBase> processDataHeader, bool created) : base(processDataHeader)
|
||||
{
|
||||
_sharedData = (SharedData*)SharedMemoryProcessDataHeader<NamedMutexProcessDataBase>.GetDataPointer(processDataHeader);
|
||||
string lockFileDirectory = Path.Combine(
|
||||
SharedMemoryHelpers.SharedFilesPath,
|
||||
Id.GetRuntimeTempDirectoryName(),
|
||||
SharedMemoryLockFilesDirectoryName
|
||||
);
|
||||
|
||||
if (created)
|
||||
{
|
||||
SharedMemoryHelpers.EnsureDirectoryExists(lockFileDirectory, Id, isGlobalLockAcquired: true);
|
||||
}
|
||||
|
||||
string sessionDirectory = Path.Combine(lockFileDirectory, Id.GetSessionDirectoryName());
|
||||
|
||||
if (created)
|
||||
{
|
||||
SharedMemoryHelpers.EnsureDirectoryExists(sessionDirectory, Id, isGlobalLockAcquired: true);
|
||||
}
|
||||
|
||||
string lockFilePath = Path.Combine(sessionDirectory, Id.Name);
|
||||
_sharedLockFileHandle = SharedMemoryHelpers.CreateOrOpenFile(lockFilePath, Id, created, out _);
|
||||
}
|
||||
|
||||
protected override bool IsLockOwnedByThreadInThisProcess(Thread thread)
|
||||
{
|
||||
return _sharedData->LockOwnerProcessId == (uint)Environment.ProcessId &&
|
||||
_sharedData->LockOwnerThreadId == (uint)thread.ManagedThreadId;
|
||||
}
|
||||
|
||||
protected override void SetLockOwnerToCurrentThread()
|
||||
{
|
||||
_sharedData->LockOwnerProcessId = (uint)Environment.ProcessId;
|
||||
_sharedData->LockOwnerThreadId = (uint)Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
private bool IsLockOwnedByAnyThread
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sharedData->LockOwnerProcessId != InvalidProcessId &&
|
||||
_sharedData->LockOwnerThreadId != InvalidThreadId;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ReleaseLockCore()
|
||||
{
|
||||
Debug.Assert(_lockCount == 0);
|
||||
_sharedData->LockOwnerProcessId = InvalidProcessId;
|
||||
_sharedData->LockOwnerThreadId = InvalidThreadId;
|
||||
|
||||
Interop.Sys.FLock(_sharedLockFileHandle, Interop.Sys.LockOperations.LOCK_UN);
|
||||
_processLevelMutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
protected override bool IsAbandoned
|
||||
{
|
||||
get => _sharedData->IsAbandoned;
|
||||
set => _sharedData->IsAbandoned = value;
|
||||
}
|
||||
|
||||
public override void Close(bool releaseSharedData)
|
||||
{
|
||||
base.Close(releaseSharedData);
|
||||
|
||||
_sharedLockFileHandle.Dispose();
|
||||
_processLevelMutex.Dispose();
|
||||
}
|
||||
|
||||
protected override unsafe MutexTryAcquireLockResult AcquireLockCore(int timeoutMilliseconds)
|
||||
{
|
||||
int startTime = 0;
|
||||
if (timeoutMilliseconds > 0)
|
||||
{
|
||||
startTime = Environment.TickCount;
|
||||
}
|
||||
|
||||
// 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.
|
||||
bool releaseProcessLock = true;
|
||||
try
|
||||
{
|
||||
if (!_processLevelMutex.WaitOne(timeoutMilliseconds))
|
||||
{
|
||||
return MutexTryAcquireLockResult.TimedOut;
|
||||
}
|
||||
}
|
||||
catch (AbandonedMutexException)
|
||||
{
|
||||
// If the process-level lock was abandoned, the shared mutex will also have been abandoned.
|
||||
// We don't need to do anything here. We'll acquire the shared lock below and throw for abandonment as expected.
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_lockCount > 0)
|
||||
{
|
||||
Debug.Assert(IsLockOwnedByCurrentThread);
|
||||
// The lock is already owned by the current thread.
|
||||
checked
|
||||
{
|
||||
_lockCount++;
|
||||
}
|
||||
return MutexTryAcquireLockResult.AcquiredLockRecursively;
|
||||
}
|
||||
|
||||
switch (timeoutMilliseconds)
|
||||
{
|
||||
case -1:
|
||||
bool acquiredLock = false;
|
||||
while (_sharedData->TimedWaiterCount > 0)
|
||||
{
|
||||
if (SharedMemoryHelpers.TryAcquireFileLock(_sharedLockFileHandle, nonBlocking: true))
|
||||
{
|
||||
acquiredLock = true;
|
||||
break;
|
||||
}
|
||||
Thread.Sleep(PollLoopMaximumSleepMilliseconds);
|
||||
}
|
||||
|
||||
if (acquiredLock)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
acquiredLock = SharedMemoryHelpers.TryAcquireFileLock(_sharedLockFileHandle, nonBlocking: false);
|
||||
Debug.Assert(acquiredLock);
|
||||
break;
|
||||
case 0:
|
||||
if (!SharedMemoryHelpers.TryAcquireFileLock(_sharedLockFileHandle, nonBlocking: true))
|
||||
{
|
||||
return MutexTryAcquireLockResult.TimedOut;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (SharedMemoryHelpers.TryAcquireFileLock(_sharedLockFileHandle, nonBlocking: true))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_sharedData->TimedWaiterCount++;
|
||||
|
||||
do
|
||||
{
|
||||
int elapsedMilliseconds = Environment.TickCount - startTime;
|
||||
if (elapsedMilliseconds >= timeoutMilliseconds)
|
||||
{
|
||||
_sharedData->TimedWaiterCount--;
|
||||
return MutexTryAcquireLockResult.TimedOut;
|
||||
}
|
||||
|
||||
int remainingTimeoutMilliseconds = timeoutMilliseconds - elapsedMilliseconds;
|
||||
int sleepMilliseconds = Math.Min(PollLoopMaximumSleepMilliseconds, remainingTimeoutMilliseconds);
|
||||
Thread.Sleep(sleepMilliseconds);
|
||||
} while (!SharedMemoryHelpers.TryAcquireFileLock(_sharedLockFileHandle, nonBlocking: true));
|
||||
_sharedData->TimedWaiterCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
releaseProcessLock = false;
|
||||
|
||||
// Detect abandoned lock that isn't marked as abandoned.
|
||||
if (IsLockOwnedByAnyThread)
|
||||
return MutexTryAcquireLockResult.AcquiredLockButMutexWasAbandoned;
|
||||
|
||||
return MutexTryAcquireLockResult.AcquiredLock;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (releaseProcessLock)
|
||||
{
|
||||
_processLevelMutex.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal ref struct SharedData
|
||||
{
|
||||
private uint _timedWaiterCount;
|
||||
private uint _lockOwnerProcessId;
|
||||
private uint _lockOwnerThreadId;
|
||||
private byte _isAbandoned;
|
||||
|
||||
public uint TimedWaiterCount { get => _timedWaiterCount; set => _timedWaiterCount = value; }
|
||||
public uint LockOwnerProcessId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _lockOwnerProcessId;
|
||||
}
|
||||
set
|
||||
{
|
||||
_lockOwnerProcessId = value;
|
||||
}
|
||||
}
|
||||
|
||||
public uint LockOwnerThreadId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _lockOwnerThreadId;
|
||||
}
|
||||
set
|
||||
{
|
||||
_lockOwnerThreadId = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAbandoned
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isAbandoned != 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
_isAbandoned = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum MutexTryAcquireLockResult : byte
|
||||
{
|
||||
AcquiredLock,
|
||||
AcquiredLockButMutexWasAbandoned,
|
||||
TimedOut,
|
||||
AcquiredLockRecursively,
|
||||
}
|
||||
|
||||
internal static partial class WaitSubsystem
|
||||
{
|
||||
private sealed class NamedMutex : IWaitableObject
|
||||
{
|
||||
private readonly SharedMemoryProcessDataHeader<NamedMutexProcessDataBase> _processDataHeader;
|
||||
|
||||
public NamedMutex(SharedMemoryProcessDataHeader<NamedMutexProcessDataBase> processDataHeader)
|
||||
{
|
||||
_processDataHeader = processDataHeader;
|
||||
}
|
||||
|
||||
public int Wait_Locked(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool interruptible, bool prioritize, ref LockHolder lockHolder)
|
||||
{
|
||||
LockHolder scope = SharedMemoryManager<NamedMutexProcessDataBase>.Instance.AcquireCreationDeletionProcessLock();
|
||||
try
|
||||
{
|
||||
lockHolder.Dispose();
|
||||
MutexTryAcquireLockResult result = _processDataHeader._processData!.TryAcquireLock(waitInfo, timeoutMilliseconds, ref scope);
|
||||
return result switch
|
||||
{
|
||||
MutexTryAcquireLockResult.AcquiredLock => WaitHandle.WaitSuccess,
|
||||
MutexTryAcquireLockResult.AcquiredLockButMutexWasAbandoned => WaitHandle.WaitAbandoned,
|
||||
MutexTryAcquireLockResult.TimedOut => WaitHandle.WaitTimeout,
|
||||
_ => throw new InvalidOperationException("Unexpected result from TryAcquireLock")
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Signal(int count, ref LockHolder lockHolder)
|
||||
{
|
||||
lockHolder.Dispose();
|
||||
_processDataHeader._processData!.ReleaseLock();
|
||||
}
|
||||
|
||||
public void OnDeleteHandle()
|
||||
{
|
||||
LockHolder scope = SharedMemoryManager<NamedMutexProcessDataBase>.Instance.AcquireCreationDeletionProcessLock();
|
||||
try
|
||||
{
|
||||
_processDataHeader.DecrementRefCount();
|
||||
}
|
||||
finally
|
||||
{
|
||||
scope.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static NamedMutex? CreateNamedMutex(string name, bool isUserScope, bool initiallyOwned, out bool createdNew)
|
||||
{
|
||||
var namedMutexProcessData = NamedMutexProcessDataBase.CreateOrOpen(name, isUserScope, createIfNotExist: true, acquireLockIfCreated: initiallyOwned, out createdNew);
|
||||
if (namedMutexProcessData is null)
|
||||
{
|
||||
createdNew = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.Assert(namedMutexProcessData._processData is not null);
|
||||
return new NamedMutex(namedMutexProcessData);
|
||||
}
|
||||
|
||||
public static OpenExistingResult OpenNamedMutex(string name, bool isUserScope, out NamedMutex? result)
|
||||
{
|
||||
var namedMutexProcessData = NamedMutexProcessDataBase.CreateOrOpen(name, isUserScope, createIfNotExist: false, acquireLockIfCreated: false, out _);
|
||||
if (namedMutexProcessData is null)
|
||||
{
|
||||
result = null;
|
||||
return OpenExistingResult.NameNotFound;
|
||||
}
|
||||
|
||||
Debug.Assert(namedMutexProcessData._processData is not null);
|
||||
result = new NamedMutex(namedMutexProcessData);
|
||||
return OpenExistingResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -143,7 +143,6 @@ namespace System.Threading
|
|||
|
||||
if (!usedSyncContextWait)
|
||||
{
|
||||
#if !CORECLR // CoreCLR sends the wait events from the native side
|
||||
bool sendWaitEvents =
|
||||
millisecondsTimeout != 0 &&
|
||||
!useTrivialWaits &&
|
||||
|
@ -178,17 +177,14 @@ namespace System.Threading
|
|||
|
||||
// When tryNonblockingWaitFirst is true, we have a final wait result from the nonblocking wait above
|
||||
if (!tryNonblockingWaitFirst)
|
||||
#endif
|
||||
{
|
||||
waitResult = WaitOneCore(waitHandle.DangerousGetHandle(), millisecondsTimeout, useTrivialWaits);
|
||||
}
|
||||
|
||||
#if !CORECLR // CoreCLR sends the wait events from the native side
|
||||
if (sendWaitEvents)
|
||||
{
|
||||
NativeRuntimeEventSource.Log.WaitHandleWaitStop();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (waitResult == WaitAbandoned)
|
||||
|
@ -400,7 +396,6 @@ namespace System.Threading
|
|||
{
|
||||
int waitResult = WaitFailed;
|
||||
|
||||
#if !CORECLR // CoreCLR sends the wait events from the native side
|
||||
bool sendWaitEvents =
|
||||
millisecondsTimeout != 0 &&
|
||||
NativeRuntimeEventSource.Log.IsEnabled(
|
||||
|
@ -432,17 +427,14 @@ namespace System.Threading
|
|||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -10,9 +10,39 @@ namespace System.Threading
|
|||
{
|
||||
internal static partial class WaitSubsystem
|
||||
{
|
||||
public interface IWaitableObject
|
||||
{
|
||||
void OnDeleteHandle();
|
||||
int Wait_Locked(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool interruptible, bool prioritize, ref LockHolder lockHolder);
|
||||
void Signal(int count, ref LockHolder lockHolder);
|
||||
}
|
||||
|
||||
public static int Wait(this IWaitableObject waitable, ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool interruptible, bool prioritize)
|
||||
{
|
||||
Debug.Assert(waitInfo.Thread == Thread.CurrentThread);
|
||||
|
||||
Debug.Assert(timeoutMilliseconds >= -1);
|
||||
|
||||
var lockHolder = new LockHolder(s_lock);
|
||||
try
|
||||
{
|
||||
if (interruptible && waitInfo.CheckAndResetPendingInterrupt)
|
||||
{
|
||||
lockHolder.Dispose();
|
||||
throw new ThreadInterruptedException();
|
||||
}
|
||||
|
||||
return waitable.Wait_Locked(waitInfo, timeoutMilliseconds, interruptible, prioritize, ref lockHolder);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lockHolder.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static class HandleManager
|
||||
{
|
||||
public static IntPtr NewHandle(WaitableObject waitableObject)
|
||||
public static IntPtr NewHandle(IWaitableObject waitableObject)
|
||||
{
|
||||
Debug.Assert(waitableObject != null);
|
||||
|
||||
|
@ -24,7 +54,7 @@ namespace System.Threading
|
|||
return handle;
|
||||
}
|
||||
|
||||
public static WaitableObject FromHandle(IntPtr handle)
|
||||
public static IWaitableObject FromHandle(IntPtr handle)
|
||||
{
|
||||
if (handle == IntPtr.Zero || handle == new IntPtr(-1))
|
||||
{
|
||||
|
@ -33,7 +63,7 @@ namespace System.Threading
|
|||
|
||||
// We don't know if any other handles are invalid, and this may crash or otherwise do bad things, that is by
|
||||
// design, IntPtr is unsafe by nature.
|
||||
return (WaitableObject)GCHandle.FromIntPtr(handle).Target!;
|
||||
return (IWaitableObject)GCHandle.FromIntPtr(handle).Target!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace System.Threading
|
||||
{
|
||||
|
@ -86,6 +87,15 @@ namespace System.Threading
|
|||
/// </summary>
|
||||
private WaitableObject? _lockedMutexesHead;
|
||||
|
||||
#if FEATURE_CROSS_PROCESS_MUTEX
|
||||
/// <summary>
|
||||
/// Linked list of named mutexes that are locked by the thread and need to be abandoned before the thread exits.
|
||||
/// The linked list has only a head and no tail, which means acquired mutexes are prepended and
|
||||
/// mutexes are abandoned in reverse order.
|
||||
/// </summary>
|
||||
private NamedMutexOwnershipChain? _namedMutexOwnershipChain;
|
||||
#endif
|
||||
|
||||
public ThreadWaitInfo(Thread thread)
|
||||
{
|
||||
Debug.Assert(thread != null);
|
||||
|
@ -553,10 +563,31 @@ namespace System.Threading
|
|||
}
|
||||
}
|
||||
|
||||
#if FEATURE_CROSS_PROCESS_MUTEX
|
||||
public NamedMutexOwnershipChain NamedMutexOwnershipChain
|
||||
{
|
||||
get
|
||||
{
|
||||
SharedMemoryManager<NamedMutexProcessDataBase>.Instance.VerifyCreationDeletionProcessLockIsLocked();
|
||||
return Volatile.Read(ref _namedMutexOwnershipChain) ?? AllocateOwnershipChain();
|
||||
|
||||
NamedMutexOwnershipChain AllocateOwnershipChain()
|
||||
{
|
||||
Interlocked.CompareExchange(ref _namedMutexOwnershipChain, new NamedMutexOwnershipChain(this._thread), null!);
|
||||
return _namedMutexOwnershipChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void OnThreadExiting()
|
||||
{
|
||||
// Abandon locked mutexes. Acquired mutexes are prepended to the linked list, so the mutexes are abandoned in
|
||||
// last-acquired-first-abandoned order.
|
||||
|
||||
#if FEATURE_CROSS_PROCESS_MUTEX
|
||||
_namedMutexOwnershipChain?.Abandon();
|
||||
#endif
|
||||
s_lock.Acquire();
|
||||
try
|
||||
{
|
||||
|
@ -576,6 +607,7 @@ namespace System.Threading
|
|||
{
|
||||
s_lock.Release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed class WaitedListNode
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
@ -43,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 <see cref="ThreadWaitInfo._waitedObjects"/>).
|
||||
/// - The waiter registers a <see cref="ThreadWaitInfo.WaitedListNode"/> with each <see cref="WaitableObject"/>
|
||||
|
@ -144,7 +142,10 @@ namespace System.Threading
|
|||
}
|
||||
}
|
||||
|
||||
private static SafeWaitHandle NewHandle(WaitableObject waitableObject)
|
||||
#pragma warning disable CA1859 // Change type of parameter 'waitableObject' from 'System.Threading.IWaitableObject' to 'System.Threading.WaitableObject' for improved performance
|
||||
// Some platforms have more implementations of IWaitableObject than just WaitableObject.
|
||||
private static SafeWaitHandle NewHandle(IWaitableObject waitableObject)
|
||||
#pragma warning restore CA1859 // Change type of parameter 'waitableObject' from 'System.Threading.IWaitableObject' to 'System.Threading.WaitableObject' for improved performance
|
||||
{
|
||||
var safeWaitHandle = new SafeWaitHandle();
|
||||
|
||||
|
@ -193,7 +194,26 @@ namespace System.Threading
|
|||
return safeWaitHandle;
|
||||
}
|
||||
|
||||
public static SafeWaitHandle? CreateNamedMutex(bool initiallyOwned, string name, out bool createdNew)
|
||||
#if FEATURE_CROSS_PROCESS_MUTEX
|
||||
public static SafeWaitHandle? CreateNamedMutex(bool initiallyOwned, string name, bool isUserScope, out bool createdNew)
|
||||
{
|
||||
NamedMutex? namedMutex = NamedMutex.CreateNamedMutex(name, isUserScope, initiallyOwned: initiallyOwned, out createdNew);
|
||||
if (namedMutex == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
SafeWaitHandle safeWaitHandle = NewHandle(namedMutex);
|
||||
return safeWaitHandle;
|
||||
}
|
||||
|
||||
public static OpenExistingResult OpenNamedMutex(string name, bool isUserScope, out SafeWaitHandle? result)
|
||||
{
|
||||
OpenExistingResult status = NamedMutex.OpenNamedMutex(name, isUserScope, out NamedMutex? mutex);
|
||||
result = status == OpenExistingResult.Success ? NewHandle(mutex!) : null;
|
||||
return status;
|
||||
}
|
||||
#else
|
||||
public static SafeWaitHandle? CreateNamedMutex(bool initiallyOwned, string name, bool isUserScope, out bool createdNew)
|
||||
{
|
||||
// For initially owned, newly created named mutexes, there is a potential race
|
||||
// between adding the mutex to the named object table and initially acquiring it.
|
||||
|
@ -202,6 +222,11 @@ namespace System.Threading
|
|||
LockHolder lockHolder = new LockHolder(s_lock);
|
||||
try
|
||||
{
|
||||
if (isUserScope)
|
||||
{
|
||||
name = $"User\\{name}";
|
||||
}
|
||||
|
||||
WaitableObject? waitableObject = WaitableObject.CreateNamedMutex_Locked(name, out createdNew);
|
||||
if (waitableObject == null)
|
||||
{
|
||||
|
@ -227,12 +252,18 @@ namespace System.Threading
|
|||
}
|
||||
}
|
||||
|
||||
public static OpenExistingResult OpenNamedMutex(string name, out SafeWaitHandle? result)
|
||||
public static OpenExistingResult OpenNamedMutex(string name, bool isUserScope, out SafeWaitHandle? result)
|
||||
{
|
||||
if (isUserScope)
|
||||
{
|
||||
name = $"User\\{name}";
|
||||
}
|
||||
|
||||
OpenExistingResult status = WaitableObject.OpenNamedMutex(name, out WaitableObject? mutex);
|
||||
result = status == OpenExistingResult.Success ? NewHandle(mutex!) : null;
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void DeleteHandle(IntPtr handle)
|
||||
{
|
||||
|
@ -241,7 +272,7 @@ namespace System.Threading
|
|||
|
||||
public static void SetEvent(IntPtr handle)
|
||||
{
|
||||
SetEvent(HandleManager.FromHandle(handle));
|
||||
SetEvent((WaitableObject)HandleManager.FromHandle(handle));
|
||||
}
|
||||
|
||||
public static void SetEvent(WaitableObject waitableObject)
|
||||
|
@ -261,7 +292,7 @@ namespace System.Threading
|
|||
|
||||
public static void ResetEvent(IntPtr handle)
|
||||
{
|
||||
ResetEvent(HandleManager.FromHandle(handle));
|
||||
ResetEvent((WaitableObject)HandleManager.FromHandle(handle));
|
||||
}
|
||||
|
||||
public static void ResetEvent(WaitableObject waitableObject)
|
||||
|
@ -282,7 +313,7 @@ namespace System.Threading
|
|||
public static int ReleaseSemaphore(IntPtr handle, int count)
|
||||
{
|
||||
Debug.Assert(count > 0);
|
||||
return ReleaseSemaphore(HandleManager.FromHandle(handle), count);
|
||||
return ReleaseSemaphore((WaitableObject)HandleManager.FromHandle(handle), count);
|
||||
}
|
||||
|
||||
public static int ReleaseSemaphore(WaitableObject waitableObject, int count)
|
||||
|
@ -306,14 +337,14 @@ namespace System.Threading
|
|||
ReleaseMutex(HandleManager.FromHandle(handle));
|
||||
}
|
||||
|
||||
public static void ReleaseMutex(WaitableObject waitableObject)
|
||||
public static void ReleaseMutex(IWaitableObject waitableObject)
|
||||
{
|
||||
Debug.Assert(waitableObject != null);
|
||||
|
||||
LockHolder lockHolder = new LockHolder(s_lock);
|
||||
try
|
||||
{
|
||||
waitableObject.SignalMutex(ref lockHolder);
|
||||
waitableObject.Signal(1, ref lockHolder);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -328,7 +359,7 @@ namespace System.Threading
|
|||
}
|
||||
|
||||
public static int Wait(
|
||||
WaitableObject waitableObject,
|
||||
IWaitableObject waitableObject,
|
||||
int timeoutMilliseconds,
|
||||
bool interruptible = true,
|
||||
bool prioritize = false)
|
||||
|
@ -349,14 +380,30 @@ namespace System.Threading
|
|||
Debug.Assert(timeoutMilliseconds >= -1);
|
||||
|
||||
ThreadWaitInfo waitInfo = Thread.CurrentThread.WaitInfo;
|
||||
|
||||
#if FEATURE_CROSS_PROCESS_MUTEX
|
||||
if (waitHandles.Length == 1 && HandleManager.FromHandle(waitHandles[0]) is NamedMutex namedMutex)
|
||||
{
|
||||
// Named mutexes don't participate in the wait subsystem fully.
|
||||
return namedMutex.Wait(waitInfo, timeoutMilliseconds, interruptible: true, prioritize: false);
|
||||
}
|
||||
#endif
|
||||
|
||||
WaitableObject?[] waitableObjects = waitInfo.GetWaitedObjectArray(waitHandles.Length);
|
||||
bool success = false;
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < waitHandles.Length; ++i)
|
||||
{
|
||||
Debug.Assert(waitHandles[i] != IntPtr.Zero);
|
||||
WaitableObject waitableObject = HandleManager.FromHandle(waitHandles[i]);
|
||||
IWaitableObject waitableObjectMaybe = HandleManager.FromHandle(waitHandles[i]);
|
||||
|
||||
if (waitableObjectMaybe is not WaitableObject waitableObject)
|
||||
{
|
||||
throw new ArgumentException("Only unnamed waitable objects are supported in multi-wait operations.", nameof(waitHandles));
|
||||
}
|
||||
|
||||
if (waitForAll)
|
||||
{
|
||||
// Check if this is a duplicate, as wait-for-all does not support duplicates. Including the parent
|
||||
|
@ -421,8 +468,8 @@ namespace System.Threading
|
|||
}
|
||||
|
||||
public static int SignalAndWait(
|
||||
WaitableObject waitableObjectToSignal,
|
||||
WaitableObject waitableObjectToWaitOn,
|
||||
IWaitableObject waitableObjectToSignal,
|
||||
IWaitableObject waitableObjectToWaitOn,
|
||||
int timeoutMilliseconds,
|
||||
bool interruptible = true,
|
||||
bool prioritize = false)
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace System.Threading
|
|||
///
|
||||
/// Used by the wait subsystem on Unix, so this class cannot have any dependencies on the wait subsystem.
|
||||
/// </summary>
|
||||
public sealed class WaitableObject
|
||||
public sealed class WaitableObject : IWaitableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary to look up named waitable objects. This implementation only supports in-process
|
||||
|
@ -301,30 +301,6 @@ namespace System.Threading
|
|||
}
|
||||
}
|
||||
|
||||
public int Wait(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool interruptible, bool prioritize)
|
||||
{
|
||||
Debug.Assert(waitInfo != null);
|
||||
Debug.Assert(waitInfo.Thread == Thread.CurrentThread);
|
||||
|
||||
Debug.Assert(timeoutMilliseconds >= -1);
|
||||
|
||||
var lockHolder = new LockHolder(s_lock);
|
||||
try
|
||||
{
|
||||
if (interruptible && waitInfo.CheckAndResetPendingInterrupt)
|
||||
{
|
||||
lockHolder.Dispose();
|
||||
throw new ThreadInterruptedException();
|
||||
}
|
||||
|
||||
return Wait_Locked(waitInfo, timeoutMilliseconds, interruptible, prioritize, ref lockHolder);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lockHolder.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function does not check for a pending thread interrupt. Callers are expected to do that soon after
|
||||
/// acquiring <see cref="s_lock"/>.
|
||||
|
|
|
@ -24,11 +24,6 @@ namespace System.Threading.Tests
|
|||
if (PlatformDetection.IsMobile)
|
||||
return false;
|
||||
|
||||
// Cross-process named mutex support is not implemented on NativeAOT and Mono
|
||||
// [ActiveIssue("https://github.com/dotnet/runtime/issues/48720")]
|
||||
if (PlatformDetection.IsMonoRuntime || PlatformDetection.IsNativeAot)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -928,7 +923,7 @@ namespace System.Threading.Tests
|
|||
}
|
||||
return createdNew;
|
||||
}
|
||||
});
|
||||
}, ThreadTestHelpers.UnexpectedTimeoutMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -261,9 +261,6 @@
|
|||
<Link>CommonSystem\Experimentals.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="('$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true' or '$(TargetsWasi)' == 'true') and '$(FeaturePortableThreadPool)' == 'true'">
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\LowLevelLifoSemaphore.Unix.Mono.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetsBrowser)' == 'true' and '$(FeatureWasmManagedThreads)' == 'true'">
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\ThreadPoolBoundHandle.Browser.Threads.Mono.cs" />
|
||||
<Compile Include="$(BclSourcesRoot)\System\Threading\PortableThreadPool.Browser.Threads.Mono.cs" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue