Skip to content

Commit b2f3cdb

Browse files
Merge remote-tracking branch 'upstream/main' into dynamic_opcode_targets
2 parents 867f283 + 6504f20 commit b2f3cdb

File tree

23 files changed

+611
-370
lines changed

23 files changed

+611
-370
lines changed

Doc/c-api/init.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,10 @@ pointer and a void pointer argument.
18341834
called from the main interpreter. Each subinterpreter now has its own
18351835
list of scheduled calls.
18361836
1837+
.. versionchanged:: 3.12
1838+
This function now always schedules *func* to be run in the main
1839+
interpreter.
1840+
18371841
.. _profiling:
18381842
18391843
Profiling and Tracing

Include/cpython/pystate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ struct _ts {
107107
# define _PyThreadState_WHENCE_THREADING 3
108108
# define _PyThreadState_WHENCE_GILSTATE 4
109109
# define _PyThreadState_WHENCE_EXEC 5
110+
# define _PyThreadState_WHENCE_THREADING_DAEMON 6
110111
#endif
111112

112113
/* Currently holds the GIL. Must be its own field to avoid data races */

Lib/test/test_atexit.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,62 @@ def thready():
7979
# want them to affect the rest of the tests.
8080
script_helper.assert_python_ok("-c", textwrap.dedent(source))
8181

82+
@threading_helper.requires_working_threading()
83+
def test_thread_created_in_atexit(self):
84+
source = """if True:
85+
import atexit
86+
import threading
87+
import time
88+
89+
90+
def run():
91+
print(24)
92+
time.sleep(1)
93+
print(42)
94+
95+
@atexit.register
96+
def start_thread():
97+
threading.Thread(target=run).start()
98+
"""
99+
return_code, stdout, stderr = script_helper.assert_python_ok("-c", source)
100+
self.assertEqual(return_code, 0)
101+
self.assertEqual(stdout, f"24{os.linesep}42{os.linesep}".encode("utf-8"))
102+
self.assertEqual(stderr, b"")
103+
104+
@threading_helper.requires_working_threading()
105+
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
106+
def test_thread_created_in_atexit_subinterpreter(self):
107+
try:
108+
from concurrent import interpreters
109+
except ImportError:
110+
self.skipTest("subinterpreters are not available")
111+
112+
read, write = os.pipe()
113+
source = f"""if True:
114+
import atexit
115+
import threading
116+
import time
117+
import os
118+
119+
def run():
120+
os.write({write}, b'spanish')
121+
time.sleep(1)
122+
os.write({write}, b'inquisition')
123+
124+
@atexit.register
125+
def start_thread():
126+
threading.Thread(target=run).start()
127+
"""
128+
interp = interpreters.create()
129+
try:
130+
interp.exec(source)
131+
132+
# Close the interpreter to invoke atexit callbacks
133+
interp.close()
134+
self.assertEqual(os.read(read, 100), b"spanishinquisition")
135+
finally:
136+
os.close(read)
137+
os.close(write)
82138

83139
@support.cpython_only
84140
class SubinterpreterTest(unittest.TestCase):

Lib/test/test_capi/test_misc.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from test import support
2323
from test.support import MISSING_C_DOCSTRINGS
2424
from test.support import import_helper
25+
from test.support import script_helper
2526
from test.support import threading_helper
2627
from test.support import warnings_helper
2728
from test.support import requires_limited_api
@@ -1641,6 +1642,36 @@ def subthread():
16411642

16421643
self.assertEqual(actual, int(interpid))
16431644

1645+
@threading_helper.requires_working_threading()
1646+
def test_pending_call_creates_thread(self):
1647+
source = """
1648+
import _testinternalcapi
1649+
import threading
1650+
import time
1651+
1652+
1653+
def output():
1654+
print(24)
1655+
time.sleep(1)
1656+
print(42)
1657+
1658+
1659+
def callback():
1660+
threading.Thread(target=output).start()
1661+
1662+
1663+
def create_pending_call():
1664+
time.sleep(1)
1665+
_testinternalcapi.simple_pending_call(callback)
1666+
1667+
1668+
threading.Thread(target=create_pending_call).start()
1669+
"""
1670+
return_code, stdout, stderr = script_helper.assert_python_ok('-c', textwrap.dedent(source))
1671+
self.assertEqual(return_code, 0)
1672+
self.assertEqual(stdout, f"24{os.linesep}42{os.linesep}".encode("utf-8"))
1673+
self.assertEqual(stderr, b"")
1674+
16441675

16451676
class SubinterpreterTest(unittest.TestCase):
16461677

@@ -1949,6 +1980,41 @@ def test_module_state_shared_in_global(self):
19491980
subinterp_attr_id = os.read(r, 100)
19501981
self.assertEqual(main_attr_id, subinterp_attr_id)
19511982

1983+
@threading_helper.requires_working_threading()
1984+
@unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()")
1985+
@requires_subinterpreters
1986+
def test_pending_call_creates_thread_subinterpreter(self):
1987+
interpreters = import_helper.import_module("concurrent.interpreters")
1988+
r, w = os.pipe()
1989+
source = f"""if True:
1990+
import _testinternalcapi
1991+
import threading
1992+
import time
1993+
import os
1994+
1995+
1996+
def output():
1997+
time.sleep(1)
1998+
os.write({w}, b"x")
1999+
2000+
2001+
def callback():
2002+
threading.Thread(target=output).start()
2003+
2004+
2005+
def create_pending_call():
2006+
time.sleep(1)
2007+
_testinternalcapi.simple_pending_call(callback)
2008+
2009+
2010+
threading.Thread(target=create_pending_call).start()
2011+
"""
2012+
interp = interpreters.create()
2013+
interp.exec(source)
2014+
interp.close()
2015+
data = os.read(r, 1)
2016+
self.assertEqual(data, b"x")
2017+
19522018

19532019
@requires_subinterpreters
19542020
class InterpreterConfigTests(unittest.TestCase):

0 commit comments

Comments
 (0)