Skip to content

Commit 213c3c8

Browse files
authored
Merge pull request #3138 from ales-erjavec/fixes/concurrent-stack-size
[FIX] utils/concurrent: Switch default thread pool
2 parents 2e04b0c + 173bd91 commit 213c3c8

File tree

1 file changed

+43
-4
lines changed

1 file changed

+43
-4
lines changed

Orange/widgets/utils/concurrent.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44
# TODO: Rename the module to something that does not conflict with stdlib
55
# concurrent
6+
import os
67
import threading
78
import atexit
89
import logging
@@ -192,15 +193,53 @@ class ThreadExecutor(QObject, concurrent.futures.Executor):
192193
193194
threadPool : Optional[QThreadPool]
194195
Thread pool to be used by the instance of the Executor. If `None`
195-
then ``QThreadPool.globalInstance()`` will be used.
196+
then a private global thread pool will be used.
196197
198+
.. versionchanged:: 3.15
199+
Before 3.15 a `QThreadPool.globalPool()` was used as the default.
200+
201+
.. warning::
202+
If you pass a custom `QThreadPool` make sure it creates threads
203+
with sufficient stack size for the tasks submitted to the executor
204+
(see `QThreadPool.setStackSize`).
197205
"""
206+
# A default thread pool. Replaced QThreadPool due to insufficient default
207+
# stack size for created threads (QTBUG-2568). Not using even on
208+
# Qt >= 5.10 just for consistency sake.
209+
class __global:
210+
__lock = threading.Lock()
211+
__instance = None
212+
213+
@classmethod
214+
def instance(cls):
215+
# type: () -> concurrent.futures.ThreadPoolExecutor
216+
with cls.__lock:
217+
if cls.__instance is None:
218+
cls.__instance = concurrent.futures.ThreadPoolExecutor(
219+
max_workers=(os.cpu_count() or 1)
220+
)
221+
return cls.__instance
198222

199223
def __init__(self, parent=None, threadPool=None, **kwargs):
200224
super().__init__(parent, **kwargs)
225+
201226
if threadPool is None:
202-
threadPool = QThreadPool.globalInstance()
227+
threadPool = self.__global.instance()
228+
203229
self._threadPool = threadPool
230+
if isinstance(threadPool, QThreadPool):
231+
def start(runnable):
232+
# type: (QRunnable) -> None
233+
threadPool.start(runnable)
234+
elif isinstance(threadPool, concurrent.futures.Executor):
235+
# adapt to Executor interface
236+
def start(runnable):
237+
# type: (QRunnable) -> None
238+
threadPool.submit(runnable.run)
239+
else:
240+
raise TypeError("Invalid `threadPool` type '{}'"
241+
.format(type(threadPool).__name__))
242+
self.__start = start
204243
self._depot_thread = None
205244
self._futures = []
206245
self._shutdown = False
@@ -233,7 +272,7 @@ def submit(self, func, *args, **kwargs):
233272

234273
self._futures.append(f)
235274
f.add_done_callback(self._future_done)
236-
self._threadPool.start(runnable)
275+
self.__start(runnable)
237276
return f
238277

239278
def submit_task(self, task):
@@ -249,7 +288,7 @@ def submit_task(self, task):
249288

250289
self._futures.append(f)
251290
f.add_done_callback(self._future_done)
252-
self._threadPool.start(runnable)
291+
self.__start(runnable)
253292
return f
254293

255294
def __make_task_runnable(self, task):

0 commit comments

Comments
 (0)