1717 GdbTimeoutError ,
1818)
1919
20- if USING_WINDOWS :
21- import msvcrt
22- from ctypes import windll , byref , wintypes , WinError , POINTER # type: ignore
23- from ctypes .wintypes import HANDLE , DWORD , BOOL
24- else :
20+ from threading import Thread
21+ from queue import Queue , Empty
22+
23+ if not USING_WINDOWS :
2524 import fcntl
2625
2726
28- __all__ = [
29- "IoManager" ,
30- "make_non_blocking" ,
31- ]
27+ __all__ = ["IoManager" ]
3228
3329
3430logger = logging .getLogger (__name__ )
@@ -68,9 +64,26 @@ def __init__(
6864 self ._allow_overwrite_timeout_times = (
6965 self .time_to_check_for_additional_output_sec > 0
7066 )
71- make_non_blocking (self .stdout )
72- if self .stderr :
73- make_non_blocking (self .stderr )
67+
68+ if USING_WINDOWS :
69+ self .queue_stdout = Queue () # type: Queue
70+ self .thread_stdout = Thread (
71+ target = _enqueue_output , args = (self .stdout , self .queue_stdout )
72+ )
73+ self .thread_stdout .daemon = True # thread dies with the program
74+ self .thread_stdout .start ()
75+
76+ if self .stderr :
77+ self .queue_stderr = Queue () # type: Queue
78+ self .thread_stderr = Thread (
79+ target = _enqueue_output , args = (self .stderr , self .queue_stderr )
80+ )
81+ self .thread_stderr .daemon = True # thread dies with the program
82+ self .thread_stderr .start ()
83+ else :
84+ fcntl .fcntl (self .stdout , fcntl .F_SETFL , os .O_NONBLOCK )
85+ if self .stderr :
86+ fcntl .fcntl (self .stderr , fcntl .F_SETFL , os .O_NONBLOCK )
7487
7588 def get_gdb_response (
7689 self , timeout_sec : float = DEFAULT_GDB_TIMEOUT_SEC , raise_error_on_timeout = True
@@ -110,22 +123,23 @@ def get_gdb_response(
110123
111124 def _get_responses_windows (self , timeout_sec ):
112125 """Get responses on windows. Assume no support for select and use a while loop."""
126+ assert USING_WINDOWS
127+
113128 timeout_time_sec = time .time () + timeout_sec
114129 responses = []
115130 while True :
116131 responses_list = []
132+
117133 try :
118- self .stdout .flush ()
119- raw_output = self .stdout .readline ().replace (b"\r " , b"\n " )
134+ raw_output = self .queue_stdout .get_nowait ()
120135 responses_list = self ._get_responses_list (raw_output , "stdout" )
121- except IOError :
136+ except Empty :
122137 pass
123138
124139 try :
125- self .stderr .flush ()
126- raw_output = self .stderr .readline ().replace (b"\r " , b"\n " )
140+ raw_output = self .queue_stderr .get_nowait ()
127141 responses_list += self ._get_responses_list (raw_output , "stderr" )
128- except IOError :
142+ except Empty :
129143 pass
130144
131145 responses += responses_list
@@ -138,11 +152,12 @@ def _get_responses_windows(self, timeout_sec):
138152 )
139153 elif time .time () > timeout_time_sec :
140154 break
141-
142155 return responses
143156
144157 def _get_responses_unix (self , timeout_sec ):
145158 """Get responses on unix-like system. Use select to wait for output."""
159+ assert not USING_WINDOWS
160+
146161 timeout_time_sec = time .time () + timeout_sec
147162 responses = []
148163 while True :
@@ -325,28 +340,7 @@ def _buffer_incomplete_responses(
325340 return (raw_output , buf )
326341
327342
328- def make_non_blocking (file_obj : io .IOBase ):
329- """make file object non-blocking
330- Windows doesn't have the fcntl module, but someone on
331- stack overflow supplied this code as an answer, and it works
332- http://stackoverflow.com/a/34504971/2893090"""
333-
334- if USING_WINDOWS :
335- LPDWORD = POINTER (DWORD )
336- PIPE_NOWAIT = wintypes .DWORD (0x00000001 )
337-
338- SetNamedPipeHandleState = windll .kernel32 .SetNamedPipeHandleState
339- SetNamedPipeHandleState .argtypes = [HANDLE , LPDWORD , LPDWORD , LPDWORD ]
340- SetNamedPipeHandleState .restype = BOOL
341-
342- h = msvcrt .get_osfhandle (file_obj .fileno ()) # type: ignore
343-
344- res = windll .kernel32 .SetNamedPipeHandleState (h , byref (PIPE_NOWAIT ), None , None )
345- if res == 0 :
346- raise ValueError (WinError ())
347-
348- else :
349- # Set the file status flag (F_SETFL) on the pipes to be non-blocking
350- # so we can attempt to read from a pipe with no new data without locking
351- # the program up
352- fcntl .fcntl (file_obj , fcntl .F_SETFL , os .O_NONBLOCK )
343+ def _enqueue_output (out , queue ):
344+ for line in iter (out .readline , b"" ):
345+ queue .put (line .replace (b"\r " , b"\n " ))
346+ # Not necessary to close, it will be done in the main process.
0 commit comments