Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/multiprocessing/forkserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
except ImportError:
pass

# gh-135335: flush stdout/stderr in case any of the preloaded modules
# wrote to them, otherwise children might inherit buffered data
util._flush_std_streams()

util._close_stdin()

sig_r, sig_w = os.pipe()
Expand Down
26 changes: 26 additions & 0 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6801,6 +6801,32 @@ def test_child_sys_path(self):
self.assertEqual(child_sys_path[1:], sys.path[1:])
self.assertIsNone(import_error, msg=f"child could not import {self._mod_name}")

def test_std_streams_flushed_after_preload(self):
# gh-135335: Check fork server flushes standard streams after
# preloading modules
if multiprocessing.get_start_method() != "forkserver":
self.skipTest("forkserver specific test")

# Create a test module in the temporary directory on the child's path
# TODO: This can all be simplified once gh-126631 is fixed and we can
# use __main__ instead of a module.
os.mkdir(os.path.join(self._temp_dir, 'a'))
with open(os.path.join(self._temp_dir, 'a', '__init__.py'), "w") as f:
f.write('''if 1:
import sys
print('stdout', file=sys.stdout)
print('stderr', file=sys.stderr)''')

name = os.path.join(os.path.dirname(__file__), 'mp_preload_flush.py')
env = {'PYTHONPATH': self._temp_dir}
rc, out, err = test.support.script_helper.assert_python_ok(name, **env)
self.assertEqual(rc, 0)

# We want to see all the output if it isn't as expected
self.maxDiff = None
self.assertEqual(err.decode().rstrip(), 'stderr')
self.assertEqual(out.decode().rstrip(), 'stdout')


class MiscTestCase(unittest.TestCase):
def test__all__(self):
Expand Down
12 changes: 12 additions & 0 deletions Lib/test/mp_preload_flush.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import multiprocessing
import sys

if __name__ == '__main__':
assert 'a' not in sys.modules
multiprocessing.set_forkserver_preload(['a'])
for _ in range(2):
p = multiprocessing.Process()
p.start()
p.join()
else:
assert 'a' in sys.modules
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Flush ``stdout`` and ``stderr`` after preloading modules in the
:mod:`multiprocessing` ``forkserver``.
Loading