@@ -748,16 +748,63 @@ def _use_posix_spawn():
748748 # By default, assume that posix_spawn() does not properly report errors.
749749 return False
750750
751- _CAN_USE_KQUEUE = all (
752- hasattr (select , x )
753- for x in (
754- "kqueue" ,
755- "KQ_EV_ADD" ,
756- "KQ_EV_ONESHOT" ,
757- "KQ_FILTER_PROC" ,
758- "KQ_NOTE_EXIT" ,
759- )
760- )
751+
752+ def _can_use_pidfd_open ():
753+ # Availability: Linux >= 5.3
754+ if not hasattr (os , "pidfd_open" ):
755+ return False
756+ try :
757+ pidfd = os .pidfd_open (os .getpid (), 0 )
758+ except OSError as err :
759+ if err .errno in {errno .EMFILE , errno .ENFILE }:
760+ # transitory 'too many open files'
761+ return True
762+ # likely blocked by security policy like SECCOMP (EPERM,
763+ # EACCES, ENOSYS)
764+ return False
765+ else :
766+ os .close (pidfd )
767+ return True
768+
769+
770+ def _can_use_kqueue ():
771+ # Availability: macOS, BSD
772+ if not all (
773+ hasattr (select , x )
774+ for x in (
775+ "kqueue" ,
776+ "KQ_EV_ADD" ,
777+ "KQ_EV_ONESHOT" ,
778+ "KQ_FILTER_PROC" ,
779+ "KQ_NOTE_EXIT" ,
780+ )
781+ ):
782+ return False
783+
784+ kq = None
785+ try :
786+ kq = select .kqueue ()
787+ kev = select .kevent (
788+ os .getpid (),
789+ filter = select .KQ_FILTER_PROC ,
790+ flags = select .KQ_EV_ADD | select .KQ_EV_ONESHOT ,
791+ fflags = select .KQ_NOTE_EXIT ,
792+ )
793+ events = kq .control ([kev ], 1 , 0 )
794+ return True
795+ except OSError as err :
796+ if err .errno in {errno .EMFILE , errno .ENFILE }:
797+ # transitory 'too many open files'
798+ return True
799+ return False
800+ finally :
801+ if kq is not None :
802+ kq .close ()
803+
804+
805+ _CAN_USE_PIDFD_OPEN = _can_use_pidfd_open ()
806+ _CAN_USE_KQUEUE = _can_use_kqueue ()
807+
761808
762809# These are primarily fail-safe knobs for negatives. A True value does not
763810# guarantee the given libc/syscall API will be used.
@@ -2061,7 +2108,7 @@ def _wait_pidfd(self, timeout):
20612108 """Wait for PID to terminate using pidfd_open() + poll().
20622109 Linux >= 5.3 only.
20632110 """
2064- if not hasattr ( os , "pidfd_open" ) :
2111+ if not _CAN_USE_PIDFD_OPEN :
20652112 return False
20662113 try :
20672114 pidfd = os .pidfd_open (self .pid , 0 )
@@ -2091,7 +2138,7 @@ def _wait_kqueue(self, timeout):
20912138 try :
20922139 kq = select .kqueue ()
20932140 except OSError :
2094- # usually EMFILE / ENFILE (too many open files)
2141+ # likely EMFILE / ENFILE (too many open files)
20952142 return False
20962143
20972144 try :
@@ -2103,7 +2150,7 @@ def _wait_kqueue(self, timeout):
21032150 )
21042151 try :
21052152 events = kq .control ([kev ], 1 , timeout ) # wait
2106- except OSError as err :
2153+ except OSError as err : # should never happen
21072154 return False
21082155 if not events :
21092156 raise TimeoutExpired (self .args , timeout )
0 commit comments