@@ -304,7 +304,7 @@ internal func monitorProcessTermination(
304
304
// pidfd is only supported on Linux kernel 5.4 and above
305
305
// On older releases, use signalfd so we do not need
306
306
// to register anything with epoll
307
- if _isLinuxKernelAtLeast ( major : 5 , minor : 4 ) {
307
+ if processIdentifier . processFileDescriptor > 0 {
308
308
// Register processFileDescriptor with epoll
309
309
var event = epoll_event (
310
310
events: EPOLLIN . rawValue,
@@ -418,8 +418,10 @@ private extension siginfo_t {
418
418
}
419
419
}
420
420
421
- // Okay to be unlocked global mutable because this thread is only created once
421
+ // Okay to be unlocked global mutable because this value is only set once like dispatch_once
422
422
private nonisolated ( unsafe) var _signalPipe: ( readEnd: CInt , writeEnd: CInt ) = ( readEnd: - 1 , writeEnd: - 1 )
423
+ // Okay to be unlocked global mutable because this value is only set once like dispatch_once
424
+ private nonisolated ( unsafe) var _waitProcessFileDescriptorSupported = false
423
425
private let _processMonitorState : Mutex < ProcessMonitorState > = . init( . notStarted)
424
426
425
427
private func shutdown( ) {
@@ -518,7 +520,7 @@ private func monitorThreadFunc(args: UnsafeMutableRawPointer?) -> UnsafeMutableR
518
520
}
519
521
520
522
// P_PIDFD requires Linux Kernel 5.4 and above
521
- if _isLinuxKernelAtLeast ( major : 5 , minor : 4 ) {
523
+ if _waitProcessFileDescriptorSupported {
522
524
_blockAndWaitForProcessFileDescriptor ( targetFileDescriptor, context: context)
523
525
} else {
524
526
_reapAllKnownChildProcesses ( targetFileDescriptor, context: context)
@@ -569,9 +571,9 @@ private let setup: () = {
569
571
return
570
572
}
571
573
572
- // On Linux kernel lower than 5.4 we have to rely on signal handler
574
+ // If the current kernel does not support pidfd, fallback to signal handler
573
575
// Create the self-pipe that signal handler writes to
574
- if !_isLinuxKernelAtLeast ( major : 5 , minor : 4 ) {
576
+ if !_isWaitProcessFileDescriptorSupported ( ) {
575
577
var pipeCreationError : SubprocessError ? = nil
576
578
do {
577
579
let ( readEnd, writeEnd) = try FileDescriptor . pipe ( )
@@ -608,6 +610,9 @@ private let setup: () = {
608
610
_reportFailureWithErrno ( errno)
609
611
return
610
612
}
613
+ } else {
614
+ // Mark waitid(P_PIDFD) as supported
615
+ _waitProcessFileDescriptorSupported = true
611
616
}
612
617
let monitorThreadContext = MonitorThreadContext (
613
618
epollFileDescriptor: epollFileDescriptor,
@@ -755,36 +760,25 @@ private func _reapAllKnownChildProcesses(_ signalFd: CInt, context: MonitorThrea
755
760
}
756
761
}
757
762
758
- private func _isLinuxKernelAtLeast( major: Int , minor: Int ) -> Bool {
759
- var buffer = utsname ( )
760
- guard uname ( & buffer) == 0 else {
761
- return false
762
- }
763
- let releaseString = withUnsafeBytes ( of: & buffer. release) { ptr in
764
- let buffer = ptr. bindMemory ( to: CChar . self)
765
- return String ( cString: buffer. baseAddress!)
766
- }
767
-
768
- // utsname release follows the format
769
- // major.minor.patch-extra
770
- guard let mainVersion = releaseString. split ( separator: " - " ) . first else {
771
- return false
772
- }
773
-
774
- let versionComponents = mainVersion. split ( separator: " . " )
775
-
776
- guard let currentMajor = Int ( versionComponents [ 0 ] ) ,
777
- let currentMinor = Int ( versionComponents [ 1 ] ) else {
778
- return false
779
- }
780
-
781
- if currentMajor > major {
782
- return true
783
- } else if currentMajor == major {
784
- return currentMinor >= minor
785
- } else {
763
+ internal func _isWaitProcessFileDescriptorSupported( ) -> Bool {
764
+ // waitid(P_PIDFD) is only supported on Linux kernel 5.4 and above
765
+ // Prob whether the current system supports it by calling it with self pidfd
766
+ // and checking for EINVAL (waitid sets errno to EINVAL if it does not
767
+ // recognize the id type).
768
+ var siginfo = siginfo_t ( )
769
+ let selfPidfd = _pidfd_open ( getpid ( ) )
770
+ if selfPidfd < 0 {
771
+ // If we can not retrieve pidfd, the system does not support waitid(P_PIDFD)
786
772
return false
787
773
}
774
+ /// The following call will fail either with
775
+ /// - ECHILD: in this case we know P_PIDFD is supported and waitid correctly
776
+ /// reported that we don't have a child with the same selfPidfd;
777
+ /// - EINVAL: in this case we know P_PIDFD is not supported because it does not
778
+ /// recognize the `P_PIDFD` type
779
+ waitid ( idtype_t ( UInt32 ( P_PIDFD) ) , id_t ( selfPidfd) , & siginfo, WEXITED | WNOWAIT)
780
+ return errno == ECHILD
788
781
}
789
782
783
+
790
784
#endif // canImport(Glibc) || canImport(Android) || canImport(Musl)
0 commit comments