This commit is contained in:
Jeremy Koritzinsky 2025-07-30 10:04:19 -04:00 committed by GitHub
commit 6e61722bd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
119 changed files with 2396 additions and 12929 deletions

View File

@ -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" />

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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
{

View File

@ -64,8 +64,6 @@ nativeStringResourceTable_mscorrc
#CreateFileMappingW
#CreateFileA
#CreateFileW
#CreateMutexW
#CreateMutexExW
#CreateEventW
#CreateEventExW
#CreateProcessW

View File

@ -262,3 +262,9 @@ FCIMPL0(OBJECTREF, RhpGetNextFinalizableObject)
}
}
FCIMPLEND
FCIMPL0(FC_BOOL_RET, RhpCurrentThreadIsFinalizerThread)
{
FC_RETURN_BOOL(ThreadStore::GetCurrentThread() == g_pFinalizerThread);
}
FCIMPLEND

View File

@ -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">

View File

@ -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();

View File

@ -517,5 +517,10 @@ namespace System.Threading
}
s_allDone.WaitOne();
}
internal static bool CurrentThreadIsFinalizerThread()
{
return RuntimeImports.RhpCurrentThreadIsFinalizerThread();
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -75,8 +75,7 @@ CObjectType CorUnix::otFile(
CFileProcessLocalDataCleanupRoutine,
CObjectType::UnwaitableObject,
CObjectType::SignalingNotApplicable,
CObjectType::ThreadReleaseNotApplicable,
CObjectType::OwnershipNotApplicable
CObjectType::ThreadReleaseNotApplicable
);
CAllowedObjectTypes CorUnix::aotFile(otiFile);

View File

@ -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. */

View File

@ -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(

View File

@ -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_

View File

@ -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_

View File

@ -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)

View File

@ -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>

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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
/*++

View File

@ -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[],

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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)).

View File

@ -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

View File

@ -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

View File

@ -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 );
}

View File

@ -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 );
}

View File

@ -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);
}

View File

@ -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 );
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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());

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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 )
{

View File

@ -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 );
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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:
{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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);

View File

@ -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.

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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!;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}

View File

@ -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>

View File

@ -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

View File

@ -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)

View File

@ -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"/>.

View File

@ -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);
}
}

View File

@ -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" />

View File

@ -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