Skip to content

Commit f6a4bbb

Browse files
committed
Ensure sufficient stack size for worker threads
1 parent fd6ae27 commit f6a4bbb

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

qasync/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,13 @@ class _QThreadWorker(QtCore.QThread):
102102
For use by the QThreadExecutor
103103
"""
104104

105-
def __init__(self, queue, num):
105+
def __init__(self, queue, num, stackSize=None):
106106
self.__queue = queue
107107
self.__stop = False
108108
self.__num = num
109109
super().__init__()
110+
if stackSize is not None:
111+
self.setStackSize(stackSize)
110112

111113
def run(self):
112114
queue = self.__queue
@@ -156,12 +158,20 @@ class QThreadExecutor:
156158
... assert r == 4
157159
"""
158160

159-
def __init__(self, max_workers=10):
161+
def __init__(self, max_workers=10, stack_size=None):
160162
super().__init__()
161163
self.__max_workers = max_workers
162164
self.__queue = Queue()
165+
if stack_size is None:
166+
# Match cpython/Python/thread_pthread.h
167+
if sys.platform.startswith("darwin"):
168+
stack_size = 16 * 2 ** 20
169+
elif sys.platform.startswith("freebsd"):
170+
stack_size = 4 * 2 ** 20
171+
elif sys.platform.startswith("aix"):
172+
stack_size = 2 * 2 ** 20
163173
self.__workers = [
164-
_QThreadWorker(self.__queue, i + 1) for i in range(max_workers)
174+
_QThreadWorker(self.__queue, i + 1, stack_size) for i in range(max_workers)
165175
]
166176
self.__been_shutdown = False
167177

tests/test_qthreadexec.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,15 @@ def test_ctx_after_shutdown(shutdown_executor):
3535
def test_submit_after_shutdown(shutdown_executor):
3636
with pytest.raises(RuntimeError):
3737
shutdown_executor.submit(None)
38+
39+
40+
def test_stack_recursion_limit(executor):
41+
# Test that worker threads have sufficient stack size for the default
42+
# sys.getrecursionlimit. If not this should fail with SIGSEGV or SIGBUS
43+
# (or event SIGILL?)
44+
def rec(a, *args, **kwargs):
45+
rec(a, *args, **kwargs)
46+
fs = [executor.submit(rec, 1) for _ in range(10)]
47+
for f in fs:
48+
with pytest.raises(RecursionError):
49+
f.result()

0 commit comments

Comments
 (0)