Skip to content

Commit

Permalink
Add a thread-safe implementation of Process.currentDirectoryURL
Browse files Browse the repository at this point in the history
posix_spawn_file_actions_addchdir_np is the official way to set the working directory of a spawned process and is supported on both macOS and glibc (Linux, etc.). This makes Process.currentDirectoryURL thread-safe, as the current approach will result in the working directory being nondeterministically assigned when spawning processes across multiple threads, using different working directories.
  • Loading branch information
jakepetroules committed Sep 15, 2024
1 parent a12c7d1 commit 84ffdeb
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 11 deletions.
21 changes: 21 additions & 0 deletions Sources/CoreFoundation/CFPlatform.c
Original file line number Diff line number Diff line change
Expand Up @@ -2274,6 +2274,27 @@ CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_
return posix_spawn_file_actions_addclose((posix_spawn_file_actions_t *)file_actions, filedes);
}

CF_EXPORT int _CFPosixSpawnFileActionsChdir(_CFPosixSpawnFileActionsRef file_actions, const char *path) {
#if defined(__GLIBC__) || TARGET_OS_DARWIN || defined(__FreeBSD__) || defined(__ANDROID__)
// Pre-standard posix_spawn_file_actions_addchdir_np version available in:
// - Solaris 11.3 (October 2015)
// - Glibc 2.29 (February 2019)
// - macOS 10.15 (October 2019)
// - musl 1.1.24 (October 2019)
// - FreeBSD 13.1 (May 2022)
// - Android 14 (October 2023)
return posix_spawn_file_actions_addchdir_np((posix_spawn_file_actions_t *)file_actions, path);
#else
// Standardized posix_spawn_file_actions_addchdir version (POSIX.1-2024, June 2024) available in:
// - Solaris 11.4 (August 2018)
// - NetBSD 10.0 (March 2024)
// Currently missing as of:
// - OpenBSD 7.5 (April 2024)
// - QNX 8 (December 2023)
return posix_spawn_file_actions_addchdir((posix_spawn_file_actions_t *)file_actions, path);
#endif
}

CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *_Nullable const argv[_Nullable _CF_RESTRICT], char *_Nullable const envp[_Nullable _CF_RESTRICT]) {
return posix_spawn(pid, path, (posix_spawn_file_actions_t *)file_actions, (posix_spawnattr_t *)attrp, argv, envp);
}
Expand Down
1 change: 1 addition & 0 deletions Sources/CoreFoundation/include/ForSwiftFoundationOnly.h
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ CF_EXPORT int _CFPosixSpawnFileActionsDestroy(_CFPosixSpawnFileActionsRef file_a
CF_EXPORT void _CFPosixSpawnFileActionsDealloc(_CFPosixSpawnFileActionsRef file_actions);
CF_EXPORT int _CFPosixSpawnFileActionsAddDup2(_CFPosixSpawnFileActionsRef file_actions, int filedes, int newfiledes);
CF_EXPORT int _CFPosixSpawnFileActionsAddClose(_CFPosixSpawnFileActionsRef file_actions, int filedes);
CF_EXPORT int _CFPosixSpawnFileActionsChdir(_CFPosixSpawnFileActionsRef file_actions, const char *path);
#ifdef __cplusplus
CF_EXPORT int _CFPosixSpawn(pid_t *_CF_RESTRICT pid, const char *_CF_RESTRICT path, _CFPosixSpawnFileActionsRef file_actions, _CFPosixSpawnAttrRef _Nullable _CF_RESTRICT attrp, char *const argv[], char *const envp[]);
#else
Expand Down
14 changes: 3 additions & 11 deletions Sources/Foundation/Process.swift
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,9 @@ open class Process: NSObject, @unchecked Sendable {
for fd in addclose.filter({ $0 >= 0 }) {
try _throwIfPosixError(_CFPosixSpawnFileActionsAddClose(fileActions, fd))
}
if let dir = currentDirectoryURL?.path {
try _throwIfPosixError(_CFPosixSpawnFileActionsChdir(fileActions, dir))
}

#if canImport(Darwin) || os(Android) || os(OpenBSD)
var spawnAttrs: posix_spawnattr_t? = nil
Expand All @@ -947,17 +950,6 @@ open class Process: NSObject, @unchecked Sendable {
}
#endif

let fileManager = FileManager()
let previousDirectoryPath = fileManager.currentDirectoryPath
if let dir = currentDirectoryURL?.path, !fileManager.changeCurrentDirectoryPath(dir) {
throw _NSErrorWithErrno(errno, reading: true, url: currentDirectoryURL)
}

defer {
// Reset the previous working directory path.
_ = fileManager.changeCurrentDirectoryPath(previousDirectoryPath)
}

// Launch
var pid = pid_t()

Expand Down

0 comments on commit 84ffdeb

Please sign in to comment.