diff --git a/src/coreclr/gc/unix/numasupport.cpp b/src/coreclr/gc/unix/numasupport.cpp index 731bc7c0b0d31e..409dbb4a30eb01 100644 --- a/src/coreclr/gc/unix/numasupport.cpp +++ b/src/coreclr/gc/unix/numasupport.cpp @@ -12,7 +12,7 @@ #include #include -#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) && !defined(TARGET_OPENHARMONY) #include #endif @@ -21,7 +21,7 @@ int g_highestNumaNode = 0; // Is numa available bool g_numaAvailable = false; -#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) && !defined(TARGET_OPENHARMONY) static int GetNodeNum(const char* path, bool firstOnly) { DIR *dir; @@ -56,7 +56,7 @@ static int GetNodeNum(const char* path, bool firstOnly) void NUMASupportInitialize() { -#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) && !defined(TARGET_OPENHARMONY) if (syscall(__NR_get_mempolicy, NULL, NULL, 0, 0, 0) < 0) return; @@ -72,7 +72,7 @@ void NUMASupportInitialize() int GetNumaNodeNumByCpu(int cpu) { -#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) && !defined(TARGET_OPENHARMONY) char path[64]; if (snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d", cpu) < 0) return -1; @@ -85,7 +85,7 @@ int GetNumaNodeNumByCpu(int cpu) long BindMemoryPolicy(void* start, unsigned long len, const unsigned long* nodemask, unsigned long maxnode) { -#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) && !defined(TARGET_OPENHARMONY) return syscall(__NR_mbind, (long)start, len, 1, (long)nodemask, maxnode, 0); #else return -1; 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 414f29f6c9e8c1..3f23195a6d8d23 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 @@ -54,6 +54,7 @@ $(DefineConstants);TARGET_ILLUMOS $(DefineConstants);TARGET_SOLARIS $(DefineConstants);TARGET_HAIKU + $(DefineConstants);TARGET_OPENHARMONY $(DefineConstants);FEATURE_CROSS_PROCESS_MUTEX diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/SharedMemoryManager.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/SharedMemoryManager.Unix.cs index f33075278d6648..9646fef949e5c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/SharedMemoryManager.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/SharedMemoryManager.Unix.cs @@ -414,7 +414,15 @@ private static string InitalizeSharedFilesPath() } } + // On non-Apple platforms, shared memory files live under the process temp directory. +#if TARGET_OPENHARMONY + // HarmonyOS (OpenHarmony) app sandboxes mount /tmp read-only, so use the process temp + // directory (which honors TMPDIR on Unix and falls back to /tmp) for the .NET shared + // memory files (named mutexes, etc.). + return Path.GetTempPath(); +#else return "/tmp/"; +#endif } internal static SafeFileHandle CreateOrOpenFile(string sharedMemoryFilePath, SharedMemoryId id, bool createIfNotExist, out bool createdFile) diff --git a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs index 28ecea26fca530..6cd0114927a258 100644 --- a/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/OperatingSystem.cs @@ -220,6 +220,17 @@ internal static bool IsHaiku() => false; #endif + /// + /// Indicates whether the current application is running on HarmonyOS (OpenHarmony). + /// + [NonVersionable] + internal static bool IsOpenHarmony() => +#if TARGET_OPENHARMONY + true; +#else + false; +#endif + /// /// Indicates whether the current application is running on Android. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/NamedMutex.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/NamedMutex.Unix.cs index 077f68c63b23e2..d43fa784d0ff31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/NamedMutex.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/NamedMutex.Unix.cs @@ -82,7 +82,7 @@ internal abstract class NamedMutexProcessDataBase(SharedMemoryProcessDataHeader< // independently by the processes involved. See https://github.com/dotnet/runtime/issues/10519. // On OpenBSD, cross process mutexes are not supported in the pthread implementation. See https://github.com/dotnet/runtime/pull/125089. // On Haiku, robust mutexes are WIP. See https://github.com/dotnet/runtime/pull/126701#issuecomment-4334338213. - private static bool UsePThreadMutexes => !OperatingSystem.IsApplePlatform() && !OperatingSystem.IsFreeBSD() && !OperatingSystem.IsOpenBSD() && !OperatingSystem.IsHaiku(); + private static bool UsePThreadMutexes => !OperatingSystem.IsApplePlatform() && !OperatingSystem.IsFreeBSD() && !OperatingSystem.IsOpenBSD() && !OperatingSystem.IsHaiku() && !OperatingSystem.IsOpenHarmony(); private readonly SharedMemoryProcessDataHeader _processDataHeader = header; protected nuint _lockCount; diff --git a/src/libraries/System.Threading/tests/MutexTests.cs b/src/libraries/System.Threading/tests/MutexTests.cs index 3354ac0f587e1c..e7ec25c3406685 100644 --- a/src/libraries/System.Threading/tests/MutexTests.cs +++ b/src/libraries/System.Threading/tests/MutexTests.cs @@ -1039,7 +1039,16 @@ public void NamedMutex_OtherEvent_NotCompatible() Assert.Throws(() => WaitHandle.WaitAny(new WaitHandle[] { m, mre }, 0)); } - private const string GlobalSharedMemoryDirectory = $"/tmp/.dotnet/shm/global"; + // The runtime places global shared memory files under {SharedFilesPath}/.dotnet/shm/global + // (see SharedMemoryManager.Unix.cs). SharedFilesPath is /tmp/ on non-Apple platforms except + // on HarmonyOS, where it honors TMPDIR. Mirror the runtime's derivation so these tests + // always target the same directory the runtime uses. + private static string GlobalSharedMemoryDirectory => +#if TARGET_OPENHARMONY + Path.Combine(Path.GetTempPath(), ".dotnet", "shm", "global"); +#else + "/tmp/.dotnet/shm/global"; +#endif private const UnixFileMode AllUsersRwx = UnixFileMode.UserRead | UnixFileMode.UserWrite