Skip to content

Commit 2998aaa

Browse files
On Windows: join background threads in _Win32Handles.
This doesn't really solve a known issue, but this is cleaner.
1 parent 5005863 commit 2998aaa

File tree

1 file changed

+22
-7
lines changed

1 file changed

+22
-7
lines changed

Diff for: prompt_toolkit/input/win32.py

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import contextvars
12
import msvcrt
23
import os
34
import sys
5+
import threading
46
from abc import abstractmethod
57
from asyncio import get_event_loop
68
from contextlib import contextmanager
@@ -38,10 +40,6 @@ class _Win32InputBase(Input):
3840
"""
3941
Base class for `Win32Input` and `Win32PipeInput`.
4042
"""
41-
42-
def __init__(self) -> None:
43-
self.win32_handles = _Win32Handles()
44-
4543
@property
4644
@abstractmethod
4745
def handle(self) -> HANDLE:
@@ -54,7 +52,7 @@ class Win32Input(_Win32InputBase):
5452
"""
5553

5654
def __init__(self, stdin: Optional[TextIO] = None) -> None:
57-
super().__init__()
55+
self.win32_handles = _Win32Handles()
5856
self.console_input_reader = ConsoleInputReader()
5957

6058
def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]:
@@ -482,6 +480,9 @@ class _Win32Handles:
482480
def __init__(self) -> None:
483481
self._handle_callbacks: Dict[int, Callable[[], None]] = {}
484482

483+
# Background threads that do the blocking `wait_for_handles` calls.
484+
self._wait_threads: Dict[int, threading.Thread] = {}
485+
485486
# Windows Events that are triggered when we have to stop watching this
486487
# handle.
487488
self._remove_events: Dict[int, HANDLE] = {}
@@ -511,7 +512,7 @@ def ready() -> None:
511512
try:
512513
callback()
513514
finally:
514-
run_in_executor_with_context(wait, loop=loop)
515+
start_wait_thread()
515516

516517
# Wait for the input to become ready.
517518
# (Use an executor for this, the Windows asyncio event loop doesn't
@@ -527,7 +528,13 @@ def wait() -> None:
527528
else:
528529
loop.call_soon_threadsafe(ready)
529530

530-
run_in_executor_with_context(wait, loop=loop)
531+
def start_wait_thread() -> None:
532+
ctx = contextvars.copy_context()
533+
thread = threading.Thread(target=ctx.run, args=(wait, ))
534+
self._wait_threads[handle.value] = thread
535+
thread.start()
536+
537+
start_wait_thread()
531538

532539
def remove_win32_handle(self, handle: HANDLE) -> Optional[Callable[[], None]]:
533540
"""
@@ -545,6 +552,14 @@ def remove_win32_handle(self, handle: HANDLE) -> Optional[Callable[[], None]]:
545552
else:
546553
windll.kernel32.SetEvent(event)
547554

555+
# Join the background thread, if there is one.
556+
try:
557+
wait_thread = self._wait_threads.pop(handle.value)
558+
except KeyError:
559+
pass
560+
else:
561+
wait_thread.join()
562+
548563
try:
549564
return self._handle_callbacks.pop(handle.value)
550565
except KeyError:

0 commit comments

Comments
 (0)