Skip to content

Commit 32312d5

Browse files
authored
Emulate POSIX_SPAWN_CLOEXEC_DEFAULT in fork/exec (#79)
* Emulate POSIX_SPAWN_CLOEXEC_DEFAULT in fork/exec. POSIX_SPAWN_CLOEXEC_DEFAULT is only available on Darwin. Emulate POSIX_SPAWN_CLOEXEC_DEFAULT on other platforms by calling close after fork, before exec. This commit also removes _subprocess_posix_spawn_fallback because we can't emulate POSIX_SPAWN_CLOEXEC_DEFAULT in a thread-safe manner while using posix_spawn. * Use NSIG_MAX insead of a constant * Update Linux testSuspendResumeProcess to use waitpid instead of polling * Use closefrom on OpenBSD instead of looping through fds to close
1 parent f04c17d commit 32312d5

File tree

6 files changed

+260
-244
lines changed

6 files changed

+260
-244
lines changed

Sources/Subprocess/Platforms/Subprocess+Linux.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ internal func monitorProcessTermination(
389389
if siginfo.si_pid == 0 && siginfo.si_signo == 0 {
390390
// Save this continuation to be called by signal hander
391391
var newState = storage
392-
newState.continuations[processIdentifier.processDescriptor] = continuation
392+
newState.continuations[processIdentifier.value] = continuation
393393
state = .started(newState)
394394
return nil
395395
}
@@ -472,7 +472,7 @@ internal extension siginfo_t {
472472
// Okay to be unlocked global mutable because this value is only set once like dispatch_once
473473
private nonisolated(unsafe) var _signalPipe: (readEnd: CInt, writeEnd: CInt) = (readEnd: -1, writeEnd: -1)
474474
// Okay to be unlocked global mutable because this value is only set once like dispatch_once
475-
private nonisolated(unsafe) var _waitprocessDescriptorSupported = false
475+
private nonisolated(unsafe) var _waitProcessDescriptorSupported = false
476476
private let _processMonitorState: Mutex<ProcessMonitorState> = .init(.notStarted)
477477

478478
private func shutdown() {
@@ -568,8 +568,8 @@ private func monitorThreadFunc(context: MonitorThreadContext) {
568568
}
569569

570570
// P_PIDFD requires Linux Kernel 5.4 and above
571-
if _waitprocessDescriptorSupported {
572-
_blockAndWaitForprocessDescriptor(targetFileDescriptor, context: context)
571+
if _waitProcessDescriptorSupported {
572+
_blockAndWaitForProcessDescriptor(targetFileDescriptor, context: context)
573573
} else {
574574
_reapAllKnownChildProcesses(targetFileDescriptor, context: context)
575575
}
@@ -658,7 +658,7 @@ private let setup: () = {
658658
}
659659
} else {
660660
// Mark waitid(P_PIDFD) as supported
661-
_waitprocessDescriptorSupported = true
661+
_waitProcessDescriptorSupported = true
662662
}
663663
let monitorThreadContext = MonitorThreadContext(
664664
epollFileDescriptor: epollFileDescriptor,
@@ -705,7 +705,7 @@ internal func _setupMonitorSignalHandler() {
705705
setup
706706
}
707707

708-
private func _blockAndWaitForprocessDescriptor(_ pidfd: CInt, context: MonitorThreadContext) {
708+
private func _blockAndWaitForProcessDescriptor(_ pidfd: CInt, context: MonitorThreadContext) {
709709
var terminationStatus: Result<TerminationStatus, SubprocessError>
710710

711711
var siginfo = siginfo_t()

Sources/Subprocess/Platforms/Subprocess+Unix.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,19 +112,30 @@ extension Execution {
112112

113113
#if os(Linux) || os(Android)
114114
// On linux, use pidfd_send_signal if possible
115-
if shouldSendToProcessGroup {
115+
if shouldSendToProcessGroup || self.processIdentifier.processDescriptor < 0 {
116116
// pidfd_send_signal does not support sending signal to process group
117117
try _kill(pid, signal: signal)
118118
} else {
119-
guard _pidfd_send_signal(
119+
let rc = _pidfd_send_signal(
120120
processIdentifier.processDescriptor,
121121
signal.rawValue
122-
) == 0 else {
123-
throw SubprocessError(
124-
code: .init(.failedToSendSignal(signal.rawValue)),
125-
underlyingError: .init(rawValue: errno)
126-
)
122+
)
123+
let capturedErrno = errno
124+
if rc == 0 {
125+
// _pidfd_send_signal succeeded
126+
return
127127
}
128+
if capturedErrno == ENOSYS {
129+
// _pidfd_send_signal is not implemented. Fallback to kill
130+
try _kill(pid, signal: signal)
131+
return
132+
}
133+
134+
// Throw all other errors
135+
throw SubprocessError(
136+
code: .init(.failedToSendSignal(signal.rawValue)),
137+
underlyingError: .init(rawValue: capturedErrno)
138+
)
128139
}
129140
#else
130141
try _kill(pid, signal: signal)

0 commit comments

Comments
 (0)