From 4d0aebbf9b127ec76de02186e46fdd4854ee4d16 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 2 Sep 2020 13:02:52 -0700 Subject: [PATCH 01/14] Add Environment.ProcessPath Fixes #40862 --- .../Unix/System.Native/Interop.GetLine.cs | 14 ----- .../System.Native/Interop.GetProcessPath.cs | 17 ++++++ .../Unix/System.Native/Interop.POpen.cs | 17 ------ .../Kernel32/Interop.GetModuleFileName.cs | 16 ++++++ src/libraries/Native/Unix/CMakeLists.txt | 4 -- .../Native/Unix/System.Native/pal_io.c | 16 ------ .../Native/Unix/System.Native/pal_io.h | 7 --- .../Native/Unix/System.Native/pal_process.c | 52 ++++++++++++++----- .../Native/Unix/System.Native/pal_process.h | 16 +++--- .../src/System.Diagnostics.Process.csproj | 4 -- .../src/System/Diagnostics/Process.FreeBSD.cs | 6 --- .../src/System/Diagnostics/Process.OSX.cs | 15 ------ .../src/System/Diagnostics/Process.Unix.cs | 2 +- .../System/Diagnostics/Process.UnknownUnix.cs | 5 -- .../tests/ProcessTests.cs | 13 ++--- .../System.Private.CoreLib.Shared.projitems | 6 +++ .../src/System/Environment.Browser.cs | 6 +++ .../src/System/Environment.Unix.cs | 6 +++ .../src/System/Environment.Windows.cs | 26 +++++++++- .../System.Runtime/ref/System.Runtime.cs | 1 + 20 files changed, 128 insertions(+), 121 deletions(-) delete mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetLine.cs create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs delete mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.POpen.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileName.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetLine.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetLine.cs deleted file mode 100644 index 8033720f2a907..0000000000000 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetLine.cs +++ /dev/null @@ -1,14 +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; -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Sys - { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetLine", SetLastError = true)] - internal static extern string GetLine(IntPtr stream); - } -} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs new file mode 100644 index 0000000000000..ab064903dac12 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs @@ -0,0 +1,17 @@ +// 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 + { + /// + /// Takes a path containing relative subpaths or links and returns the absolute path. + /// This function works on both files and folders and returns a null-terminated string. + /// + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetProcessPath", SetLastError = true)] + internal static extern string? GetProcessPath(); + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.POpen.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.POpen.cs deleted file mode 100644 index efa67b813cf8e..0000000000000 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.POpen.cs +++ /dev/null @@ -1,17 +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; -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Sys - { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_POpen", SetLastError = true)] - internal static extern IntPtr POpen(string command, string type); - - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PClose", SetLastError = true)] - internal static extern int PClose(IntPtr stream); - } -} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileName.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileName.cs new file mode 100644 index 0000000000000..9c8f0109177b3 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileName.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32; + +internal static partial class Interop +{ + internal static unsafe partial class Kernel32 + { + [DllImport(Libraries.Kernel32, EntryPoint = "GetModuleFileNameW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)] + internal static extern uint GetModuleFileName(IntPtr hModule, ref char lpFilename, uint nSize); + } +} diff --git a/src/libraries/Native/Unix/CMakeLists.txt b/src/libraries/Native/Unix/CMakeLists.txt index e08ba5111c053..c9af0c4c7086d 100644 --- a/src/libraries/Native/Unix/CMakeLists.txt +++ b/src/libraries/Native/Unix/CMakeLists.txt @@ -156,10 +156,6 @@ if (CLR_CMAKE_TARGET_LINUX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE") endif () -if(CLR_CMAKE_TARGET_FREEBSD) - add_definitions(-D_BSD_SOURCE) # required for getline -endif(CLR_CMAKE_TARGET_FREEBSD) - # CLR_ADDITIONAL_LINKER_FLAGS - used for passing additional arguments to linker # CLR_ADDITIONAL_COMPILER_OPTIONS - used for passing additional arguments to compiler # diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index 79f9c831afed6..ea8375c1b8df1 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -40,11 +40,6 @@ // Somehow, AIX mangles the definition for this behind a C++ def // Redeclare it here extern int getpeereid(int, uid_t *__restrict__, gid_t *__restrict__); -// This function declaration is hidden behind `_XOPEN_SOURCE=700`, but we need -// `_ALL_SOURCE` to build the runtime, and that resets that definition to 600. -// Instead of trying to wrangle ifdefs in system headers with more definitions, -// just declare it here. -extern ssize_t getline(char **, size_t *, FILE *); #endif #if HAVE_STAT64 @@ -961,17 +956,6 @@ int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, i #endif } -char* SystemNative_GetLine(FILE* stream) -{ - assert(stream != NULL); - - char* lineptr = NULL; - size_t n = 0; - ssize_t length = getline(&lineptr, &n, stream); - - return length >= 0 ? lineptr : NULL; -} - int32_t SystemNative_Read(intptr_t fd, void* buffer, int32_t bufferSize) { return Common_Read(fd, buffer, bufferSize); diff --git a/src/libraries/Native/Unix/System.Native/pal_io.h b/src/libraries/Native/Unix/System.Native/pal_io.h index b955a569f37db..dbc2be8721a94 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.h +++ b/src/libraries/Native/Unix/System.Native/pal_io.h @@ -598,13 +598,6 @@ PALEXPORT int32_t SystemNative_Poll(PollEvent* pollEvents, uint32_t eventCount, */ PALEXPORT int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, int32_t advice); -/** -* Reads a line from the provided stream. -* -* Returns the read line, or null if no line could be read. The caller is responsible for freeing the malloc'd line. -*/ -PALEXPORT char* SystemNative_GetLine(FILE* stream); - /** * Reads the number of bytes specified into the provided buffer from the specified, opened file descriptor. * diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index 44cb59d4c1a2a..9f5a3f384c3c3 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -28,6 +28,9 @@ #include #endif +#ifdef __APPLE__ +#include +#endif // Validate that our SysLogPriority values are correct for the platform c_static_assert(PAL_LOG_EMERG == LOG_EMERG); @@ -516,19 +519,6 @@ done:; #endif } -FILE* SystemNative_POpen(const char* command, const char* type) -{ - assert(command != NULL); - assert(type != NULL); - return popen(command, type); -} - -int32_t SystemNative_PClose(FILE* stream) -{ - assert(stream != NULL); - return pclose(stream); -} - // Each platform type has it's own RLIMIT values but the same name, so we need // to convert our standard types into the platform specific ones. static int32_t ConvertRLimitResourcesPalToPlatform(RLimitResources value) @@ -871,3 +861,39 @@ int32_t SystemNative_SchedGetAffinity(int32_t pid, intptr_t* mask) return result; } #endif + +// Returns the full path to the executable for the current process resolving symbolic links. +// The caller is responsible for releasing the buffer. Returns null on error. +char* SystemNative_GetProcessPath() +{ + // Get path to the executable for the current process using + // platform specific means. +#ifdef __APPLE__ + // On Mac, we ask the OS for the absolute path to the entrypoint executable + uint32_t path_length = 0; + if (_NSGetExecutablePath(nullptr, &path_length) != -1) + { + errno = EINVAL; + return NULL; + } + + char path_buf[path_length]; + if (_NSGetExecutablePath(path_buf, &path_length) != 0) + { + errno = EINVAL; + return NULL; + } + + return realpath(path_buf, NULL); +#else + +#ifdef __linux__ +#define symlinkEntrypointExecutable "/proc/self/exe" +#else +#define symlinkEntrypointExecutable "/proc/curproc/exe" +#endif + + // Resove the symlink to the executable from /proc + return realpath(symlinkEntrypointExecutable, NULL); +#endif +} diff --git a/src/libraries/Native/Unix/System.Native/pal_process.h b/src/libraries/Native/Unix/System.Native/pal_process.h index 49d5b85553c5a..c8ae2d2dff4f1 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.h +++ b/src/libraries/Native/Unix/System.Native/pal_process.h @@ -36,16 +36,6 @@ PALEXPORT int32_t SystemNative_ForkAndExecProcess( int32_t* stdoutFd, // [out] if redirectStdout, the parent's fd for the child's stdout int32_t* stderrFd); // [out] if redirectStderr, the parent's fd for the child's stderr -/** - * Shim for the popen function. - */ -PALEXPORT FILE* SystemNative_POpen(const char* command, const char* type); - -/** - * Shim for the pclose function. - */ -PALEXPORT int32_t SystemNative_PClose(FILE* stream); - /************ * The values below in the header are fixed and correct for managed callers to use forever. * We must never change them. The implementation must either static_assert that they are equal @@ -252,3 +242,9 @@ PALEXPORT int32_t SystemNative_SchedSetAffinity(int32_t pid, intptr_t* mask); */ PALEXPORT int32_t SystemNative_SchedGetAffinity(int32_t pid, intptr_t* mask); #endif + +/** + * Returns the path of the executable that started the currently executing process, + * resolving symbolic links. The caller is responsible for releasing the buffer. + */ +PALEXPORT char* SystemNative_GetProcessPath(void); diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index 947d0f2a1bb0c..f0eeba653ea37 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -240,8 +240,6 @@ Link="Common\Interop\Unix\Interop.ForkAndExecProcess.cs" /> - - Gets the path to the current executable, or null if it could not be retrieved. - private static string? GetExePath() - { - return Interop.Process.GetProcPath(Environment.ProcessId); - } - // ---------------------------------- // ---- Unix PAL layer ends here ---- // ---------------------------------- diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs index 1028d215e03f6..c06f622880ddd 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.OSX.cs @@ -91,21 +91,6 @@ private int ParentProcessId } } - /// Gets the path to the current executable, or null if it could not be retrieved. - private static string? GetExePath() - { - try - { - return Interop.libproc.proc_pidpath(Environment.ProcessId); - } - catch (Win32Exception) - { - // It will throw System.ComponentModel.Win32Exception (2): No such file or Directory when - // the executable file is deleted. - return null; - } - } - // ---------------------------------- // ---- Unix PAL layer ends here ---- // ---------------------------------- diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index 5096a2adea86c..35fb9912b25ed 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -680,7 +680,7 @@ private static string[] CreateEnvp(ProcessStartInfo psi) } // Then check the executable's directory - string? path = GetExePath(); + string? path = Environment.ProcessPath; if (path != null) { try diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs index 072fb489f56c9..67b4af31225b6 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.UnknownUnix.cs @@ -76,11 +76,6 @@ private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr throw new PlatformNotSupportedException(); } - private static string GetExePath() - { - throw new PlatformNotSupportedException(); - } - /// Gets execution path private string GetPathToOpenFile() { diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index 5fa900d1883de..7ac2db84d31ae 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -494,15 +494,12 @@ public void TestMainModule() { Process p = Process.GetCurrentProcess(); - // On UAP casing may not match - we use Path.GetFileName(exePath) instead of kernel32!GetModuleFileNameEx which is not available on UAP - Func normalize = PlatformDetection.IsInAppContainer ? - (Func)((s) => s.ToLowerInvariant()) : - (s) => s; - Assert.InRange(p.Modules.Count, 1, int.MaxValue); - Assert.Equal(normalize(RemoteExecutor.HostRunnerName), normalize(p.MainModule.ModuleName)); - Assert.EndsWith(normalize(RemoteExecutor.HostRunnerName), normalize(p.MainModule.FileName)); - Assert.Equal(normalize(string.Format("System.Diagnostics.ProcessModule ({0})", RemoteExecutor.HostRunnerName)), normalize(p.MainModule.ToString())); + Assert.Equal(RemoteExecutor.HostRunnerName, p.MainModule.ModuleName); + Assert.EndsWith(RemoteExecutor.HostRunnerName, p.MainModule.FileName); + Assert.Equal(string.Format("System.Diagnostics.ProcessModule ({0})", RemoteExecutor.HostRunnerName), p.MainModule.ToString()); + + Assert.Equal(Environment.ProcessPath, p.MainModule.FileName); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] 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 4cf5f00f44b7f..1b32a55a27fba 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 @@ -1380,6 +1380,9 @@ Common\Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs + + Common\Interop\Windows\Kernel32\Interop.GetModuleFileName.cs + Common\Interop\Windows\Kernel32\Interop.GetProcessMemoryInfo.cs @@ -1709,6 +1712,9 @@ Common\Interop\Unix\System.Native\Interop.GetHostName.cs + + Common\Interop\Unix\System.Native\Interop.GetProcessPath.cs + Common\Interop\Unix\System.Native\Interop.GetRandomBytes.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs index 07ad3b8b31c75..db0b704717218 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs @@ -26,5 +26,11 @@ private static OperatingSystem GetOSVersion() } private static int GetCurrentProcessId() => 42; + + /// + /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. + /// + /// Path of the executable that started the currently executing process + public static string? ProcessPath => null; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs index 9b5ff6ac417cd..9abd46f9a1f59 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -112,5 +112,11 @@ private static unsafe bool TryGetUserNameFromPasswd(byte* buf, int bufLen, out s } private static int GetCurrentProcessId() => Interop.Sys.GetPid(); + + /// + /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. + /// + /// Path of the executable that started the currently executing process + public static string? ProcessPath => Interop.Sys.GetProcessPath(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs index 751747b96593c..9c5d6b5ee521d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -72,7 +72,7 @@ private static string ExpandEnvironmentVariablesCore(string name) } if (length == 0) - Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw Win32Marshal.GetExceptionForLastWin32Error(); // length includes the null terminator builder.Length = (int)length - 1; @@ -88,6 +88,30 @@ private static string ExpandEnvironmentVariablesCore(string name) private static int GetCurrentProcessId() => unchecked((int)Interop.Kernel32.GetCurrentProcessId()); + /// + /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. + /// + /// Path of the executable that started the currently executing process + public static string? ProcessPath + { + get + { + var builder = new ValueStringBuilder(stackalloc char[Interop.Kernel32.MAX_PATH]); + + uint length; + while ((length = Interop.Kernel32.GetModuleFileName(IntPtr.Zero, ref builder.GetPinnableReference(), (uint)builder.Capacity)) >= builder.Capacity) + { + builder.EnsureCapacity((int)length); + } + + if (length == 0) + throw Win32Marshal.GetExceptionForLastWin32Error(); + + builder.Length = (int)length; + return builder.ToString(); + } + } + private static unsafe OperatingSystem GetOSVersion() { if (Interop.NtDll.RtlGetVersionEx(out Interop.NtDll.RTL_OSVERSIONINFOEX osvi) != 0) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 01ce63724ef5a..e5143cc08060c 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -1884,6 +1884,7 @@ public static partial class Environment public static string MachineName { get { throw null; } } public static string NewLine { get { throw null; } } public static System.OperatingSystem OSVersion { get { throw null; } } + public static string? ProcessPath { get { throw null; } } public static int ProcessId { get { throw null; } } public static int ProcessorCount { get { throw null; } } public static string StackTrace { get { throw null; } } From 499654a702d35877a54b6d2a7ba1c6dfe472a3fa Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 25 Sep 2020 19:44:43 -0700 Subject: [PATCH 02/14] Fix macOS --- src/libraries/Native/Unix/System.Native/pal_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index 9f5a3f384c3c3..9cad3025e4881 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -871,7 +871,7 @@ char* SystemNative_GetProcessPath() #ifdef __APPLE__ // On Mac, we ask the OS for the absolute path to the entrypoint executable uint32_t path_length = 0; - if (_NSGetExecutablePath(nullptr, &path_length) != -1) + if (_NSGetExecutablePath(NULL, &path_length) != -1) { errno = EINVAL; return NULL; From 8d6dceac4878b9ecbe4536def5e8ad556dbf8ba3 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 26 Sep 2020 11:16:35 -0700 Subject: [PATCH 03/14] Fix comment --- .../src/Interop/Unix/System.Native/Interop.GetProcessPath.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs index ab064903dac12..5825d94a3fc29 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs @@ -8,8 +8,7 @@ internal static partial class Interop internal static partial class Sys { /// - /// Takes a path containing relative subpaths or links and returns the absolute path. - /// This function works on both files and folders and returns a null-terminated string. + /// Returns the full path to the executable for the current process resolving symbolic links. /// [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetProcessPath", SetLastError = true)] internal static extern string? GetProcessPath(); From 5c64acf8ef726d35c05b428623734d38598a348c Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 26 Sep 2020 11:28:56 -0700 Subject: [PATCH 04/14] FreeBSD impl --- .../Native/Unix/System.Native/pal_process.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index 9cad3025e4881..d90b83cdbac91 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -32,6 +32,12 @@ #include #endif +#ifdef __FreeBSD__ +#include +#include +#include +#endif + // Validate that our SysLogPriority values are correct for the platform c_static_assert(PAL_LOG_EMERG == LOG_EMERG); c_static_assert(PAL_LOG_ALERT == LOG_ALERT); @@ -885,6 +891,22 @@ char* SystemNative_GetProcessPath() } return realpath(path_buf, NULL); +#elif defined(__FreeBSD__) + static const int name[] = + { + CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 + }; + + char path[PATH_MAX]; + size_t len; + + len = sizeof(path); + if (!sysctl(name, 4, path, &len, NULL, 0) != 0) + { + return NULL; + } + + return strdup(path); #else #ifdef __linux__ From e82c5c4aaa56d254e0567ec63a46497dc926f2b8 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 26 Sep 2020 12:21:36 -0700 Subject: [PATCH 05/14] Fix FreeBSD --- src/libraries/Native/Unix/System.Native/pal_process.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index d90b83cdbac91..d912e876ba5df 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -874,7 +874,7 @@ char* SystemNative_GetProcessPath() { // Get path to the executable for the current process using // platform specific means. -#ifdef __APPLE__ +#if defined(__APPLE__) // On Mac, we ask the OS for the absolute path to the entrypoint executable uint32_t path_length = 0; if (_NSGetExecutablePath(NULL, &path_length) != -1) @@ -901,7 +901,7 @@ char* SystemNative_GetProcessPath() size_t len; len = sizeof(path); - if (!sysctl(name, 4, path, &len, NULL, 0) != 0) + if (sysctl(name, 4, path, &len, NULL, 0) != 0) { return NULL; } From 12d68e31dfe882196448afffcdb3b486ef12c63e Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 29 Sep 2020 00:33:46 -0700 Subject: [PATCH 06/14] Add cache --- .../Native/Unix/System.Native/pal_process.c | 7 ++- .../src/System/Environment.Browser.cs | 4 +- .../src/System/Environment.Unix.cs | 11 ++--- .../src/System/Environment.Windows.cs | 33 ++++++------- .../src/System/Environment.cs | 48 +++++++++++++++---- 5 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index d912e876ba5df..6abdb42d1d6aa 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -907,6 +907,11 @@ char* SystemNative_GetProcessPath() } return strdup(path); +#elif defined(__sun) + const char* path = getexecname(); + if (path == NULL) + return NULL; + return realpath(path, NULL); #else #ifdef __linux__ @@ -915,7 +920,7 @@ char* SystemNative_GetProcessPath() #define symlinkEntrypointExecutable "/proc/curproc/exe" #endif - // Resove the symlink to the executable from /proc + // Resolve the symlink to the executable from /proc return realpath(symlinkEntrypointExecutable, NULL); #endif } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs index db0b704717218..a2f4b16fc6e1a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs @@ -25,12 +25,12 @@ private static OperatingSystem GetOSVersion() return new OperatingSystem(PlatformID.Other, new Version(1, 0, 0, 0)); } - private static int GetCurrentProcessId() => 42; + private static int GetProcessId() => 42; /// /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. /// /// Path of the executable that started the currently executing process - public static string? ProcessPath => null; + private static string GetProcessPath() => ""; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs index 9abd46f9a1f59..331e62c42c240 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -111,12 +112,10 @@ private static unsafe bool TryGetUserNameFromPasswd(byte* buf, int bufLen, out s throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); } - private static int GetCurrentProcessId() => Interop.Sys.GetPid(); + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private static int GetProcessId() => Interop.Sys.GetPid(); - /// - /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. - /// - /// Path of the executable that started the currently executing process - public static string? ProcessPath => Interop.Sys.GetProcessPath(); + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private static string? GetProcessPath() => Interop.Sys.GetProcessPath(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs index 9c5d6b5ee521d..cba665d376080 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -2,6 +2,7 @@ // 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; @@ -86,30 +87,24 @@ private static string ExpandEnvironmentVariablesCore(string name) Interop.Kernel32.GetComputerName() ?? throw new InvalidOperationException(SR.InvalidOperation_ComputerName); - private static int GetCurrentProcessId() => unchecked((int)Interop.Kernel32.GetCurrentProcessId()); + [MethodImplAttribute(MethodImplOptions.NoInlining)] + private static int GetProcessId() => unchecked((int)Interop.Kernel32.GetCurrentProcessId()); - /// - /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. - /// - /// Path of the executable that started the currently executing process - public static string? ProcessPath + private static string GetProcessPath() { - get - { - var builder = new ValueStringBuilder(stackalloc char[Interop.Kernel32.MAX_PATH]); + var builder = new ValueStringBuilder(stackalloc char[Interop.Kernel32.MAX_PATH]); - uint length; - while ((length = Interop.Kernel32.GetModuleFileName(IntPtr.Zero, ref builder.GetPinnableReference(), (uint)builder.Capacity)) >= builder.Capacity) - { - builder.EnsureCapacity((int)length); - } + uint length; + while ((length = Interop.Kernel32.GetModuleFileName(IntPtr.Zero, ref builder.GetPinnableReference(), (uint)builder.Capacity)) >= builder.Capacity) + { + builder.EnsureCapacity((int)length); + } - if (length == 0) - throw Win32Marshal.GetExceptionForLastWin32Error(); + if (length == 0) + throw Win32Marshal.GetExceptionForLastWin32Error(); - builder.Length = (int)length; - return builder.ToString(); - } + builder.Length = (int)length; + return builder.ToString(); } private static unsafe OperatingSystem GetOSVersion() diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.cs index c971a01516498..fd9c1efed0dc7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.cs @@ -117,21 +117,48 @@ public static string GetFolderPath(SpecialFolder folder, SpecialFolderOption opt return GetFolderPathCore(folder, option); } - private static int s_processId; - private static volatile bool s_haveProcessId; + private static volatile int s_processId; /// Gets the unique identifier for the current process. public static int ProcessId { get { - if (!s_haveProcessId) + int processId = s_processId; + if (processId == 0) { - s_processId = GetCurrentProcessId(); - s_haveProcessId = true; + Interlocked.CompareExchange(ref s_processId, GetProcessId(), 0); + processId = s_processId; + // Assume that process Id zero is invalid for user processes. It holds for all mainstream operating systems. + Debug.Assert(processId != 0); } + return processId; + } + } + + private static volatile string? s_processPath; - return s_processId; + /// + /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. + /// + /// Path of the executable that started the currently executing process + /// + /// If the executable is renamed or deleted, the return value is undefined and depends on the operating system. + /// + public static string? ProcessPath + { + get + { + string? processPath = s_processPath; + if (processPath == null) + { + // The value is cache both as a performance optimization and to ensure that the API always returns + // same path in the given process. + Interlocked.CompareExchange(ref s_processPath, GetProcessPath() ?? "", null); + processPath = s_processPath; + Debug.Assert(processPath != null); + } + return (processPath.Length != 0) ? processPath : null; } } @@ -141,17 +168,20 @@ public static int ProcessId public static string NewLine => NewLineConst; - private static OperatingSystem? s_osVersion; + private static volatile OperatingSystem? s_osVersion; public static OperatingSystem OSVersion { get { - if (s_osVersion == null) + OperatingSystem? osVersion = s_osVersion; + if (osVersion == null) { Interlocked.CompareExchange(ref s_osVersion, GetOSVersion(), null); + osVersion = s_osVersion; + Debug.Assert(osVersion != null); } - return s_osVersion; + return osVersion; } } From 5da357f2d914d0a5519c1eec5f7dffd269d90d11 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 09:03:38 -0700 Subject: [PATCH 07/14] Update src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs Co-authored-by: Stephen Toub --- .../src/Interop/Unix/System.Native/Interop.GetProcessPath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs index 5825d94a3fc29..c64d21e5054b8 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetProcessPath.cs @@ -8,7 +8,7 @@ internal static partial class Interop internal static partial class Sys { /// - /// Returns the full path to the executable for the current process resolving symbolic links. + /// Returns the full path to the executable for the current process, resolving symbolic links. /// [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetProcessPath", SetLastError = true)] internal static extern string? GetProcessPath(); From 4cbd0f1bec3038444d5a359bcbe90924b404cbf5 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 09:06:02 -0700 Subject: [PATCH 08/14] Apply suggestions from code review Co-authored-by: Stephen Toub --- .../System.Private.CoreLib/src/System/Environment.Browser.cs | 2 +- .../System.Private.CoreLib/src/System/Environment.Windows.cs | 2 +- .../System.Private.CoreLib/src/System/Environment.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs index a2f4b16fc6e1a..f46b41f5d845a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs @@ -31,6 +31,6 @@ private static OperatingSystem GetOSVersion() /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. /// /// Path of the executable that started the currently executing process - private static string GetProcessPath() => ""; + private static string? GetProcessPath() => ""; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs index cba665d376080..8073d167c5010 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -90,7 +90,7 @@ private static string ExpandEnvironmentVariablesCore(string name) [MethodImplAttribute(MethodImplOptions.NoInlining)] private static int GetProcessId() => unchecked((int)Interop.Kernel32.GetCurrentProcessId()); - private static string GetProcessPath() + private static string? GetProcessPath() { var builder = new ValueStringBuilder(stackalloc char[Interop.Kernel32.MAX_PATH]); diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.cs index fd9c1efed0dc7..68932e746211f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.cs @@ -152,8 +152,8 @@ public static string? ProcessPath string? processPath = s_processPath; if (processPath == null) { - // The value is cache both as a performance optimization and to ensure that the API always returns - // same path in the given process. + // The value is cached both as a performance optimization and to ensure that the API always returns + // the same path in a given process. Interlocked.CompareExchange(ref s_processPath, GetProcessPath() ?? "", null); processPath = s_processPath; Debug.Assert(processPath != null); From 804790f084fb63f4e6c11cc628ce8d03a8a318a2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 15:00:46 -0700 Subject: [PATCH 09/14] Apply suggestions from code review Co-authored-by: Ryan Lucia --- src/libraries/Native/Unix/System.Native/pal_process.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index 6abdb42d1d6aa..7b9d779b31312 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -872,8 +872,8 @@ int32_t SystemNative_SchedGetAffinity(int32_t pid, intptr_t* mask) // The caller is responsible for releasing the buffer. Returns null on error. char* SystemNative_GetProcessPath() { - // Get path to the executable for the current process using - // platform specific means. + // Get the path to the executable for the current process using + // platform-specific means. #if defined(__APPLE__) // On Mac, we ask the OS for the absolute path to the entrypoint executable uint32_t path_length = 0; From 53b7f9c2091048e6847d9e49fcbdd64ffe963bbe Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 15:01:08 -0700 Subject: [PATCH 10/14] Update src/libraries/Native/Unix/System.Native/pal_process.c Co-authored-by: Ryan Lucia --- src/libraries/Native/Unix/System.Native/pal_process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index 7b9d779b31312..f4b896f004200 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -868,7 +868,7 @@ int32_t SystemNative_SchedGetAffinity(int32_t pid, intptr_t* mask) } #endif -// Returns the full path to the executable for the current process resolving symbolic links. +// Returns the full path to the executable for the current process, resolving symbolic links. // The caller is responsible for releasing the buffer. Returns null on error. char* SystemNative_GetProcessPath() { From 034c92d3da456647f7fc2f7854134fa61ea1a1f8 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 23:12:05 -0700 Subject: [PATCH 11/14] CR feedback --- src/libraries/Native/Unix/System.Native/pal_process.c | 7 ++----- .../src/System/Environment.Browser.cs | 2 +- .../System.Private.CoreLib/src/System/Environment.Unix.cs | 4 ++-- .../src/System/Environment.Windows.cs | 2 +- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_process.c b/src/libraries/Native/Unix/System.Native/pal_process.c index 7b9d779b31312..123bacd4b9554 100644 --- a/src/libraries/Native/Unix/System.Native/pal_process.c +++ b/src/libraries/Native/Unix/System.Native/pal_process.c @@ -872,10 +872,7 @@ int32_t SystemNative_SchedGetAffinity(int32_t pid, intptr_t* mask) // The caller is responsible for releasing the buffer. Returns null on error. char* SystemNative_GetProcessPath() { - // Get the path to the executable for the current process using - // platform-specific means. #if defined(__APPLE__) - // On Mac, we ask the OS for the absolute path to the entrypoint executable uint32_t path_length = 0; if (_NSGetExecutablePath(NULL, &path_length) != -1) { @@ -915,9 +912,9 @@ char* SystemNative_GetProcessPath() #else #ifdef __linux__ -#define symlinkEntrypointExecutable "/proc/self/exe" + const char* symlinkEntrypointExecutable = "/proc/self/exe"; #else -#define symlinkEntrypointExecutable "/proc/curproc/exe" + const char* symlinkEntrypointExecutable = "/proc/curproc/exe"; #endif // Resolve the symlink to the executable from /proc diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs index f46b41f5d845a..7ba016ee24ba2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Browser.cs @@ -31,6 +31,6 @@ private static OperatingSystem GetOSVersion() /// Returns the path of the executable that started the currently executing process. Returns null when the path is not available. /// /// Path of the executable that started the currently executing process - private static string? GetProcessPath() => ""; + private static string? GetProcessPath() => null; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs index 331e62c42c240..6ed2b34a291f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Unix.cs @@ -112,10 +112,10 @@ private static unsafe bool TryGetUserNameFromPasswd(byte* buf, int bufLen, out s throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); } - [MethodImplAttribute(MethodImplOptions.NoInlining)] + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Avoid inlining PInvoke frame into the hot path private static int GetProcessId() => Interop.Sys.GetPid(); - [MethodImplAttribute(MethodImplOptions.NoInlining)] + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Avoid inlining PInvoke frame into the hot path private static string? GetProcessPath() => Interop.Sys.GetProcessPath(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs index 8073d167c5010..b769f59cc2df2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.Windows.cs @@ -87,7 +87,7 @@ private static string ExpandEnvironmentVariablesCore(string name) Interop.Kernel32.GetComputerName() ?? throw new InvalidOperationException(SR.InvalidOperation_ComputerName); - [MethodImplAttribute(MethodImplOptions.NoInlining)] + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Avoid inlining PInvoke frame into the hot path private static int GetProcessId() => unchecked((int)Interop.Kernel32.GetCurrentProcessId()); private static string? GetProcessPath() From e961792ded7981b07d14bc33b22b6619c03b4612 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 23:22:58 -0700 Subject: [PATCH 12/14] Improve tests --- .../tests/ProcessTests.cs | 2 - .../tests/System/EnvironmentTests.cs | 37 +++++++++++++------ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index 7ac2db84d31ae..c67df209280de 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -498,8 +498,6 @@ public void TestMainModule() Assert.Equal(RemoteExecutor.HostRunnerName, p.MainModule.ModuleName); Assert.EndsWith(RemoteExecutor.HostRunnerName, p.MainModule.FileName); Assert.Equal(string.Format("System.Diagnostics.ProcessModule ({0})", RemoteExecutor.HostRunnerName), p.MainModule.ToString()); - - Assert.Equal(Environment.ProcessPath, p.MainModule.FileName); } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] diff --git a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs index ec2a052d827d8..f6c29649da237 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs @@ -60,6 +60,21 @@ public void CurrentManagedThreadId_Idempotent() Assert.Equal(Environment.CurrentManagedThreadId, Environment.CurrentManagedThreadId); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public void CurrentManagedThreadId_DifferentForActiveThreads() + { + var ids = new HashSet(); + Barrier b = new Barrier(10); + Task.WaitAll((from i in Enumerable.Range(0, b.ParticipantCount) + select Task.Factory.StartNew(() => + { + b.SignalAndWait(); + lock (ids) ids.Add(Environment.CurrentManagedThreadId); + b.SignalAndWait(); + }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)).ToArray()); + Assert.Equal(b.ParticipantCount, ids.Count); + } + [Fact] public void ProcessId_Idempotent() { @@ -74,19 +89,17 @@ public void ProcessId_MatchesExpectedValue() Assert.Equal(handle.Process.Id, int.Parse(handle.Process.StandardOutput.ReadToEnd())); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - public void CurrentManagedThreadId_DifferentForActiveThreads() + [Fact] + public void ProcessPath_Idempotent() { - var ids = new HashSet(); - Barrier b = new Barrier(10); - Task.WaitAll((from i in Enumerable.Range(0, b.ParticipantCount) - select Task.Factory.StartNew(() => - { - b.SignalAndWait(); - lock (ids) ids.Add(Environment.CurrentManagedThreadId); - b.SignalAndWait(); - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)).ToArray()); - Assert.Equal(b.ParticipantCount, ids.Count); + Assert.Same(Environment.ProcessPath, Environment.ProcessPath); + } + + [Fact] + public void ProcessPath_MatchedExpectedValue() + { + string expectedProcessPath = PlatformDetection.IsBrowser ? null : Process.GetCurrentProcess().MainModule.FileName; + Assert.Equal(expectedProcessPath, Environment.ProcessPath); } [Fact] From 22e3eb17cb7b0b79963bbf5690f5b5c5d4f6160b Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 23:25:25 -0700 Subject: [PATCH 13/14] Update src/libraries/System.Private.CoreLib/src/System/Environment.cs Co-authored-by: Ryan Lucia --- src/libraries/System.Private.CoreLib/src/System/Environment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Environment.cs b/src/libraries/System.Private.CoreLib/src/System/Environment.cs index 68932e746211f..15e19465cae31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Environment.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Environment.cs @@ -143,7 +143,7 @@ public static int ProcessId /// /// Path of the executable that started the currently executing process /// - /// If the executable is renamed or deleted, the return value is undefined and depends on the operating system. + /// If the executable is renamed or deleted before this property is first accessed, the return value is undefined and depends on the operating system. /// public static string? ProcessPath { From d4e4182a165e770334dcca8b14fddf45098c3e1c Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 30 Sep 2020 23:42:03 -0700 Subject: [PATCH 14/14] Typo --- .../System.Runtime.Extensions/tests/System/EnvironmentTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs index f6c29649da237..8007fa8f2ec9f 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs @@ -96,7 +96,7 @@ public void ProcessPath_Idempotent() } [Fact] - public void ProcessPath_MatchedExpectedValue() + public void ProcessPath_MatchesExpectedValue() { string expectedProcessPath = PlatformDetection.IsBrowser ? null : Process.GetCurrentProcess().MainModule.FileName; Assert.Equal(expectedProcessPath, Environment.ProcessPath);