Skip to content

Commit 1a20748

Browse files
committed
Behavior changed to raise an exception when fork is used within a thread. Started to fix tests.
1 parent ce90d77 commit 1a20748

2 files changed

Lines changed: 26 additions & 23 deletions

File tree

Lib/test/_test_multiprocessing.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
from test.support import socket_helper
4040
from test.support import threading_helper
4141
from test.support import warnings_helper
42-
from test.support.script_helper import run_python_until_end
42+
from test.support.script_helper import assert_python_failure, assert_python_ok
4343

4444
# Skip tests if _multiprocessing wasn't built.
4545
_multiprocessing = import_helper.import_module('_multiprocessing')
@@ -6284,7 +6284,15 @@ def test_list(self):
62846284

62856285
def setUp(self):
62866286
self.manager = self.manager_class()
6287-
self.manager.start()
6287+
# gh-135427
6288+
# In some of the tests, a forked child forks another child of itself. In that case, using
6289+
# warnings_helper.ignore_warnings decorator does not actually ignore the warning from that
6290+
# child of child, and a warnings_helper.ignore_warnings exception is raised.
6291+
with warnings.catch_warnings():
6292+
warnings.filterwarnings('ignore',
6293+
message=".*fork.*may lead to deadlocks in the child.*",
6294+
category=DeprecationWarning)
6295+
self.manager.start()
62886296
self.proc = None
62896297

62906298
def tearDown(self):
@@ -7165,15 +7173,13 @@ def test_fork(self):
71657173
print("In parent")
71667174
"""
71677175

7168-
res, _ = run_python_until_end("-c", code, PYTHONWARNINGS='always')
7176+
res = assert_python_ok("-c", code, PYTHONWARNINGS='always')
71697177
self.assertIn(b'In child', res.out)
71707178
self.assertIn(b'In parent', res.out)
71717179
self.assertIn(b'DeprecationWarning', res.err)
71727180
self.assertIn(b'is multi-threaded, use of fork() may lead to deadlocks in the child', res.err)
71737181

7174-
res, _ = run_python_until_end("-c", code, PYTHONWARNINGS='error')
7175-
self.assertIn(b'In child', res.out)
7176-
self.assertIn(b'In parent', res.out)
7182+
res = assert_python_failure("-c", code, PYTHONWARNINGS='error')
71777183
self.assertIn(b'DeprecationWarning', res.err)
71787184
self.assertIn(b'is multi-threaded, use of fork() may lead to deadlocks in the child', res.err)
71797185

@@ -7195,14 +7201,11 @@ def test_forkpty(self):
71957201
print(f"In parent")
71967202
"""
71977203

7198-
res, _ = run_python_until_end("-c", code, PYTHONWARNINGS='always')
7199-
print(res)
7204+
res = assert_python_ok("-c", code, PYTHONWARNINGS='always')
72007205
self.assertIn(b'In parent', res.out)
72017206
self.assertIn(b'DeprecationWarning', res.err)
72027207
self.assertIn(b'is multi-threaded, use of forkpty() may lead to deadlocks in the child', res.err)
72037208

7204-
res, _ = run_python_until_end("-c", code, PYTHONWARNINGS='error')
7205-
print(res)
7206-
self.assertIn(b'In parent', res.out)
7209+
res = assert_python_failure("-c", code, PYTHONWARNINGS='error')
72077210
self.assertIn(b'DeprecationWarning', res.err)
72087211
self.assertIn(b'is multi-threaded, use of forkpty() may lead to deadlocks in the child', res.err)

Modules/posixmodule.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7992,7 +7992,7 @@ os_register_at_fork_impl(PyObject *module, PyObject *before,
79927992
//
79937993
// This should only be called from the parent process after
79947994
// PyOS_AfterFork_Parent().
7995-
static void
7995+
static int
79967996
warn_about_fork_with_threads(const char* name)
79977997
{
79987998
// It's not safe to issue the warning while the world is stopped, because
@@ -8043,22 +8043,22 @@ warn_about_fork_with_threads(const char* name)
80438043
PyObject *threading = PyImport_GetModule(&_Py_ID(threading));
80448044
if (!threading) {
80458045
PyErr_Clear();
8046-
return;
8046+
return 0;
80478047
}
80488048
PyObject *threading_active =
80498049
PyObject_GetAttr(threading, &_Py_ID(_active));
80508050
if (!threading_active) {
80518051
PyErr_Clear();
80528052
Py_DECREF(threading);
8053-
return;
8053+
return 0;
80548054
}
80558055
PyObject *threading_limbo =
80568056
PyObject_GetAttr(threading, &_Py_ID(_limbo));
80578057
if (!threading_limbo) {
80588058
PyErr_Clear();
80598059
Py_DECREF(threading);
80608060
Py_DECREF(threading_active);
8061-
return;
8061+
return 0;
80628062
}
80638063
Py_DECREF(threading);
80648064
// Duplicating what threading.active_count() does but without holding
@@ -8074,7 +8074,7 @@ warn_about_fork_with_threads(const char* name)
80748074
Py_DECREF(threading_limbo);
80758075
}
80768076
if (num_python_threads > 1) {
8077-
PyErr_WarnFormat(
8077+
return PyErr_WarnFormat(
80788078
PyExc_DeprecationWarning, 1,
80798079
#ifdef HAVE_GETPID
80808080
"This process (pid=%d) is multi-threaded, "
@@ -8086,11 +8086,8 @@ warn_about_fork_with_threads(const char* name)
80868086
getpid(),
80878087
#endif
80888088
name);
8089-
if (PyErr_Occurred()) {
8090-
PyErr_WriteUnraisable(NULL);
8091-
PyErr_Clear();
8092-
}
80938089
}
8090+
return 0;
80948091
}
80958092
#endif // HAVE_FORK1 || HAVE_FORKPTY || HAVE_FORK
80968093

@@ -8129,7 +8126,8 @@ os_fork1_impl(PyObject *module)
81298126
/* parent: release the import lock. */
81308127
PyOS_AfterFork_Parent();
81318128
// After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
8132-
warn_about_fork_with_threads("fork1");
8129+
if (warn_about_fork_with_threads("fork1") < 0)
8130+
return NULL;
81338131
}
81348132
if (pid == -1) {
81358133
errno = saved_errno;
@@ -8178,7 +8176,8 @@ os_fork_impl(PyObject *module)
81788176
/* parent: release the import lock. */
81798177
PyOS_AfterFork_Parent();
81808178
// After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
8181-
warn_about_fork_with_threads("fork");
8179+
if (warn_about_fork_with_threads("fork") < 0)
8180+
return NULL;
81828181
}
81838182
if (pid == -1) {
81848183
errno = saved_errno;
@@ -9033,7 +9032,8 @@ os_forkpty_impl(PyObject *module)
90339032
/* parent: release the import lock. */
90349033
PyOS_AfterFork_Parent();
90359034
// After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
9036-
warn_about_fork_with_threads("forkpty");
9035+
if (warn_about_fork_with_threads("forkpty") < 0)
9036+
return NULL;
90379037
}
90389038
if (pid == -1) {
90399039
return posix_error();

0 commit comments

Comments
 (0)