Skip to content

Commit f5a6329

Browse files
committed
create a single thread for call_nosync
1 parent bce7708 commit f5a6329

3 files changed

Lines changed: 50 additions & 10 deletions

File tree

tests/test_tkthread.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ def func():
435435
res = tkthread.call(func) # blocks
436436
d['pending'] = False
437437

438+
@call_until(4)
439+
def has_started():
440+
return d['pending'] is not True
438441

439442
self.assertIs(d['pending'], True)
440443
while tstart.is_alive():

tkthread/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11

2-
__version__ = '0.5.1'
2+
__version__ = '0.5.2'
33

tkthread/_willdispatch.py

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
import traceback
3535
import sys
3636
import threading
37+
try:
38+
import queue
39+
except:
40+
import Queue as queue # Py2.7
3741

3842
from . import tk
3943

@@ -76,9 +80,13 @@ def wrapped(self, *args, **kwargs):
7680
# and the main thread event loop setting dispatch=0.
7781
# As rare as the condition is, it needs to be handled.
7882
if n < rcount:
79-
traceback.print_exc(file=sys.stderr)
80-
print('retrying %r %i of %i' % (name, n+1, rcount),
81-
file=sys.stderr)
83+
try:
84+
traceback.print_exc(file=sys.stderr)
85+
print('retrying %r %i of %i' % (name, n+1, rcount),
86+
file=sys.stderr)
87+
except:
88+
# pythonw.exe sys.stderr is None
89+
pass
8290
continue
8391
else:
8492
raise
@@ -199,6 +207,40 @@ def _ensure_after_idle(widget, func, tries=None):
199207
return result
200208

201209

210+
class _NoSyncHandler:
211+
'''
212+
_tkinter.c will dispatch non-mainthread calls to the mainthread,
213+
but will create a Tcl_Condition variable that blocks the calling thread.
214+
'''
215+
def __init__(self):
216+
self.q = queue.Queue()
217+
self.th = threading.Thread(
218+
target=self._dispatcher,
219+
name='tkthread.nosync',
220+
)
221+
self.th.daemon=True
222+
self.th.start()
223+
224+
def _dispatcher(self):
225+
while True:
226+
func, args, kwargs = self.q.get()
227+
try:
228+
func(*args, **kwargs)
229+
except:
230+
# show the error
231+
try:
232+
traceback.print_exc(file=sys.stderr)
233+
except:
234+
# pythonw.exe sys.stderr is None
235+
pass
236+
237+
def call(self, func, *args, **kw):
238+
self.q.put((func, args, kw))
239+
240+
241+
_nosync_handler = _NoSyncHandler()
242+
243+
202244
def main(widget=None, sync=True):
203245
"""Decorator to run callable (no arguments) on Tcl/Tk mainthread.
204246
@@ -231,12 +273,7 @@ def sync_wrapped(_func=func, _ev=ev):
231273
ev.wait()
232274

233275
else:
234-
th = threading.Thread(
235-
target=_ensure_after_idle,
236-
args=(w, func),
237-
name='tkthread.main')
238-
th.daemon = True
239-
th.start()
276+
_nosync_handler.call(_ensure_after_idle, w, func)
240277

241278
return func
242279
return wrapped

0 commit comments

Comments
 (0)