Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Switch the attr name to _USE_VFORK for consistency
No double negatives. Also documents _USE_POSIX_SPAWN.
  • Loading branch information
gpshead committed Apr 25, 2022
commit c99fb75786c41ef36bf4e5606474730b0c92e036
32 changes: 21 additions & 11 deletions Doc/library/subprocess.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1532,26 +1532,36 @@ runtime):


.. _disable_vfork:
.. _disable_posix_spawn:

Disabling potential use of ``vfork()``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Disabling use of ``vfork()`` or ``posix_spawn()``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

On Linux, :mod:`subprocess` defaults to using the ``vfork()`` system call
internally when it is safe to do so rather than ``fork()``. This greatly
improves performance.

If you ever encounter a presumed highly-unusual situation where you need to
prevent ``vfork()`` from being used by Python, you can set the
:attr:`subprocess.disable_vfork_reason` attribute to a non-empty string.
Ideally one describing why and linking to a bug report explaining how to setup
an environment with code to reproduce the issue preventing it from working
properly. Without recording that information, nobody will understand when they
can unset it in your code in the future.
:attr:`subprocess._USE_VFORK` attribute to a false value.

subprocess._USE_VFORK = False # See CPython issue gh-NNNNNN.

Setting this has no impact on use of ``posix_spawn()`` which could use
``vfork()`` within its libc implementation.
``vfork()`` internally within its libc implementation. There is a similar
:attr:`subprocess._USE_POSIX_SPAWN` attribute if you need to prevent use of
that.

subprocess._USE_POSIX_SPAWN = False # See CPython issue gh-NNNNNN.

It is safe to set this attribute on older Python versions. Do not assume it
exists to be read until 3.11.
It is safe to set these to false on any Python version. They will have no
effect on older versions when unsupported. Do not assume the attributes are
available to read. Despite their names, a true value does not indicate that the
corresponding function will be used, only that that it may be.

Please file issues any time you have to use these private knobs with a way to
reproduce the issue you were seeing. Link to that issue from a comment in your
code.

.. versionadded:: 3.11 ``disable_vfork_reason``
.. versionadded:: 3.8 ``_USE_POSIX_SPAWN``
.. versionadded:: 3.11 ``_USE_VFORK``

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never tried this syntax, I don't know if it's properly rendered. You will see :-)

2 changes: 1 addition & 1 deletion Lib/multiprocessing/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ def spawnv_passfds(path, args, passfds):
args, [os.fsencode(path)], True, passfds, None, None,
-1, -1, -1, -1, -1, -1, errpipe_read, errpipe_write,
False, False, None, None, None, -1, None,
subprocess.disable_vfork_reason)
subprocess._USE_VFORK)
finally:
os.close(errpipe_read)
os.close(errpipe_write)
Expand Down
11 changes: 2 additions & 9 deletions Lib/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,6 @@ def _fork_exec(*args, **kwargs):
import selectors


# This is purely a failsafe. If non-empty the _posixsubprocess code will never
# consider using vfork(). While any true value will trigger this, set it to a
# description of why it is being disabled. With a link to a bug report
# explaining how to setup an environment with code to reproduce the issue this
# is working around. Otherwise nobody will understand when they can unset it.
disable_vfork_reason = ""


# Exception classes used by this module.
class SubprocessError(Exception): pass

Expand Down Expand Up @@ -711,6 +703,7 @@ def _use_posix_spawn():


_USE_POSIX_SPAWN = _use_posix_spawn()
_USE_VFORK = sys.platform == 'linux'


class Popen:
Expand Down Expand Up @@ -1800,7 +1793,7 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
errpipe_read, errpipe_write,
restore_signals, start_new_session,
gid, gids, uid, umask,
preexec_fn, disable_vfork_reason)
preexec_fn, _USE_VFORK)
self._child_created = True
finally:
# be sure the FD is closed no matter what
Expand Down
5 changes: 2 additions & 3 deletions Lib/test/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -3107,7 +3107,7 @@ def test_fork_exec(self):
1, 2, 3, 4,
True, True,
False, [], 0, -1,
func, "")
func, False)
# Attempt to prevent
# "TypeError: fork_exec() takes exactly N arguments (M given)"
# from passing the test. More refactoring to have us start
Expand Down Expand Up @@ -3614,8 +3614,7 @@ def test_getoutput(self):

def test__all__(self):
"""Ensure that __all__ is populated properly."""
intentionally_excluded = {"list2cmdline", "Handle", "pwd", "grp",
"fcntl", "disable_vfork_reason"}
intentionally_excluded = {"list2cmdline", "Handle", "pwd", "grp", "fcntl"}
exported = set(subprocess.__all__)
possible_exports = set()
import types
Expand Down
6 changes: 3 additions & 3 deletions Modules/_posixsubprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
Py_ssize_t arg_num, num_groups = 0;
int need_after_fork = 0;
int saved_errno = 0;
int disable_vfork;
int allow_vfork;

if (!PyArg_ParseTuple(
args, "OOpO!OOiiiiiiiiiiOOOiOp:fork_exec",
Expand All @@ -762,7 +762,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
&errread, &errwrite, &errpipe_read, &errpipe_write,
&restore_signals, &call_setsid,
&gid_object, &groups_list, &uid_object, &child_umask,
&preexec_fn, &disable_vfork))
&preexec_fn, &allow_vfork))
return NULL;

if ((preexec_fn != Py_None) &&
Expand Down Expand Up @@ -941,7 +941,7 @@ subprocess_fork_exec(PyObject *module, PyObject *args)
#ifdef VFORK_USABLE
/* Use vfork() only if it's safe. See the comment above child_exec(). */
sigset_t old_sigs;
if (preexec_fn == Py_None && !disable_vfork &&
if (preexec_fn == Py_None && allow_vfork &&
!call_setuid && !call_setgid && !call_setgroups) {
/* Block all signals to ensure that no signal handlers are run in the
* child process while it shares memory with us. Note that signals
Expand Down