Skip to content

Commit dd7d013

Browse files
committed
mitogen: Kill (hung) bootstrap processes after 5 second timeout
Since using select.select() in the first stage (to handle an obscure corner case where stdin appears to be non-blocking) there has been a report of first stage processes running for ever in an infinite loop - reading 0 bytes from stdin. This attempts to do an end run around that problem by aborting if the bootstrap takes longer than a few seconds for *any* reason. Existing retry logic should deal with it as before. 5 seconds is a best guess at a suitable timeout.
1 parent bd0eed3 commit dd7d013

File tree

2 files changed

+9
-2
lines changed

2 files changed

+9
-2
lines changed

docs/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ To avail of fixes in an unreleased version, please download a ZIP file
2121
In progress (unreleased)
2222
------------------------
2323

24+
* :gh:issue:`1266` :mod:`mitogen`: Prevent hung bootstrap processes, add 5
25+
second timeout to first stage
26+
2427

2528
v0.3.30 (2025-10-30)
2629
--------------------

mitogen/parent.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,10 @@ def _first_stage():
14391439
if os.uname()[0]=='Darwin'and os.uname()[2][:2]in'2021'and sys.version[:3]=='2.7':os.environ['PYTHON_LAUNCHED_FROM_WRAPPER']='1'
14401440
os.environ['ARGV0']=sys.executable
14411441
os.execl(sys.executable,sys.executable+'(mitogen:CONTEXT_NAME)')
1442+
# Timeout execution after a few seconds, to prevent hung processes.
1443+
# Cause might be closed/reset pipe/stream backing stdin (fd=0);
1444+
# or blocking write to interpreter stdin (fd=W, fd=w).
1445+
signal.alarm(5)
14421446
os.write(1,'MITO000\n'.encode())
14431447
C=''.encode()
14441448
while PREAMBLE_COMPRESSED_LEN-len(C)and select.select([0],[],[]):C+=os.read(0,PREAMBLE_COMPRESSED_LEN-len(C))
@@ -1480,11 +1484,11 @@ def get_boot_command(self):
14801484
# Just enough to decode, decompress, and exec the first stage.
14811485
# Priorities: wider compatibility, faster startup, shorter length.
14821486
# `sys.path=...` for https://github.com/python/cpython/issues/115911.
1483-
# `import os,select` here (not stage 1) to save a few bytes overall.
1487+
# `import os,...` here (not stage 1) saves a few bytes overall.
14841488
return self.get_python_argv() + [
14851489
'-c',
14861490
'import sys;sys.path=[p for p in sys.path if p];'
1487-
'import binascii,os,select,zlib;'
1491+
'import binascii,os,select,signal,zlib;'
14881492
'exec(zlib.decompress(binascii.a2b_base64("%s")))' % (encoded.decode(),),
14891493
]
14901494

0 commit comments

Comments
 (0)