diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 888f3f8b1498e0..20073739b2bbd0 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -290,6 +290,7 @@
+
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs
new file mode 100644
index 00000000000000..52233ffc583b14
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Mutex.CoreCLR.Unix.cs
@@ -0,0 +1,135 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Threading
+{
+ ///
+ /// Synchronization primitive that can also be used for interprocess synchronization
+ ///
+ public sealed partial class Mutex : WaitHandle
+ {
+ private void CreateMutexCore(bool initiallyOwned, string? name, out bool createdNew)
+ {
+ SafeWaitHandle mutexHandle = CreateMutexCore(initiallyOwned, name, 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, out Mutex? result)
+ {
+ ArgumentException.ThrowIfNullOrEmpty(name);
+
+ 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, 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,
+ out int errorCode,
+ out string? errorDetails)
+ {
+ byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize];
+ SafeWaitHandle mutexHandle = CreateMutex(initialOwner, name, 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, out int errorCode, out string? errorDetails)
+ {
+ byte* systemCallErrors = stackalloc byte[SystemCallErrorsBufferSize];
+ SafeWaitHandle mutexHandle = OpenMutex(name, systemCallErrors, SystemCallErrorsBufferSize);
+ errorCode = mutexHandle.IsInvalid ? Marshal.GetLastPInvokeError() : Interop.Errors.ERROR_SUCCESS;
+ errorDetails = mutexHandle.IsInvalid ? GetErrorDetails(systemCallErrors) : null;
+ return mutexHandle;
+ }
+
+ private static unsafe string? GetErrorDetails(byte* systemCallErrors)
+ {
+ int systemCallErrorsLength =
+ new ReadOnlySpan(systemCallErrors, SystemCallErrorsBufferSize).IndexOf((byte)'\0');
+ if (systemCallErrorsLength > 0)
+ {
+ try
+ {
+ return
+ SR.Format(SR.Unix_SystemCallErrors, Encoding.UTF8.GetString(systemCallErrors, systemCallErrorsLength));
+ }
+ catch { } // avoid hiding the original error due to an error here
+ }
+
+ return null;
+ }
+
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_CreateMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
+ private static unsafe partial SafeWaitHandle CreateMutex([MarshalAs(UnmanagedType.Bool)] bool initialOwner, string? name, byte* systemCallErrors, uint systemCallErrorsBufferSize);
+
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "PAL_OpenMutexW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
+ private static unsafe partial SafeWaitHandle OpenMutex(string name, byte* systemCallErrors, uint systemCallErrorsBufferSize);
+ }
+}
diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h
index 2bf5b17344d3de..189db1ae5b4475 100644
--- a/src/coreclr/pal/inc/pal.h
+++ b/src/coreclr/pal/inc/pal.h
@@ -1048,6 +1048,15 @@ CreateMutexExW(
IN DWORD dwFlags,
IN DWORD dwDesiredAccess);
+PALIMPORT
+HANDLE
+PALAPI
+PAL_CreateMutexW(
+ IN BOOL bInitialOwner,
+ IN LPCWSTR lpName,
+ IN LPSTR lpSystemCallErrors,
+ IN DWORD dwSystemCallErrorsBufferSize);
+
// CreateMutexExW: dwFlags
#define CREATE_MUTEX_INITIAL_OWNER ((DWORD)0x1)
@@ -1061,6 +1070,14 @@ OpenMutexW(
IN BOOL bInheritHandle,
IN LPCWSTR lpName);
+PALIMPORT
+HANDLE
+PALAPI
+PAL_OpenMutexW(
+ IN LPCWSTR lpName,
+ IN LPSTR lpSystemCallErrors,
+ IN DWORD dwSystemCallErrorsBufferSize);
+
#ifdef UNICODE
#define OpenMutex OpenMutexW
#endif
diff --git a/src/coreclr/pal/src/configure.cmake b/src/coreclr/pal/src/configure.cmake
index 304bc3461e9f3f..5366932629c628 100644
--- a/src/coreclr/pal/src/configure.cmake
+++ b/src/coreclr/pal/src/configure.cmake
@@ -133,6 +133,7 @@ check_function_exists(semget HAS_SYSV_SEMAPHORES)
check_function_exists(pthread_mutex_init HAS_PTHREAD_MUTEXES)
check_function_exists(ttrace HAVE_TTRACE)
check_function_exists(pipe2 HAVE_PIPE2)
+check_function_exists(strerrorname_np HAVE_STRERRORNAME_NP)
check_cxx_source_compiles("
#include
diff --git a/src/coreclr/pal/src/include/pal/mutex.hpp b/src/coreclr/pal/src/include/pal/mutex.hpp
index 464f0f72afb454..016668dafb1624 100644
--- a/src/coreclr/pal/src/include/pal/mutex.hpp
+++ b/src/coreclr/pal/src/include/pal/mutex.hpp
@@ -32,6 +32,7 @@ namespace CorUnix
PAL_ERROR
InternalCreateMutex(
+ SharedMemorySystemCallErrors *errors,
CPalThread *pThread,
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
@@ -47,6 +48,7 @@ namespace CorUnix
PAL_ERROR
InternalOpenMutex(
+ SharedMemorySystemCallErrors *errors,
CPalThread *pThread,
LPCSTR lpName,
HANDLE *phMutex
@@ -151,10 +153,10 @@ enum class MutexTryAcquireLockResult
class MutexHelpers
{
public:
- static void InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *mutex);
+ static void InitializeProcessSharedRobustRecursiveMutex(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex);
static void DestroyMutex(pthread_mutex_t *mutex);
- static MutexTryAcquireLockResult TryAcquireLock(pthread_mutex_t *mutex, DWORD timeoutMilliseconds);
+ static MutexTryAcquireLockResult TryAcquireLock(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex, DWORD timeoutMilliseconds);
static void ReleaseLock(pthread_mutex_t *mutex);
};
#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
@@ -172,7 +174,7 @@ class NamedMutexSharedData
bool m_isAbandoned;
public:
- NamedMutexSharedData();
+ NamedMutexSharedData(SharedMemorySystemCallErrors *errors);
~NamedMutexSharedData();
#if NAMED_MUTEX_USE_PTHREAD_MUTEX
@@ -214,10 +216,10 @@ class NamedMutexProcessData : public SharedMemoryProcessDataBase
bool m_hasRefFromLockOwnerThread;
public:
- static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef);
- static SharedMemoryProcessDataHeader *Open(LPCSTR name);
+ static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, bool acquireLockIfCreated, bool *createdRef);
+ static SharedMemoryProcessDataHeader *Open(SharedMemorySystemCallErrors *errors, LPCSTR name);
private:
- static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, bool createIfNotExist, bool acquireLockIfCreated, bool *createdRef);
+ static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, bool createIfNotExist, bool acquireLockIfCreated, bool *createdRef);
public:
NamedMutexProcessData(
@@ -248,7 +250,7 @@ class NamedMutexProcessData : public SharedMemoryProcessDataBase
void SetNextInThreadOwnedNamedMutexList(NamedMutexProcessData *next);
public:
- MutexTryAcquireLockResult TryAcquireLock(DWORD timeoutMilliseconds);
+ MutexTryAcquireLockResult TryAcquireLock(SharedMemorySystemCallErrors *errors, DWORD timeoutMilliseconds);
void ReleaseLock();
void Abandon();
private:
diff --git a/src/coreclr/pal/src/include/pal/sharedmemory.h b/src/coreclr/pal/src/include/pal/sharedmemory.h
index 88834b93d06738..fce6d2859cc05e 100644
--- a/src/coreclr/pal/src/include/pal/sharedmemory.h
+++ b/src/coreclr/pal/src/include/pal/sharedmemory.h
@@ -85,6 +85,19 @@ class SharedMemoryException
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 SharedMemoryHelpers
{
private:
@@ -106,20 +119,22 @@ class SharedMemoryHelpers
static void BuildSharedFilesPath(PathCharString& destination, const char *suffix, int suffixByteCount);
static bool AppendUInt32String(PathCharString& destination, UINT32 value);
- static bool EnsureDirectoryExists(const char *path, bool isGlobalLockAcquired, bool createIfNotExist = true, bool isSystemDirectory = false);
+ static bool EnsureDirectoryExists(SharedMemorySystemCallErrors *errors, const char *path, bool isGlobalLockAcquired, bool createIfNotExist = true, bool isSystemDirectory = false);
private:
- static int Open(LPCSTR path, int flags, mode_t mode = static_cast(0));
+ static int Open(SharedMemorySystemCallErrors *errors, LPCSTR path, int flags, mode_t mode = static_cast(0));
public:
- static int OpenDirectory(LPCSTR path);
- static int CreateOrOpenFile(LPCSTR path, bool createIfNotExist = true, bool *createdRef = nullptr);
+ static int OpenDirectory(SharedMemorySystemCallErrors *errors, LPCSTR path);
+ static int CreateOrOpenFile(SharedMemorySystemCallErrors *errors, LPCSTR path, bool createIfNotExist = true, bool *createdRef = nullptr);
static void CloseFile(int fileDescriptor);
- static SIZE_T GetFileSize(int fileDescriptor);
- static void SetFileSize(int fileDescriptor, SIZE_T byteCount);
+ 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(int fileDescriptor, SIZE_T byteCount);
+ static void *MemoryMapFile(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor, SIZE_T byteCount);
- static bool TryAcquireFileLock(int fileDescriptor, int operation);
+ static bool TryAcquireFileLock(SharedMemorySystemCallErrors *errors, int fileDescriptor, int operation);
static void ReleaseFileLock(int fileDescriptor);
static void VerifyStringOperation(bool success);
@@ -207,7 +222,7 @@ class SharedMemoryProcessDataHeader
SharedMemoryProcessDataHeader *m_nextInProcessDataHeaderList;
public:
- static SharedMemoryProcessDataHeader *CreateOrOpen(LPCSTR name, SharedMemorySharedDataHeader requiredSharedDataHeader, SIZE_T sharedDataByteCount, bool createIfNotExist, bool *createdRef);
+ static SharedMemoryProcessDataHeader *CreateOrOpen(SharedMemorySystemCallErrors *errors, LPCSTR name, SharedMemorySharedDataHeader requiredSharedDataHeader, SIZE_T sharedDataByteCount, bool createIfNotExist, bool *createdRef);
public:
static SharedMemoryProcessDataHeader *PalObject_GetProcessDataHeader(CorUnix::IPalObject *object);
@@ -260,7 +275,7 @@ class SharedMemoryManager
public:
static void AcquireCreationDeletionProcessLock();
static void ReleaseCreationDeletionProcessLock();
- static void AcquireCreationDeletionFileLock();
+ static void AcquireCreationDeletionFileLock(SharedMemorySystemCallErrors *errors);
static void ReleaseCreationDeletionFileLock();
public:
diff --git a/src/coreclr/pal/src/include/pal/utils.h b/src/coreclr/pal/src/include/pal/utils.h
index 83cf2b104c1ff9..fdd5b3b965a167 100644
--- a/src/coreclr/pal/src/include/pal/utils.h
+++ b/src/coreclr/pal/src/include/pal/utils.h
@@ -212,3 +212,5 @@ class StringHolder
};
#endif /* _PAL_UTILS_H_ */
+
+const char *GetFriendlyErrorCodeString(int errorCode);
diff --git a/src/coreclr/pal/src/misc/utils.cpp b/src/coreclr/pal/src/misc/utils.cpp
index f279ef3d580c14..0d96cc991305ae 100644
--- a/src/coreclr/pal/src/misc/utils.cpp
+++ b/src/coreclr/pal/src/misc/utils.cpp
@@ -366,3 +366,61 @@ BOOL IsRunningOnMojaveHardenedRuntime()
}
#endif // __APPLE__
+
+const char *GetFriendlyErrorCodeString(int errorCode)
+{
+#if HAVE_STRERRORNAME_NP
+ const char *error = strerrorname_np(errorCode);
+ if (error != nullptr)
+ {
+ return error;
+ }
+#else // !HAVE_STRERRORNAME_NP
+ switch (errorCode)
+ {
+ case EACCES: return "EACCES";
+ #if EAGAIN == EWOULDBLOCK
+ case EAGAIN: return "EAGAIN/EWOULDBLOCK";
+ #else
+ case EAGAIN: return "EAGAIN";
+ case EWOULDBLOCK: return "EWOULDBLOCK";
+ #endif
+ case EBADF: return "EBADF";
+ case EBUSY: return "EBUSY";
+ case EDQUOT: return "EDQUOT";
+ case EEXIST: return "EEXIST";
+ case EFAULT: return "EFAULT";
+ case EFBIG: return "EFBIG";
+ case EINVAL: return "EINVAL";
+ case EINTR: return "EINTR";
+ case EIO: return "EIO";
+ case EISDIR: return "EISDIR";
+ case ELOOP: return "ELOOP";
+ case EMFILE: return "EMFILE";
+ case EMLINK: return "EMLINK";
+ case ENAMETOOLONG: return "ENAMETOOLONG";
+ case ENFILE: return "ENFILE";
+ case ENODEV: return "ENODEV";
+ case ENOENT: return "ENOENT";
+ case ENOLCK: return "ENOLCK";
+ case ENOMEM: return "ENOMEM";
+ case ENOSPC: return "ENOSPC";
+ #if ENOTSUP == EOPNOTSUPP
+ case ENOTSUP: return "ENOTSUP/EOPNOTSUPP";
+ #else
+ case ENOTSUP: return "ENOTSUP";
+ case EOPNOTSUPP: return "EOPNOTSUPP";
+ #endif
+ case ENOTDIR: return "ENOTDIR";
+ case ENOTEMPTY: return "ENOTEMPTY";
+ case ENXIO: return "ENXIO";
+ case EOVERFLOW: return "EOVERFLOW";
+ case EPERM: return "EPERM";
+ case EROFS: return "EROFS";
+ case ETXTBSY: return "ETXTBSY";
+ case EXDEV: return "EXDEV";
+ }
+#endif // HAVE_STRERRORNAME_NP
+
+ return strerror(errorCode);
+}
diff --git a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp
index a2342f23efa5cf..ea5aae444dad00 100644
--- a/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp
+++ b/src/coreclr/pal/src/sharedmemory/sharedmemory.cpp
@@ -1,14 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#include "pal/dbgmsg.h"
+SET_DEFAULT_DEBUG_CHANNEL(SHMEM); // some headers have code with asserts, so do this first
+
#include "pal/sharedmemory.h"
-#include "pal/dbgmsg.h"
#include "pal/file.hpp"
#include "pal/malloc.hpp"
#include "pal/thread.hpp"
#include "pal/virtual.h"
#include "pal/process.h"
+#include "pal/utils.h"
#include
#include
@@ -23,8 +26,6 @@
using namespace CorUnix;
-SET_DEFAULT_DEBUG_CHANNEL(SHMEM);
-
#include "pal/sharedmemory.inl"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -59,6 +60,71 @@ DWORD SharedMemoryException::GetErrorCode() const
return m_errorCode;
}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SharedMemorySystemCallErrors
+
+SharedMemorySystemCallErrors::SharedMemorySystemCallErrors(char *buffer, int bufferSize)
+ : m_buffer(buffer), m_bufferSize(bufferSize), m_length(0), m_isTracking(bufferSize != 0)
+{
+ _ASSERTE((buffer == nullptr) == (bufferSize == 0));
+ _ASSERTE(bufferSize >= 0);
+}
+
+void SharedMemorySystemCallErrors::Append(LPCSTR format, ...)
+{
+ if (!m_isTracking)
+ {
+ return;
+ }
+
+ char *buffer = m_buffer;
+ _ASSERTE(buffer != nullptr);
+ int bufferSize = m_bufferSize;
+ _ASSERTE(bufferSize != 0);
+ int length = m_length;
+ _ASSERTE(length < bufferSize);
+ _ASSERTE(buffer[length] == '\0');
+ if (length >= bufferSize - 1)
+ {
+ return;
+ }
+
+ if (length != 0)
+ {
+ length++; // the previous null terminator will be changed to a space if the append succeeds
+ }
+
+ va_list args;
+ va_start(args, format);
+ int result = _vsnprintf_s(buffer + length, bufferSize - length, bufferSize - 1 - length, format, args);
+ va_end(args);
+
+ if (result == 0)
+ {
+ return;
+ }
+
+ if (result < 0 || result >= bufferSize - length)
+ {
+ // There's not enough space to append this error, discard the append and stop tracking
+ if (length == 0)
+ {
+ buffer[0] = '\0';
+ }
+ m_isTracking = false;
+ return;
+ }
+
+ if (length != 0)
+ {
+ buffer[length - 1] = ' '; // change the previous null terminator to a space
+ }
+
+ length += result;
+ _ASSERTE(buffer[length] == '\0');
+ m_length = length;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SharedMemoryHelpers
@@ -94,6 +160,7 @@ SIZE_T SharedMemoryHelpers::AlignUp(SIZE_T value, SIZE_T alignment)
}
bool SharedMemoryHelpers::EnsureDirectoryExists(
+ SharedMemorySystemCallErrors *errors,
const char *path,
bool isGlobalLockAcquired,
bool createIfNotExist,
@@ -123,15 +190,39 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
if (isGlobalLockAcquired)
{
- if (mkdir(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+ int operationResult = mkdir(path, PermissionsMask_AllUsers_ReadWriteExecute);
+ if (operationResult != 0)
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "mkdir(\"%s\", AllUsers_ReadWriteExecute) == %d; errno == %s;",
+ path,
+ operationResult,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
- if (chmod(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+
+ operationResult = ChangeMode(path, PermissionsMask_AllUsers_ReadWriteExecute);
+ if (operationResult != 0)
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "chmod(\"%s\", AllUsers_ReadWriteExecute) == %d; errno == %s;",
+ path,
+ operationResult,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
rmdir(path);
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
+
return true;
}
@@ -140,13 +231,35 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
if (mkdtemp(tempPath.OpenStringBuffer()) == nullptr)
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "mkdtemp(\"%s\") == nullptr; errno == %s;",
+ (const char *)tempPath,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
- if (chmod(tempPath, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+
+ int operationResult = ChangeMode(tempPath, PermissionsMask_AllUsers_ReadWriteExecute);
+ if (operationResult != 0)
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "chmod(\"%s\", AllUsers_ReadWriteExecute) == %d; errno == %s;",
+ (const char *)tempPath,
+ operationResult,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
rmdir(tempPath);
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
+
if (rename(tempPath, path) == 0)
{
return true;
@@ -161,6 +274,27 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
// If the path exists, check that it's a directory
if (statResult != 0 || !(statInfo.st_mode & S_IFDIR))
{
+ if (errors != nullptr)
+ {
+ if (statResult != 0)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "stat(\"%s\", ...) == %d; errno == %s;",
+ path,
+ statResult,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+ else
+ {
+ errors->Append(
+ "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & 0x%x) == 0;",
+ path,
+ (int)statInfo.st_mode,
+ (int)S_IFDIR);
+ }
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
@@ -176,6 +310,15 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
{
return true;
}
+
+ if (errors != nullptr)
+ {
+ errors->Append(
+ "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & CurrentUser_ReadWriteExecute) != CurrentUser_ReadWriteExecute;",
+ path,
+ (int)statInfo.st_mode);
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
@@ -186,19 +329,27 @@ bool SharedMemoryHelpers::EnsureDirectoryExists(
{
return true;
}
- if (!createIfNotExist || chmod(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
+ if (!createIfNotExist || ChangeMode(path, PermissionsMask_AllUsers_ReadWriteExecute) != 0)
{
// We were not asked to create the path or we weren't able to set the new permissions.
// As a last resort, check that at least the current user has full access.
if ((statInfo.st_mode & PermissionsMask_CurrentUser_ReadWriteExecute) != PermissionsMask_CurrentUser_ReadWriteExecute)
{
+ if (errors != nullptr)
+ {
+ errors->Append(
+ "stat(\"%s\", &info) == 0; info.st_mode == 0x%x; (info.st_mode & CurrentUser_ReadWriteExecute) != CurrentUser_ReadWriteExecute;",
+ path,
+ (int)statInfo.st_mode);
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
}
return true;
}
-int SharedMemoryHelpers::Open(LPCSTR path, int flags, mode_t mode)
+int SharedMemoryHelpers::Open(SharedMemorySystemCallErrors *errors, LPCSTR path, int flags, mode_t mode)
{
int openErrorCode;
@@ -213,6 +364,7 @@ int SharedMemoryHelpers::Open(LPCSTR path, int flags, mode_t mode)
openErrorCode = errno;
} while (openErrorCode == EINTR);
+ SharedMemoryError sharedMemoryError;
switch (openErrorCode)
{
case ENOENT:
@@ -221,29 +373,48 @@ int SharedMemoryHelpers::Open(LPCSTR path, int flags, mode_t mode)
return -1;
case ENAMETOOLONG:
- throw SharedMemoryException(static_cast(SharedMemoryError::NameTooLong));
+ sharedMemoryError = SharedMemoryError::NameTooLong;
+ break;
case EMFILE:
case ENFILE:
case ENOMEM:
- throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory));
+ sharedMemoryError = SharedMemoryError::OutOfMemory;
+ break;
default:
- throw SharedMemoryException(static_cast(SharedMemoryError::IO));
+ sharedMemoryError = SharedMemoryError::IO;
+ break;
}
+
+ if (sharedMemoryError != SharedMemoryError::NameTooLong && errors != nullptr)
+ {
+ errors->Append(
+ "open(\"%s\", 0x%x, 0x%x) == -1; errno == %s;",
+ path,
+ flags,
+ (int)mode,
+ GetFriendlyErrorCodeString(openErrorCode));
+ }
+
+ throw SharedMemoryException(static_cast(sharedMemoryError));
}
-int SharedMemoryHelpers::OpenDirectory(LPCSTR path)
+int SharedMemoryHelpers::OpenDirectory(SharedMemorySystemCallErrors *errors, LPCSTR path)
{
_ASSERTE(path != nullptr);
_ASSERTE(path[0] != '\0');
- int fileDescriptor = Open(path, O_RDONLY);
+ int fileDescriptor = Open(errors, path, O_RDONLY);
_ASSERTE(fileDescriptor != -1 || errno == ENOENT);
return fileDescriptor;
}
-int SharedMemoryHelpers::CreateOrOpenFile(LPCSTR path, bool createIfNotExist, bool *createdRef)
+int SharedMemoryHelpers::CreateOrOpenFile(
+ SharedMemorySystemCallErrors *errors,
+ LPCSTR path,
+ bool createIfNotExist,
+ bool *createdRef)
{
_ASSERTE(path != nullptr);
_ASSERTE(path[0] != '\0');
@@ -252,7 +423,7 @@ int SharedMemoryHelpers::CreateOrOpenFile(LPCSTR path, bool createIfNotExist, bo
// Try to open the file
int openFlags = O_RDWR;
- int fileDescriptor = Open(path, openFlags);
+ int fileDescriptor = Open(errors, path, openFlags);
if (fileDescriptor != -1)
{
if (createdRef != nullptr)
@@ -273,13 +444,24 @@ int SharedMemoryHelpers::CreateOrOpenFile(LPCSTR path, bool createIfNotExist, bo
// File does not exist, create the file
openFlags |= O_CREAT | O_EXCL;
- fileDescriptor = Open(path, openFlags, PermissionsMask_AllUsers_ReadWrite);
+ fileDescriptor = Open(errors, path, openFlags, PermissionsMask_AllUsers_ReadWrite);
_ASSERTE(fileDescriptor != -1);
// The permissions mask passed to open() is filtered by the process' permissions umask, so open() may not set all of
// the requested permissions. Use chmod() to set the proper permissions.
- if (chmod(path, PermissionsMask_AllUsers_ReadWrite) != 0)
+ int operationResult = ChangeMode(path, PermissionsMask_AllUsers_ReadWrite);
+ if (operationResult != 0)
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "chmod(\"%s\", AllUsers_ReadWrite) == %d; errno == %s;",
+ path,
+ operationResult,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
CloseFile(fileDescriptor);
unlink(path);
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
@@ -303,21 +485,54 @@ void SharedMemoryHelpers::CloseFile(int fileDescriptor)
} while (closeResult != 0 && errno == EINTR);
}
-SIZE_T SharedMemoryHelpers::GetFileSize(int fileDescriptor)
+int SharedMemoryHelpers::ChangeMode(LPCSTR path, mode_t mode)
{
+ _ASSERTE(path != nullptr);
+ _ASSERTE(path[0] != '\0');
+
+ int chmodResult;
+ do
+ {
+ chmodResult = chmod(path, mode);
+ } while (chmodResult != 0 && errno == EINTR);
+
+ return chmodResult;
+}
+
+SIZE_T SharedMemoryHelpers::GetFileSize(SharedMemorySystemCallErrors *errors, LPCSTR filePath, int fileDescriptor)
+{
+ _ASSERTE(filePath != nullptr);
+ _ASSERTE(filePath[0] != '\0');
_ASSERTE(fileDescriptor != -1);
off_t endOffset = lseek(fileDescriptor, 0, SEEK_END);
if (endOffset == static_cast(-1) ||
lseek(fileDescriptor, 0, SEEK_SET) == static_cast(-1))
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "lseek(\"%s\", 0, %s) == -1; errno == %s;",
+ filePath,
+ endOffset == (off_t)-1 ? "SEEK_END" : "SEEK_SET",
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
+
return endOffset;
}
-void SharedMemoryHelpers::SetFileSize(int fileDescriptor, SIZE_T byteCount)
+void SharedMemoryHelpers::SetFileSize(
+ SharedMemorySystemCallErrors *errors,
+ LPCSTR filePath,
+ int fileDescriptor,
+ SIZE_T byteCount)
{
+ _ASSERTE(filePath != nullptr);
+ _ASSERTE(filePath[0] != '\0');
_ASSERTE(fileDescriptor != -1);
_ASSERTE(static_cast(byteCount) == byteCount);
@@ -328,15 +543,33 @@ void SharedMemoryHelpers::SetFileSize(int fileDescriptor, SIZE_T byteCount)
{
break;
}
- if (errno != EINTR)
+
+ int errorCode = errno;
+ if (errorCode != EINTR)
{
+ if (errors != nullptr)
+ {
+ errors->Append(
+ "ftruncate(\"%s\", %zu) == %d; errno == %s;",
+ filePath,
+ byteCount,
+ ftruncateResult,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
}
}
-void *SharedMemoryHelpers::MemoryMapFile(int fileDescriptor, SIZE_T byteCount)
+void *SharedMemoryHelpers::MemoryMapFile(
+ SharedMemorySystemCallErrors *errors,
+ LPCSTR filePath,
+ int fileDescriptor,
+ SIZE_T byteCount)
{
+ _ASSERTE(filePath != nullptr);
+ _ASSERTE(filePath[0] != '\0');
_ASSERTE(fileDescriptor != -1);
_ASSERTE(byteCount > sizeof(SharedMemorySharedDataHeader));
_ASSERTE(AlignDown(byteCount, GetVirtualPageSize()) == byteCount);
@@ -346,32 +579,52 @@ void *SharedMemoryHelpers::MemoryMapFile(int fileDescriptor, SIZE_T byteCount)
{
return sharedMemoryBuffer;
}
- switch (errno)
+
+ int errorCode = errno;
+ SharedMemoryError sharedMemoryError;
+ switch (errorCode)
{
+ case EMFILE:
case ENFILE:
case ENOMEM:
- throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory));
+ sharedMemoryError = SharedMemoryError::OutOfMemory;
+ break;
default:
- throw SharedMemoryException(static_cast(SharedMemoryError::IO));
+ sharedMemoryError = SharedMemoryError::IO;
+ break;
}
+
+ if (errors != nullptr)
+ {
+ errors->Append(
+ "mmap(nullptr, %zu, PROT_READ | PROT_WRITE, MAP_SHARED, \"%s\", 0) == MAP_FAILED; errno == %s;",
+ byteCount,
+ filePath,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
+ throw SharedMemoryException(static_cast(sharedMemoryError));
}
-bool SharedMemoryHelpers::TryAcquireFileLock(int fileDescriptor, int operation)
+bool SharedMemoryHelpers::TryAcquireFileLock(SharedMemorySystemCallErrors *errors, int fileDescriptor, int operation)
{
// A file lock is acquired once per file descriptor, so the caller will need to synchronize threads of this process
_ASSERTE(fileDescriptor != -1);
+ _ASSERTE((operation & LOCK_EX) ^ (operation & LOCK_SH));
_ASSERTE(!(operation & LOCK_UN));
while (true)
{
- if (flock(fileDescriptor, operation) == 0)
+ int flockResult = flock(fileDescriptor, operation);
+ if (flockResult == 0)
{
return true;
}
int flockError = errno;
+ SharedMemoryError sharedMemoryError = SharedMemoryError::IO;
switch (flockError)
{
case EWOULDBLOCK:
@@ -380,9 +633,23 @@ bool SharedMemoryHelpers::TryAcquireFileLock(int fileDescriptor, int operation)
case EINTR:
continue;
- default:
- throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory));
+ case ENOLCK:
+ sharedMemoryError = SharedMemoryError::OutOfMemory;
+ break;
+ }
+
+ if (errors != nullptr)
+ {
+ errors->Append(
+ "flock(%d, %s%s) == %d; errno == %s;",
+ fileDescriptor,
+ operation & LOCK_EX ? "LOCK_EX" : "LOCK_SH",
+ operation & LOCK_NB ? " | LOCK_NB" : "",
+ flockResult,
+ GetFriendlyErrorCodeString(flockError));
}
+
+ throw SharedMemoryException(static_cast(sharedMemoryError));
}
}
@@ -558,6 +825,7 @@ void *SharedMemorySharedDataHeader::GetData()
// SharedMemoryProcessDataHeader
SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
+ SharedMemorySystemCallErrors *errors,
LPCSTR name,
SharedMemorySharedDataHeader requiredSharedDataHeader,
SIZE_T sharedDataByteCount,
@@ -657,14 +925,14 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
return processDataHeader;
}
- SharedMemoryManager::AcquireCreationDeletionFileLock();
+ SharedMemoryManager::AcquireCreationDeletionFileLock(errors);
autoCleanup.m_acquiredCreationDeletionFileLock = true;
// Create the session directory
SharedMemoryHelpers::VerifyStringOperation(SharedMemoryManager::CopySharedMemoryBasePath(filePath));
SharedMemoryHelpers::VerifyStringOperation(filePath.Append('/'));
SharedMemoryHelpers::VerifyStringOperation(id.AppendSessionDirectoryName(filePath));
- if (!SharedMemoryHelpers::EnsureDirectoryExists(filePath, true /* isGlobalLockAcquired */, createIfNotExist))
+ if (!SharedMemoryHelpers::EnsureDirectoryExists(errors, filePath, true /* isGlobalLockAcquired */, createIfNotExist))
{
_ASSERTE(!createIfNotExist);
return nullptr;
@@ -677,7 +945,7 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
SharedMemoryHelpers::VerifyStringOperation(filePath.Append(id.GetName(), id.GetNameCharCount()));
bool createdFile;
- int fileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(filePath, createIfNotExist, &createdFile);
+ int fileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(errors, filePath, createIfNotExist, &createdFile);
if (fileDescriptor == -1)
{
_ASSERTE(!createIfNotExist);
@@ -692,7 +960,7 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
// 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(fileDescriptor, LOCK_EX | LOCK_NB))
+ if (SharedMemoryHelpers::TryAcquireFileLock(errors, fileDescriptor, LOCK_EX | LOCK_NB))
{
// The shared memory file is not being used, flag it as created so that its contents will be reinitialized
SharedMemoryHelpers::ReleaseFileLock(fileDescriptor);
@@ -711,18 +979,18 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
SIZE_T sharedDataTotalByteCount = SharedMemorySharedDataHeader::GetTotalByteCount(sharedDataByteCount);
if (createdFile)
{
- SharedMemoryHelpers::SetFileSize(fileDescriptor, sharedDataTotalByteCount);
+ SharedMemoryHelpers::SetFileSize(errors, filePath, fileDescriptor, sharedDataTotalByteCount);
}
else
{
- SIZE_T currentFileSize = SharedMemoryHelpers::GetFileSize(fileDescriptor);
+ SIZE_T currentFileSize = SharedMemoryHelpers::GetFileSize(errors, filePath, fileDescriptor);
if (currentFileSize < sharedDataUsedByteCount)
{
throw SharedMemoryException(static_cast(SharedMemoryError::HeaderMismatch));
}
if (currentFileSize < sharedDataTotalByteCount)
{
- SharedMemoryHelpers::SetFileSize(fileDescriptor, sharedDataTotalByteCount);
+ SharedMemoryHelpers::SetFileSize(errors, filePath, fileDescriptor, sharedDataTotalByteCount);
}
}
@@ -730,14 +998,23 @@ SharedMemoryProcessDataHeader *SharedMemoryProcessDataHeader::CreateOrOpen(
// 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(fileDescriptor, LOCK_SH | LOCK_NB))
+ if (!SharedMemoryHelpers::TryAcquireFileLock(errors, fileDescriptor, LOCK_SH | LOCK_NB))
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "flock(\"%s\", LOCK_SH | LOCK_NB) == -1; errno == %s;",
+ (const char *)filePath,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
autoCleanup.m_acquiredFileLock = true;
// Map the file into memory, and initialize or validate the header
- void *mappedBuffer = SharedMemoryHelpers::MemoryMapFile(fileDescriptor, sharedDataTotalByteCount);
+ void *mappedBuffer = SharedMemoryHelpers::MemoryMapFile(errors, filePath, fileDescriptor, sharedDataTotalByteCount);
autoCleanup.m_mappedBuffer = mappedBuffer;
autoCleanup.m_mappedBufferByteCount = sharedDataTotalByteCount;
SharedMemorySharedDataHeader *sharedDataHeader;
@@ -926,11 +1203,11 @@ void SharedMemoryProcessDataHeader::Close()
bool releaseSharedData = false;
try
{
- SharedMemoryManager::AcquireCreationDeletionFileLock();
+ SharedMemoryManager::AcquireCreationDeletionFileLock(nullptr);
autoReleaseCreationDeletionFileLock.m_acquired = true;
SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor);
- if (SharedMemoryHelpers::TryAcquireFileLock(m_fileDescriptor, LOCK_EX | LOCK_NB))
+ if (SharedMemoryHelpers::TryAcquireFileLock(nullptr, m_fileDescriptor, LOCK_EX | LOCK_NB))
{
SharedMemoryHelpers::ReleaseFileLock(m_fileDescriptor);
releaseSharedData = true;
@@ -1142,7 +1419,7 @@ void SharedMemoryManager::ReleaseCreationDeletionProcessLock()
LeaveCriticalSection(&s_creationDeletionProcessLock);
}
-void SharedMemoryManager::AcquireCreationDeletionFileLock()
+void SharedMemoryManager::AcquireCreationDeletionFileLock(SharedMemorySystemCallErrors *errors)
{
_ASSERTE(IsCreationDeletionProcessLockAcquired());
_ASSERTE(!IsCreationDeletionFileLockAcquired());
@@ -1150,27 +1427,48 @@ void SharedMemoryManager::AcquireCreationDeletionFileLock()
if (s_creationDeletionLockFileDescriptor == -1)
{
if (!SharedMemoryHelpers::EnsureDirectoryExists(
+ errors,
*gSharedFilesPath,
false /* isGlobalLockAcquired */,
false /* createIfNotExist */,
true /* isSystemDirectory */))
{
+ _ASSERTE(errno == ENOENT);
+ if (errors != nullptr)
+ {
+ errors->Append("stat(\"%s\", ...) == -1; errno == ENOENT;", (const char *)*gSharedFilesPath);
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
+
SharedMemoryHelpers::EnsureDirectoryExists(
+ errors,
*s_runtimeTempDirectoryPath,
false /* isGlobalLockAcquired */);
+
SharedMemoryHelpers::EnsureDirectoryExists(
+ errors,
*s_sharedMemoryDirectoryPath,
false /* isGlobalLockAcquired */);
- s_creationDeletionLockFileDescriptor = SharedMemoryHelpers::OpenDirectory(*s_sharedMemoryDirectoryPath);
+
+ s_creationDeletionLockFileDescriptor = SharedMemoryHelpers::OpenDirectory(errors, *s_sharedMemoryDirectoryPath);
if (s_creationDeletionLockFileDescriptor == -1)
{
+ if (errors != nullptr)
+ {
+ int errorCode = errno;
+ errors->Append(
+ "open(\"%s\", O_RDONLY | O_CLOEXEC, 0) == -1; errno == %s;",
+ (const char *)*s_sharedMemoryDirectoryPath,
+ GetFriendlyErrorCodeString(errorCode));
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
}
- bool acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(s_creationDeletionLockFileDescriptor, LOCK_EX);
+ bool acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(errors, s_creationDeletionLockFileDescriptor, LOCK_EX);
_ASSERTE(acquiredFileLock);
#ifdef _DEBUG
s_creationDeletionFileLockOwnerThreadId = THREADSilentGetCurrentThreadId();
diff --git a/src/coreclr/pal/src/synchmgr/wait.cpp b/src/coreclr/pal/src/synchmgr/wait.cpp
index d666d5101ba7ad..5ae53759fa2be5 100644
--- a/src/coreclr/pal/src/synchmgr/wait.cpp
+++ b/src/coreclr/pal/src/synchmgr/wait.cpp
@@ -439,7 +439,7 @@ DWORD CorUnix::InternalWaitForMultipleObjectsEx(
try
{
MutexTryAcquireLockResult tryAcquireLockResult =
- static_cast(processDataHeader->GetData())->TryAcquireLock(dwMilliseconds);
+ static_cast(processDataHeader->GetData())->TryAcquireLock(nullptr, dwMilliseconds);
switch (tryAcquireLockResult)
{
case MutexTryAcquireLockResult::AcquiredLock:
diff --git a/src/coreclr/pal/src/synchobj/mutex.cpp b/src/coreclr/pal/src/synchobj/mutex.cpp
index 3fff2e7917fade..e3d63fe232b49f 100644
--- a/src/coreclr/pal/src/synchobj/mutex.cpp
+++ b/src/coreclr/pal/src/synchobj/mutex.cpp
@@ -23,6 +23,7 @@ SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do t
#include "pal/mutex.hpp"
#include "pal/file.hpp"
#include "pal/thread.hpp"
+#include "pal/utils.h"
#include "../synchmgr/synchmanager.hpp"
@@ -92,33 +93,71 @@ static CAllowedObjectTypes aotAnyMutex(anyMutexTypeIds, ARRAY_SIZE(anyMutexTypeI
Function:
CreateMutexW
+ See doc for PAL_CreateMutexW.
+--*/
+
+HANDLE
+PALAPI
+CreateMutexW(
+ IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
+ IN BOOL bInitialOwner,
+ IN LPCWSTR lpName)
+{
+ return PAL_CreateMutexW(bInitialOwner, lpName, nullptr, 0);
+}
+
+/*++
+Function:
+ PAL_CreateMutexW
+
Note:
lpMutexAttributes currently ignored:
-- Win32 object security not supported
-- handles to mutex objects are not inheritable
Parameters:
- See MSDN doc.
+ lpSystemCallErrors -- An optional buffer into which system call errors are written, for more detailed error information.
+ dwSystemCallErrorsBufferSize -- Size of the buffer pointed to by lpSystemCallErrors in bytes.
+
+ See MSDN docs on CreateMutexW for all other parameters.
--*/
HANDLE
PALAPI
-CreateMutexW(
- IN LPSECURITY_ATTRIBUTES lpMutexAttributes,
+PAL_CreateMutexW(
IN BOOL bInitialOwner,
- IN LPCWSTR lpName)
+ IN LPCWSTR lpName,
+ IN LPSTR lpSystemCallErrors,
+ IN DWORD dwSystemCallErrorsBufferSize)
{
HANDLE hMutex = NULL;
PAL_ERROR palError;
CPalThread *pthr = NULL;
char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1];
- PERF_ENTRY(CreateMutexW);
- ENTRY("CreateMutexW(lpMutexAttr=%p, bInitialOwner=%d, lpName=%p (%S)\n",
- lpMutexAttributes, bInitialOwner, lpName, lpName?lpName:W16_NULLSTRING);
+ PERF_ENTRY(PAL_CreateMutexW);
+ ENTRY("PAL_CreateMutexW(bInitialOwner=%d, lpName=%p (%S), lpSystemCallErrors=%p, dwSystemCallErrorsBufferSize=%d\n",
+ bInitialOwner,
+ lpName,
+ lpName?lpName:W16_NULLSTRING,
+ lpSystemCallErrors,
+ dwSystemCallErrorsBufferSize);
pthr = InternalGetCurrentThread();
+ /* validate parameters */
+ if ((int)dwSystemCallErrorsBufferSize < 0 || (lpSystemCallErrors == nullptr) != (dwSystemCallErrorsBufferSize == 0))
+ {
+ ERROR("One or more parameters are invalid\n");
+ palError = ERROR_INVALID_PARAMETER;
+ goto CreateMutexWExit;
+ }
+
+ if (lpSystemCallErrors != nullptr)
+ {
+ lpSystemCallErrors[0] = '\0';
+ }
+
if (lpName != nullptr)
{
int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr);
@@ -138,13 +177,17 @@ CreateMutexW(
}
}
- palError = InternalCreateMutex(
- pthr,
- lpMutexAttributes,
- bInitialOwner,
- lpName == nullptr ? nullptr : utf8Name,
- &hMutex
- );
+ {
+ SharedMemorySystemCallErrors errors(lpSystemCallErrors, (int)dwSystemCallErrorsBufferSize);
+ palError = InternalCreateMutex(
+ &errors,
+ pthr,
+ nullptr,
+ bInitialOwner,
+ lpName == nullptr ? nullptr : utf8Name,
+ &hMutex
+ );
+ }
CreateMutexWExit:
//
@@ -156,14 +199,14 @@ CreateMutexW(
pthr->SetLastError(palError);
- LOGEXIT("CreateMutexW returns HANDLE %p\n", hMutex);
- PERF_EXIT(CreateMutexW);
+ LOGEXIT("PAL_CreateMutexW returns HANDLE %p\n", hMutex);
+ PERF_EXIT(PAL_CreateMutexW);
return hMutex;
}
/*++
Function:
-CreateMutexW
+CreateMutexExW
Note:
lpMutexAttributes currently ignored:
@@ -195,14 +238,16 @@ CreateMutexExW(
-- handles to mutex objects are not inheritable
Parameters:
+ errors -- An optional wrapper for system call errors, for more detailed error information.
pthr -- thread data for calling thread
phEvent -- on success, receives the allocated mutex handle
- See MSDN docs on CreateMutex for all other parameters
+ See MSDN docs on CreateMutex for all other parameters.
--*/
PAL_ERROR
CorUnix::InternalCreateMutex(
+ SharedMemorySystemCallErrors *errors,
CPalThread *pthr,
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
@@ -286,7 +331,7 @@ CorUnix::InternalCreateMutex(
SharedMemoryProcessDataHeader *processDataHeader;
try
{
- processDataHeader = NamedMutexProcessData::CreateOrOpen(lpName, !!bInitialOwner, &createdNamedMutex);
+ processDataHeader = NamedMutexProcessData::CreateOrOpen(errors, lpName, !!bInitialOwner, &createdNamedMutex);
}
catch (SharedMemoryException ex)
{
@@ -512,7 +557,7 @@ OpenMutexA (
goto OpenMutexAExit;
}
- palError = InternalOpenMutex(pthr, lpName, &hMutex);
+ palError = InternalOpenMutex(nullptr, pthr, lpName, &hMutex);
OpenMutexAExit:
if (NO_ERROR != palError)
@@ -529,11 +574,8 @@ OpenMutexA (
Function:
OpenMutexW
-Note:
- dwDesiredAccess is currently ignored (no Win32 object security support)
- bInheritHandle is currently ignored (handles to mutexes are not inheritable)
-
-See MSDN doc.
+Parameters:
+ See doc for PAL_OpenMutexW.
--*/
HANDLE
@@ -542,26 +584,61 @@ OpenMutexW(
IN DWORD dwDesiredAccess,
IN BOOL bInheritHandle,
IN LPCWSTR lpName)
+{
+ return PAL_OpenMutexW(lpName, nullptr, 0);
+}
+
+/*++
+Function:
+ PAL_OpenMutexW
+
+Note:
+ dwDesiredAccess is currently ignored (no Win32 object security support)
+ bInheritHandle is currently ignored (handles to mutexes are not inheritable)
+
+Parameters:
+ lpSystemCallErrors -- An optional buffer into which system call errors are written, for more detailed error information.
+ dwSystemCallErrorsBufferSize -- Size of the buffer pointed to by lpSystemCallErrors in bytes.
+
+ See MSDN docs on OpenMutexW for all other parameters.
+--*/
+
+HANDLE
+PALAPI
+PAL_OpenMutexW(
+ IN LPCWSTR lpName,
+ IN LPSTR lpSystemCallErrors,
+ IN DWORD dwSystemCallErrorsBufferSize)
{
HANDLE hMutex = NULL;
PAL_ERROR palError = NO_ERROR;
CPalThread *pthr = NULL;
char utf8Name[SHARED_MEMORY_MAX_NAME_CHAR_COUNT + 1];
- PERF_ENTRY(OpenMutexW);
- ENTRY("OpenMutexW(dwDesiredAccess=%#x, bInheritHandle=%d, lpName=%p (%S))\n",
- dwDesiredAccess, bInheritHandle, lpName, lpName?lpName:W16_NULLSTRING);
+ PERF_ENTRY(PAL_OpenMutexW);
+ ENTRY("PAL_OpenMutexW(lpName=%p (%S), lpSystemCallErrors=%p, dwSystemCallErrorsBufferSize=%d)\n",
+ lpName,
+ lpName?lpName:W16_NULLSTRING,
+ lpSystemCallErrors,
+ dwSystemCallErrorsBufferSize);
pthr = InternalGetCurrentThread();
/* validate parameters */
- if (lpName == nullptr)
+ if (lpName == nullptr ||
+ (int)dwSystemCallErrorsBufferSize < 0 ||
+ (lpSystemCallErrors == nullptr) != (dwSystemCallErrorsBufferSize == 0))
{
- ERROR("name is NULL\n");
+ ERROR("name is NULL or other parameters are invalid\n");
palError = ERROR_INVALID_PARAMETER;
goto OpenMutexWExit;
}
+ if (lpSystemCallErrors != nullptr)
+ {
+ lpSystemCallErrors[0] = '\0';
+ }
+
{
int bytesWritten = WideCharToMultiByte(CP_ACP, 0, lpName, -1, utf8Name, ARRAY_SIZE(utf8Name), nullptr, nullptr);
if (bytesWritten == 0)
@@ -578,9 +655,10 @@ OpenMutexW(
}
goto OpenMutexWExit;
}
- }
- palError = InternalOpenMutex(pthr, lpName == nullptr ? nullptr : utf8Name, &hMutex);
+ SharedMemorySystemCallErrors errors(lpSystemCallErrors, (int)dwSystemCallErrorsBufferSize);
+ palError = InternalOpenMutex(&errors, pthr, lpName == nullptr ? nullptr : utf8Name, &hMutex);
+ }
OpenMutexWExit:
if (NO_ERROR != palError)
@@ -588,8 +666,8 @@ OpenMutexW(
pthr->SetLastError(palError);
}
- LOGEXIT("OpenMutexW returns HANDLE %p\n", hMutex);
- PERF_EXIT(OpenMutexW);
+ LOGEXIT("PAL_OpenMutexW returns HANDLE %p\n", hMutex);
+ PERF_EXIT(PAL_OpenMutexW);
return hMutex;
}
@@ -599,6 +677,7 @@ OpenMutexW(
InternalOpenMutex
Parameters:
+ errors -- An optional wrapper for system call errors, for more detailed error information.
pthr -- thread data for calling thread
phEvent -- on success, receives the allocated mutex handle
@@ -607,6 +686,7 @@ OpenMutexW(
PAL_ERROR
CorUnix::InternalOpenMutex(
+ SharedMemorySystemCallErrors *errors,
CPalThread *pthr,
LPCSTR lpName,
HANDLE *phMutex
@@ -645,7 +725,7 @@ CorUnix::InternalOpenMutex(
SharedMemoryProcessDataHeader *processDataHeader;
try
{
- processDataHeader = NamedMutexProcessData::Open(lpName);
+ processDataHeader = NamedMutexProcessData::Open(errors, lpName);
}
catch (SharedMemoryException ex)
{
@@ -746,7 +826,7 @@ DWORD SPINLOCKTryAcquire (LONG * lock)
// MutexHelpers
#if NAMED_MUTEX_USE_PTHREAD_MUTEX
-void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *mutex)
+void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(SharedMemorySystemCallErrors *errors, pthread_mutex_t *mutex)
{
_ASSERTE(mutex != nullptr);
@@ -772,6 +852,11 @@ void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *
int error = pthread_mutexattr_init(&mutexAttributes);
if (error != 0)
{
+ if (errors != nullptr)
+ {
+ errors->Append("pthread_mutexattr_init(...) == %s;", GetFriendlyErrorCodeString(error));
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory));
}
autoCleanup.m_mutexAttributes = &mutexAttributes;
@@ -788,6 +873,11 @@ void MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(pthread_mutex_t *
error = pthread_mutex_init(mutex, &mutexAttributes);
if (error != 0)
{
+ if (errors != nullptr)
+ {
+ errors->Append("pthread_mutex_init(...) == %s;", GetFriendlyErrorCodeString(error));
+ }
+
throw SharedMemoryException(static_cast(error == EPERM ? SharedMemoryError::IO : SharedMemoryError::OutOfMemory));
}
}
@@ -800,7 +890,10 @@ void MutexHelpers::DestroyMutex(pthread_mutex_t *mutex)
_ASSERTE(error == 0 || error == EBUSY); // the error will be EBUSY if the mutex is locked
}
-MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(pthread_mutex_t *mutex, DWORD timeoutMilliseconds)
+MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(
+ SharedMemorySystemCallErrors *errors,
+ pthread_mutex_t *mutex,
+ DWORD timeoutMilliseconds)
{
_ASSERTE(mutex != nullptr);
@@ -850,7 +943,19 @@ MutexTryAcquireLockResult MutexHelpers::TryAcquireLock(pthread_mutex_t *mutex, D
throw SharedMemoryException(static_cast(NamedMutexError::MaximumRecursiveLocksReached));
default:
+ {
+ if (errors != nullptr)
+ {
+ errors->Append(
+ "%s(...) == %s;",
+ timeoutMilliseconds == (DWORD)-1 ? "pthread_mutex_lock"
+ : timeoutMilliseconds == 0 ? "pthread_mutex_trylock"
+ : "pthread_mutex_timedlock",
+ GetFriendlyErrorCodeString(lockResult));
+ }
+
throw SharedMemoryException(static_cast(NamedMutexError::Unknown));
+ }
}
}
@@ -866,7 +971,7 @@ void MutexHelpers::ReleaseLock(pthread_mutex_t *mutex)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NamedMutexSharedData
-NamedMutexSharedData::NamedMutexSharedData()
+NamedMutexSharedData::NamedMutexSharedData(SharedMemorySystemCallErrors *errors)
:
#if !NAMED_MUTEX_USE_PTHREAD_MUTEX
m_timedWaiterCount(0),
@@ -883,7 +988,7 @@ NamedMutexSharedData::NamedMutexSharedData()
_ASSERTE(SharedMemoryManager::IsCreationDeletionFileLockAcquired());
#if NAMED_MUTEX_USE_PTHREAD_MUTEX
- MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(&m_lock);
+ MutexHelpers::InitializeProcessSharedRobustRecursiveMutex(errors, &m_lock);
#endif // NAMED_MUTEX_USE_PTHREAD_MUTEX
}
@@ -917,6 +1022,7 @@ void NamedMutexSharedData::IncTimedWaiterCount()
ULONG newValue = InterlockedIncrement(reinterpret_cast(&m_timedWaiterCount));
if (newValue == 0)
{
+ InterlockedDecrement(reinterpret_cast(&m_timedWaiterCount));
throw SharedMemoryException(static_cast(SharedMemoryError::OutOfMemory));
}
}
@@ -979,17 +1085,22 @@ const UINT8 NamedMutexProcessData::SyncSystemVersion = 1;
const DWORD NamedMutexProcessData::PollLoopMaximumSleepMilliseconds = 100;
-SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(LPCSTR name, bool acquireLockIfCreated, bool *createdRef)
+SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
+ SharedMemorySystemCallErrors *errors,
+ LPCSTR name,
+ bool acquireLockIfCreated,
+ bool *createdRef)
{
- return CreateOrOpen(name, true /* createIfNotExist */, acquireLockIfCreated, createdRef);
+ return CreateOrOpen(errors, name, true /* createIfNotExist */, acquireLockIfCreated, createdRef);
}
-SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(LPCSTR name)
+SharedMemoryProcessDataHeader *NamedMutexProcessData::Open(SharedMemorySystemCallErrors *errors, LPCSTR name)
{
- return CreateOrOpen(name, false /* createIfNotExist */, false /* acquireLockIfCreated */, nullptr /* createdRef */);
+ return CreateOrOpen(errors, name, false /* createIfNotExist */, false /* acquireLockIfCreated */, nullptr /* createdRef */);
}
SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
+ SharedMemorySystemCallErrors *errors,
LPCSTR name,
bool createIfNotExist,
bool acquireLockIfCreated,
@@ -1079,6 +1190,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
bool created;
SharedMemoryProcessDataHeader *processDataHeader =
SharedMemoryProcessDataHeader::CreateOrOpen(
+ errors,
name,
SharedMemorySharedDataHeader(SharedMemoryType::Mutex, SyncSystemVersion),
sizeof(NamedMutexSharedData),
@@ -1105,7 +1217,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
if (created)
{
// Initialize the shared data
- new(processDataHeader->GetSharedDataHeader()->GetData()) NamedMutexSharedData;
+ new(processDataHeader->GetSharedDataHeader()->GetData()) NamedMutexSharedData(errors);
}
if (processDataHeader->GetData() == nullptr)
@@ -1115,7 +1227,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
SharedMemoryHelpers::BuildSharedFilesPath(lockFilePath, SHARED_MEMORY_LOCK_FILES_DIRECTORY_NAME);
if (created)
{
- SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
+ SharedMemoryHelpers::EnsureDirectoryExists(errors, lockFilePath, true /* isGlobalLockAcquired */);
}
// Create the session directory
@@ -1124,7 +1236,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
SharedMemoryHelpers::VerifyStringOperation(id->AppendSessionDirectoryName(lockFilePath));
if (created)
{
- SharedMemoryHelpers::EnsureDirectoryExists(lockFilePath, true /* isGlobalLockAcquired */);
+ SharedMemoryHelpers::EnsureDirectoryExists(errors, lockFilePath, true /* isGlobalLockAcquired */);
autoCleanup.m_lockFilePath = &lockFilePath;
autoCleanup.m_sessionDirectoryPathCharCount = lockFilePath.GetCount();
}
@@ -1132,14 +1244,22 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
// Create or open the lock file
SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append('/'));
SharedMemoryHelpers::VerifyStringOperation(lockFilePath.Append(id->GetName(), id->GetNameCharCount()));
- int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(lockFilePath, created);
+ int lockFileDescriptor = SharedMemoryHelpers::CreateOrOpenFile(errors, lockFilePath, created);
if (lockFileDescriptor == -1)
{
_ASSERTE(!created);
if (createIfNotExist)
{
+ if (errors != nullptr)
+ {
+ errors->Append(
+ "open(\"%s\", O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0) == -1; errno == ENOENT;",
+ (const char *)lockFilePath);
+ }
+
throw SharedMemoryException(static_cast(SharedMemoryError::IO));
}
+
return nullptr;
}
autoCleanup.m_createdLockFile = created;
@@ -1164,7 +1284,7 @@ SharedMemoryProcessDataHeader *NamedMutexProcessData::CreateOrOpen(
// If the mutex was created and if requested, acquire the lock initially while holding the creation/deletion locks
if (created && acquireLockIfCreated)
{
- MutexTryAcquireLockResult tryAcquireLockResult = processData->TryAcquireLock(0);
+ MutexTryAcquireLockResult tryAcquireLockResult = processData->TryAcquireLock(errors, 0);
_ASSERTE(tryAcquireLockResult == MutexTryAcquireLockResult::AcquiredLock);
}
}
@@ -1331,12 +1451,12 @@ void NamedMutexProcessData::SetNextInThreadOwnedNamedMutexList(NamedMutexProcess
m_nextInThreadOwnedNamedMutexList = next;
}
-MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMilliseconds)
+MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(SharedMemorySystemCallErrors *errors, DWORD timeoutMilliseconds)
{
NamedMutexSharedData *sharedData = GetSharedData();
#if NAMED_MUTEX_USE_PTHREAD_MUTEX
- MutexTryAcquireLockResult result = MutexHelpers::TryAcquireLock(sharedData->GetLock(), timeoutMilliseconds);
+ MutexTryAcquireLockResult result = MutexHelpers::TryAcquireLock(errors, sharedData->GetLock(), timeoutMilliseconds);
if (result == MutexTryAcquireLockResult::TimedOut)
{
return result;
@@ -1445,7 +1565,7 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil
bool acquiredFileLock = false;
while (sharedData->HasAnyTimedWaiters())
{
- if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
+ if (SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
{
acquiredFileLock = true;
break;
@@ -1457,13 +1577,13 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil
break;
}
- acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX);
+ acquiredFileLock = SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX);
_ASSERTE(acquiredFileLock);
break;
}
case 0:
- if (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
+ if (!SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
{
return MutexTryAcquireLockResult::TimedOut;
}
@@ -1472,7 +1592,7 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil
default:
{
// Try to acquire the file lock without waiting
- if (SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
+ if (SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB))
{
break;
}
@@ -1511,7 +1631,7 @@ MutexTryAcquireLockResult NamedMutexProcessData::TryAcquireLock(DWORD timeoutMil
? remainingMilliseconds
: PollLoopMaximumSleepMilliseconds;
Sleep(sleepMilliseconds);
- } while (!SharedMemoryHelpers::TryAcquireFileLock(m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB));
+ } while (!SharedMemoryHelpers::TryAcquireFileLock(errors, m_sharedLockFileDescriptor, LOCK_EX | LOCK_NB));
break;
}
}
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index 0725bd7a87f097..a26a312bd76484 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -307,6 +307,8 @@ static const Entry s_QCall[] =
DllImportEntry(OpenMutexW)
DllImportEntry(OpenSemaphoreW)
DllImportEntry(OutputDebugStringW)
+ DllImportEntry(PAL_CreateMutexW)
+ DllImportEntry(PAL_OpenMutexW)
DllImportEntry(ReleaseMutex)
DllImportEntry(ReleaseSemaphore)
DllImportEntry(ResetEvent)
diff --git a/src/libraries/Common/src/System/IO/Win32Marshal.cs b/src/libraries/Common/src/System/IO/Win32Marshal.cs
index ad2c81e9fc5158..0c11b227e677ed 100644
--- a/src/libraries/Common/src/System/IO/Win32Marshal.cs
+++ b/src/libraries/Common/src/System/IO/Win32Marshal.cs
@@ -22,7 +22,7 @@ internal static Exception GetExceptionForLastWin32Error(string? path = "")
/// Converts the specified Win32 error into a corresponding object, optionally
/// including the specified path in the error message.
///
- internal static Exception GetExceptionForWin32Error(int errorCode, string? path = "")
+ internal static Exception GetExceptionForWin32Error(int errorCode, string? path = "", string? errorDetails = null)
{
// ERROR_SUCCESS gets thrown when another unexpected interop call was made before checking GetLastWin32Error().
// Errors have to get retrieved as soon as possible after P/Invoking to avoid this.
@@ -57,13 +57,19 @@ internal static Exception GetExceptionForWin32Error(int errorCode, string? path
case Interop.Errors.ERROR_OPERATION_ABORTED:
return new OperationCanceledException();
case Interop.Errors.ERROR_INVALID_PARAMETER:
+
default:
- string msg = string.IsNullOrEmpty(path)
- ? GetPInvokeErrorMessage(errorCode)
- : $"{GetPInvokeErrorMessage(errorCode)} : '{path}'";
- return new IOException(
- msg,
- MakeHRFromErrorCode(errorCode));
+ string msg = GetPInvokeErrorMessage(errorCode);
+ if (!string.IsNullOrEmpty(path))
+ {
+ msg += $" : '{path}'.";
+ }
+ if (!string.IsNullOrEmpty(errorDetails))
+ {
+ msg += $" {errorDetails}";
+ }
+
+ return new IOException(msg, MakeHRFromErrorCode(errorCode));
}
static string GetPInvokeErrorMessage(int errorCode)
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 2e00d445ee47ef..e1cc39aeaf4aa5 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -3635,6 +3635,9 @@
Unknown error "{0}".
+
+ One or more system calls failed: {0}
+
Operation could destabilize the runtime.
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 0d6ae242ae6ec6..e5b218a6b98aae 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -2148,6 +2148,7 @@
+
@@ -2205,7 +2206,6 @@
Common\System\Memory\FixedBufferExtensions.cs
-
@@ -2716,4 +2716,4 @@
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs
index 10a91926c3e02b..07eb21b0849805 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Windows.cs
@@ -24,11 +24,6 @@ private void CreateMutexCore(bool initiallyOwned, string? name, out bool created
if (mutexHandle.IsInvalid)
{
mutexHandle.SetHandleAsInvalid();
-#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
- 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));
-#endif
if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE)
throw new WaitHandleCannotBeOpenedException(SR.Format(SR.Threading_WaitHandleCannotBeOpenedException_InvalidHandle, name));
@@ -56,13 +51,6 @@ private static OpenExistingResult OpenExistingWorker(string name, out Mutex? res
myHandle.Dispose();
-#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
- 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));
- }
-#endif
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)