1
+ import contextvars
1
2
import msvcrt
2
3
import os
3
4
import sys
5
+ import threading
4
6
from abc import abstractmethod
5
7
from asyncio import get_event_loop
6
8
from contextlib import contextmanager
@@ -38,10 +40,6 @@ class _Win32InputBase(Input):
38
40
"""
39
41
Base class for `Win32Input` and `Win32PipeInput`.
40
42
"""
41
-
42
- def __init__ (self ) -> None :
43
- self .win32_handles = _Win32Handles ()
44
-
45
43
@property
46
44
@abstractmethod
47
45
def handle (self ) -> HANDLE :
@@ -54,7 +52,7 @@ class Win32Input(_Win32InputBase):
54
52
"""
55
53
56
54
def __init__ (self , stdin : Optional [TextIO ] = None ) -> None :
57
- super (). __init__ ()
55
+ self . win32_handles = _Win32Handles ()
58
56
self .console_input_reader = ConsoleInputReader ()
59
57
60
58
def attach (self , input_ready_callback : Callable [[], None ]) -> ContextManager [None ]:
@@ -482,6 +480,9 @@ class _Win32Handles:
482
480
def __init__ (self ) -> None :
483
481
self ._handle_callbacks : Dict [int , Callable [[], None ]] = {}
484
482
483
+ # Background threads that do the blocking `wait_for_handles` calls.
484
+ self ._wait_threads : Dict [int , threading .Thread ] = {}
485
+
485
486
# Windows Events that are triggered when we have to stop watching this
486
487
# handle.
487
488
self ._remove_events : Dict [int , HANDLE ] = {}
@@ -511,7 +512,7 @@ def ready() -> None:
511
512
try :
512
513
callback ()
513
514
finally :
514
- run_in_executor_with_context ( wait , loop = loop )
515
+ start_wait_thread ( )
515
516
516
517
# Wait for the input to become ready.
517
518
# (Use an executor for this, the Windows asyncio event loop doesn't
@@ -527,7 +528,13 @@ def wait() -> None:
527
528
else :
528
529
loop .call_soon_threadsafe (ready )
529
530
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 ()
531
538
532
539
def remove_win32_handle (self , handle : HANDLE ) -> Optional [Callable [[], None ]]:
533
540
"""
@@ -545,6 +552,14 @@ def remove_win32_handle(self, handle: HANDLE) -> Optional[Callable[[], None]]:
545
552
else :
546
553
windll .kernel32 .SetEvent (event )
547
554
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
+
548
563
try :
549
564
return self ._handle_callbacks .pop (handle .value )
550
565
except KeyError :
0 commit comments