@@ -883,19 +883,20 @@ def Start(self, why):
883
883
884
884
return pid
885
885
886
- def Wait (self , waiter ):
887
- # type: (Waiter) -> int
886
+ def Wait (self , waiter , eintr_retry = True ):
887
+ # type: (Waiter, bool ) -> int
888
888
"""Wait for this process to finish."""
889
889
while True :
890
- if not waiter .WaitForOne ():
890
+ if not waiter .WaitForOne (eintr_retry ):
891
891
break
892
892
if self .state != job_state_e .Running :
893
893
break
894
894
return self .status
895
895
896
896
def JobWait (self , waiter ):
897
897
# type: (Waiter) -> job_status_t
898
- exit_code = self .Wait (waiter )
898
+ # wait builtin can be interrupted
899
+ exit_code = self .Wait (waiter , eintr_retry = False )
899
900
return job_status .Proc (exit_code )
900
901
901
902
def WhenStopped (self ):
@@ -1011,8 +1012,8 @@ def LastPid(self):
1011
1012
"""
1012
1013
return self .pids [- 1 ]
1013
1014
1014
- def Wait (self , waiter ):
1015
- # type: (Waiter) -> List[int]
1015
+ def Wait (self , waiter , eintr_retry = True ):
1016
+ # type: (Waiter, bool ) -> List[int]
1016
1017
"""Wait for this pipeline to finish.
1017
1018
1018
1019
Called by the 'wait' builtin.
@@ -1022,7 +1023,7 @@ def Wait(self, waiter):
1022
1023
assert self .procs , "no procs for Wait()"
1023
1024
while True :
1024
1025
#log('WAIT pipeline')
1025
- if not waiter .WaitForOne ():
1026
+ if not waiter .WaitForOne (eintr_retry ):
1026
1027
break
1027
1028
if self .state != job_state_e .Running :
1028
1029
#log('Pipeline DONE')
@@ -1032,7 +1033,8 @@ def Wait(self, waiter):
1032
1033
1033
1034
def JobWait (self , waiter ):
1034
1035
# type: (Waiter) -> job_status_t
1035
- pipe_status = self .Wait (waiter )
1036
+ # wait builtin can be interrupted
1037
+ pipe_status = self .Wait (waiter , eintr_retry = False )
1036
1038
return job_status .Pipeline (pipe_status )
1037
1039
1038
1040
def Run (self , waiter , fd_state ):
@@ -1288,31 +1290,58 @@ def __init__(self, job_state, exec_opts, tracer):
1288
1290
self .tracer = tracer
1289
1291
self .last_status = 127 # wait -n error code
1290
1292
1291
- def WaitForOne (self ):
1292
- # type: () -> bool
1293
+ def WaitForOne (self , eintr_retry ):
1294
+ # type: (bool ) -> bool
1293
1295
"""Wait until the next process returns (or maybe Ctrl-C).
1294
1296
1297
+ Args:
1298
+ Should be True to prevent zombies
1299
+
1295
1300
Returns:
1296
1301
True if we got a notification, or False if there was nothing to wait for.
1297
1302
1298
1303
In the interactive shell, we return True if we get a Ctrl-C, so the
1299
1304
caller will try again.
1305
+
1306
+ Callers:
1307
+ wait -n -- WaitForOne() just once
1308
+ wait -- WaitForOne() in a loop
1309
+ wait $! -- job.JobWait()
1310
+
1311
+ Comparisons:
1312
+ bash: jobs.c waitchld() Has a special case macro(!) CHECK_WAIT_INTR for
1313
+ the wait builtin
1314
+
1315
+ dash: jobs.c waitproc() uses sigfillset(), sigprocmask(), etc. Runs in a
1316
+ loop while (gotsigchld), but that might be a hack for System V!
1317
+
1318
+ You could imagine a clean API like posix::wait_for_one()
1319
+
1320
+ wait_result =
1321
+ ECHILD -- nothing to wait for
1322
+ | Done(int pid, int status) -- process done
1323
+ | EINTR(bool sigint) -- may or may not retry
1324
+
1325
+ But I think we want to keep KeyboardInterrupt as an exception for now.
1300
1326
"""
1301
1327
# This is a list of async jobs
1302
1328
try :
1303
- # -1 makes it like wait(), which waits for any process.
1304
- # NOTE: WUNTRACED is necessary to get stopped jobs. What about
1305
- # WCONTINUED?
1329
+ # Notes:
1330
+ # - The arg -1 makes it like wait(), which waits for any process.
1331
+ # - WUNTRACED is necessary to get stopped jobs. What about WCONTINUED?
1332
+ # - Unlike other syscalls, we do NOT try on EINTR, because the 'wait'
1333
+ # builtin should be interruptable. This doesn't appear to cause any
1334
+ # problems for other WaitForOne callers?
1306
1335
pid , status = posix .waitpid (- 1 , WUNTRACED )
1307
1336
except OSError as e :
1308
1337
#log('wait() error: %s', e)
1309
1338
if e .errno == errno_ .ECHILD :
1310
1339
return False # nothing to wait for caller should stop
1340
+ elif e .errno == errno_ .EINTR : # Bug #858 fix
1341
+ return eintr_retry
1311
1342
else :
1312
- # We should never get here. EINTR was handled by the 'posix'
1313
- # module. The only other error is EINVAL, which doesn't apply to
1314
- # this call.
1315
- raise
1343
+ # The signature of waitpid() means this shouldn't happen
1344
+ raise AssertionError ()
1316
1345
except KeyboardInterrupt :
1317
1346
# NOTE: Another way to handle this is to disable SIGINT when a process is
1318
1347
# running. Not sure if there's any real difference. bash and dash
0 commit comments