@@ -304,7 +304,7 @@ internal func monitorProcessTermination(
304304 // pidfd is only supported on Linux kernel 5.4 and above
305305 // On older releases, use signalfd so we do not need
306306 // to register anything with epoll
307- if _isLinuxKernelAtLeast ( major : 5 , minor : 4 ) {
307+ if processIdentifier . processFileDescriptor > 0 {
308308 // Register processFileDescriptor with epoll
309309 var event = epoll_event (
310310 events: EPOLLIN . rawValue,
@@ -418,8 +418,10 @@ private extension siginfo_t {
418418 }
419419}
420420
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
422422private 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
423425private let _processMonitorState : Mutex < ProcessMonitorState > = . init( . notStarted)
424426
425427private func shutdown( ) {
@@ -518,7 +520,7 @@ private func monitorThreadFunc(args: UnsafeMutableRawPointer?) -> UnsafeMutableR
518520 }
519521
520522 // P_PIDFD requires Linux Kernel 5.4 and above
521- if _isLinuxKernelAtLeast ( major : 5 , minor : 4 ) {
523+ if _waitProcessFileDescriptorSupported {
522524 _blockAndWaitForProcessFileDescriptor ( targetFileDescriptor, context: context)
523525 } else {
524526 _reapAllKnownChildProcesses ( targetFileDescriptor, context: context)
@@ -569,9 +571,9 @@ private let setup: () = {
569571 return
570572 }
571573
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
573575 // Create the self-pipe that signal handler writes to
574- if !_isLinuxKernelAtLeast ( major : 5 , minor : 4 ) {
576+ if !_isWaitProcessFileDescriptorSupported ( ) {
575577 var pipeCreationError : SubprocessError ? = nil
576578 do {
577579 let ( readEnd, writeEnd) = try FileDescriptor . pipe ( )
@@ -608,6 +610,9 @@ private let setup: () = {
608610 _reportFailureWithErrno ( errno)
609611 return
610612 }
613+ } else {
614+ // Mark waitid(P_PIDFD) as supported
615+ _waitProcessFileDescriptorSupported = true
611616 }
612617 let monitorThreadContext = MonitorThreadContext (
613618 epollFileDescriptor: epollFileDescriptor,
@@ -755,36 +760,25 @@ private func _reapAllKnownChildProcesses(_ signalFd: CInt, context: MonitorThrea
755760 }
756761}
757762
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)
786772 return false
787773 }
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
788781}
789782
783+
790784#endif // canImport(Glibc) || canImport(Android) || canImport(Musl)
0 commit comments