blob: af290b7e412537bc892e7cfc650e50222c0a9790 [file] [log] [blame]
Yury Selivanovb0b0e622014-02-18 22:27:48 -05001"""Selector and proactor event loops for Windows."""
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07002
Victor Stinnerf2e17682014-01-31 16:25:24 +01003import _winapi
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07004import errno
Victor Stinnerf2e17682014-01-31 16:25:24 +01005import math
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07006import socket
Victor Stinnerf2e17682014-01-31 16:25:24 +01007import struct
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07008import weakref
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07009
Guido van Rossum0eaa5ac2013-11-04 15:50:46 -080010from . import events
Guido van Rossum59691282013-10-30 14:52:03 -070011from . import base_subprocess
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070012from . import futures
13from . import proactor_events
14from . import selector_events
15from . import tasks
16from . import windows_utils
Guido van Rossum59691282013-10-30 14:52:03 -070017from . import _overlapped
Victor Stinnerf951d282014-06-29 00:46:45 +020018from .coroutines import coroutine
19from .log import logger
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070020
21
Guido van Rossum0eaa5ac2013-11-04 15:50:46 -080022__all__ = ['SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor',
23 'DefaultEventLoopPolicy',
24 ]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070025
26
27NULL = 0
28INFINITE = 0xffffffff
29ERROR_CONNECTION_REFUSED = 1225
30ERROR_CONNECTION_ABORTED = 1236
31
32
33class _OverlappedFuture(futures.Future):
34 """Subclass of Future which represents an overlapped operation.
35
36 Cancelling it will immediately cancel the overlapped operation.
37 """
38
39 def __init__(self, ov, *, loop=None):
40 super().__init__(loop=loop)
Victor Stinnerfea6a102014-07-25 00:54:53 +020041 if self._source_traceback:
42 del self._source_traceback[-1]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070043 self.ov = ov
44
Victor Stinnere912e652014-07-12 03:11:53 +020045 def __repr__(self):
46 info = [self._state.lower()]
Victor Stinnerfea6a102014-07-25 00:54:53 +020047 state = 'pending' if self.ov.pending else 'completed'
48 info.append('overlapped=<%s, %#x>' % (state, self.ov.address))
Victor Stinnere912e652014-07-12 03:11:53 +020049 if self._state == futures._FINISHED:
50 info.append(self._format_result())
51 if self._callbacks:
52 info.append(self._format_callbacks())
53 return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
54
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070055 def cancel(self):
Victor Stinnerfea6a102014-07-25 00:54:53 +020056 if not self.done():
57 try:
58 self.ov.cancel()
59 except OSError as exc:
60 context = {
61 'message': 'Cancelling an overlapped future failed',
62 'exception': exc,
63 'future': self,
64 }
65 if self._source_traceback:
66 context['source_traceback'] = self._source_traceback
67 self._loop.call_exception_handler(context)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070068 return super().cancel()
69
70
Guido van Rossum90fb9142013-10-30 14:44:05 -070071class _WaitHandleFuture(futures.Future):
72 """Subclass of Future which represents a wait handle."""
73
74 def __init__(self, wait_handle, *, loop=None):
75 super().__init__(loop=loop)
76 self._wait_handle = wait_handle
77
Victor Stinnerfea6a102014-07-25 00:54:53 +020078 def _unregister(self):
79 if self._wait_handle is None:
80 return
Guido van Rossum90fb9142013-10-30 14:44:05 -070081 try:
82 _overlapped.UnregisterWait(self._wait_handle)
83 except OSError as e:
84 if e.winerror != _overlapped.ERROR_IO_PENDING:
85 raise
Victor Stinnerfea6a102014-07-25 00:54:53 +020086 # ERROR_IO_PENDING is not an error, the wait was unregistered
87 self._wait_handle = None
88
89 def cancel(self):
90 self._unregister()
91 super().cancel()
Guido van Rossum90fb9142013-10-30 14:44:05 -070092
93
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070094class PipeServer(object):
95 """Class representing a pipe server.
96
97 This is much like a bound, listening socket.
98 """
99 def __init__(self, address):
100 self._address = address
101 self._free_instances = weakref.WeakSet()
102 self._pipe = self._server_pipe_handle(True)
103
104 def _get_unconnected_pipe(self):
105 # Create new instance and return previous one. This ensures
106 # that (until the server is closed) there is always at least
107 # one pipe handle for address. Therefore if a client attempt
108 # to connect it will not fail with FileNotFoundError.
109 tmp, self._pipe = self._pipe, self._server_pipe_handle(False)
110 return tmp
111
112 def _server_pipe_handle(self, first):
113 # Return a wrapper for a new pipe handle.
114 if self._address is None:
115 return None
116 flags = _winapi.PIPE_ACCESS_DUPLEX | _winapi.FILE_FLAG_OVERLAPPED
117 if first:
118 flags |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
119 h = _winapi.CreateNamedPipe(
120 self._address, flags,
121 _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
122 _winapi.PIPE_WAIT,
123 _winapi.PIPE_UNLIMITED_INSTANCES,
124 windows_utils.BUFSIZE, windows_utils.BUFSIZE,
125 _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
126 pipe = windows_utils.PipeHandle(h)
127 self._free_instances.add(pipe)
128 return pipe
129
130 def close(self):
131 # Close all instances which have not been connected to by a client.
132 if self._address is not None:
133 for pipe in self._free_instances:
134 pipe.close()
135 self._pipe = None
136 self._address = None
137 self._free_instances.clear()
138
139 __del__ = close
140
141
Guido van Rossum0eaa5ac2013-11-04 15:50:46 -0800142class _WindowsSelectorEventLoop(selector_events.BaseSelectorEventLoop):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700143 """Windows version of selector event loop."""
144
145 def _socketpair(self):
146 return windows_utils.socketpair()
147
148
149class ProactorEventLoop(proactor_events.BaseProactorEventLoop):
150 """Windows version of proactor event loop using IOCP."""
151
152 def __init__(self, proactor=None):
153 if proactor is None:
154 proactor = IocpProactor()
155 super().__init__(proactor)
156
157 def _socketpair(self):
158 return windows_utils.socketpair()
159
Victor Stinnerf951d282014-06-29 00:46:45 +0200160 @coroutine
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700161 def create_pipe_connection(self, protocol_factory, address):
162 f = self._proactor.connect_pipe(address)
163 pipe = yield from f
164 protocol = protocol_factory()
165 trans = self._make_duplex_pipe_transport(pipe, protocol,
166 extra={'addr': address})
167 return trans, protocol
168
Victor Stinnerf951d282014-06-29 00:46:45 +0200169 @coroutine
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700170 def start_serving_pipe(self, protocol_factory, address):
171 server = PipeServer(address)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700172
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700173 def loop(f=None):
174 pipe = None
175 try:
176 if f:
177 pipe = f.result()
178 server._free_instances.discard(pipe)
179 protocol = protocol_factory()
180 self._make_duplex_pipe_transport(
181 pipe, protocol, extra={'addr': address})
182 pipe = server._get_unconnected_pipe()
183 if pipe is None:
184 return
185 f = self._proactor.accept_pipe(pipe)
Yury Selivanovff827f02014-02-18 18:02:19 -0500186 except OSError as exc:
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700187 if pipe and pipe.fileno() != -1:
Yury Selivanovff827f02014-02-18 18:02:19 -0500188 self.call_exception_handler({
189 'message': 'Pipe accept failed',
190 'exception': exc,
191 'pipe': pipe,
192 })
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700193 pipe.close()
194 except futures.CancelledError:
195 if pipe:
196 pipe.close()
197 else:
198 f.add_done_callback(loop)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700199
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700200 self.call_soon(loop)
201 return [server]
202
Victor Stinnerf951d282014-06-29 00:46:45 +0200203 @coroutine
Guido van Rossum59691282013-10-30 14:52:03 -0700204 def _make_subprocess_transport(self, protocol, args, shell,
205 stdin, stdout, stderr, bufsize,
206 extra=None, **kwargs):
207 transp = _WindowsSubprocessTransport(self, protocol, args, shell,
208 stdin, stdout, stderr, bufsize,
Victor Stinner73f10fd2014-01-29 14:32:20 -0800209 extra=extra, **kwargs)
Guido van Rossum59691282013-10-30 14:52:03 -0700210 yield from transp._post_init()
211 return transp
212
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700213
214class IocpProactor:
215 """Proactor implementation using IOCP."""
216
217 def __init__(self, concurrency=0xffffffff):
218 self._loop = None
219 self._results = []
220 self._iocp = _overlapped.CreateIoCompletionPort(
221 _overlapped.INVALID_HANDLE_VALUE, NULL, 0, concurrency)
222 self._cache = {}
223 self._registered = weakref.WeakSet()
224 self._stopped_serving = weakref.WeakSet()
225
Victor Stinnerfea6a102014-07-25 00:54:53 +0200226 def __repr__(self):
227 return ('<%s overlapped#=%s result#=%s>'
228 % (self.__class__.__name__, len(self._cache),
229 len(self._results)))
230
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700231 def set_loop(self, loop):
232 self._loop = loop
233
234 def select(self, timeout=None):
235 if not self._results:
236 self._poll(timeout)
237 tmp = self._results
238 self._results = []
239 return tmp
240
241 def recv(self, conn, nbytes, flags=0):
242 self._register_with_iocp(conn)
243 ov = _overlapped.Overlapped(NULL)
244 if isinstance(conn, socket.socket):
245 ov.WSARecv(conn.fileno(), nbytes, flags)
246 else:
247 ov.ReadFile(conn.fileno(), nbytes)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700248
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100249 def finish_recv(trans, key, ov):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700250 try:
251 return ov.getresult()
252 except OSError as exc:
253 if exc.winerror == _overlapped.ERROR_NETNAME_DELETED:
254 raise ConnectionResetError(*exc.args)
255 else:
256 raise
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700257
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100258 return self._register(ov, conn, finish_recv)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700259
260 def send(self, conn, buf, flags=0):
261 self._register_with_iocp(conn)
262 ov = _overlapped.Overlapped(NULL)
263 if isinstance(conn, socket.socket):
264 ov.WSASend(conn.fileno(), buf, flags)
265 else:
266 ov.WriteFile(conn.fileno(), buf)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700267
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100268 def finish_send(trans, key, ov):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700269 try:
270 return ov.getresult()
271 except OSError as exc:
272 if exc.winerror == _overlapped.ERROR_NETNAME_DELETED:
273 raise ConnectionResetError(*exc.args)
274 else:
275 raise
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700276
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100277 return self._register(ov, conn, finish_send)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700278
279 def accept(self, listener):
280 self._register_with_iocp(listener)
281 conn = self._get_accept_socket(listener.family)
282 ov = _overlapped.Overlapped(NULL)
283 ov.AcceptEx(listener.fileno(), conn.fileno())
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700284
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700285 def finish_accept(trans, key, ov):
286 ov.getresult()
287 # Use SO_UPDATE_ACCEPT_CONTEXT so getsockname() etc work.
288 buf = struct.pack('@P', listener.fileno())
289 conn.setsockopt(socket.SOL_SOCKET,
290 _overlapped.SO_UPDATE_ACCEPT_CONTEXT, buf)
291 conn.settimeout(listener.gettimeout())
292 return conn, conn.getpeername()
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700293
Victor Stinnerf951d282014-06-29 00:46:45 +0200294 @coroutine
Victor Stinner7de26462014-01-11 00:03:21 +0100295 def accept_coro(future, conn):
296 # Coroutine closing the accept socket if the future is cancelled
297 try:
298 yield from future
299 except futures.CancelledError:
300 conn.close()
301 raise
302
303 future = self._register(ov, listener, finish_accept)
304 coro = accept_coro(future, conn)
305 tasks.async(coro, loop=self._loop)
306 return future
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700307
308 def connect(self, conn, address):
309 self._register_with_iocp(conn)
310 # The socket needs to be locally bound before we call ConnectEx().
311 try:
312 _overlapped.BindLocal(conn.fileno(), conn.family)
313 except OSError as e:
314 if e.winerror != errno.WSAEINVAL:
315 raise
316 # Probably already locally bound; check using getsockname().
317 if conn.getsockname()[1] == 0:
318 raise
319 ov = _overlapped.Overlapped(NULL)
320 ov.ConnectEx(conn.fileno(), address)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700321
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700322 def finish_connect(trans, key, ov):
323 ov.getresult()
324 # Use SO_UPDATE_CONNECT_CONTEXT so getsockname() etc work.
325 conn.setsockopt(socket.SOL_SOCKET,
326 _overlapped.SO_UPDATE_CONNECT_CONTEXT, 0)
327 return conn
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700328
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700329 return self._register(ov, conn, finish_connect)
330
331 def accept_pipe(self, pipe):
332 self._register_with_iocp(pipe)
333 ov = _overlapped.Overlapped(NULL)
334 ov.ConnectNamedPipe(pipe.fileno())
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700335
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100336 def finish_accept_pipe(trans, key, ov):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700337 ov.getresult()
338 return pipe
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700339
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100340 return self._register(ov, pipe, finish_accept_pipe)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700341
342 def connect_pipe(self, address):
343 ov = _overlapped.Overlapped(NULL)
344 ov.WaitNamedPipeAndConnect(address, self._iocp, ov.address)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700345
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100346 def finish_connect_pipe(err, handle, ov):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700347 # err, handle were arguments passed to PostQueuedCompletionStatus()
348 # in a function run in a thread pool.
349 if err == _overlapped.ERROR_SEM_TIMEOUT:
350 # Connection did not succeed within time limit.
351 msg = _overlapped.FormatMessage(err)
352 raise ConnectionRefusedError(0, msg, None, err)
353 elif err != 0:
354 msg = _overlapped.FormatMessage(err)
355 raise OSError(0, msg, None, err)
356 else:
357 return windows_utils.PipeHandle(handle)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700358
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100359 return self._register(ov, None, finish_connect_pipe, wait_for_post=True)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700360
Guido van Rossum90fb9142013-10-30 14:44:05 -0700361 def wait_for_handle(self, handle, timeout=None):
362 if timeout is None:
363 ms = _winapi.INFINITE
364 else:
Victor Stinnerf2e17682014-01-31 16:25:24 +0100365 # RegisterWaitForSingleObject() has a resolution of 1 millisecond,
366 # round away from zero to wait *at least* timeout seconds.
367 ms = math.ceil(timeout * 1e3)
Guido van Rossum90fb9142013-10-30 14:44:05 -0700368
369 # We only create ov so we can use ov.address as a key for the cache.
370 ov = _overlapped.Overlapped(NULL)
371 wh = _overlapped.RegisterWaitWithQueue(
372 handle, self._iocp, ov.address, ms)
373 f = _WaitHandleFuture(wh, loop=self._loop)
374
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100375 def finish_wait_for_handle(trans, key, ov):
Victor Stinnerfea6a102014-07-25 00:54:53 +0200376 f._unregister()
Richard Oudkerk71196e72013-11-24 17:50:40 +0000377 # Note that this second wait means that we should only use
378 # this with handles types where a successful wait has no
379 # effect. So events or processes are all right, but locks
380 # or semaphores are not. Also note if the handle is
381 # signalled and then quickly reset, then we may return
382 # False even though we have not timed out.
383 return (_winapi.WaitForSingleObject(handle, 0) ==
384 _winapi.WAIT_OBJECT_0)
Guido van Rossum90fb9142013-10-30 14:44:05 -0700385
Victor Stinnerc89c8a72014-02-26 17:35:30 +0100386 self._cache[ov.address] = (f, ov, None, finish_wait_for_handle)
Guido van Rossum90fb9142013-10-30 14:44:05 -0700387 return f
388
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700389 def _register_with_iocp(self, obj):
390 # To get notifications of finished ops on this objects sent to the
391 # completion port, were must register the handle.
392 if obj not in self._registered:
393 self._registered.add(obj)
394 _overlapped.CreateIoCompletionPort(obj.fileno(), self._iocp, 0, 0)
395 # XXX We could also use SetFileCompletionNotificationModes()
396 # to avoid sending notifications to completion port of ops
397 # that succeed immediately.
398
399 def _register(self, ov, obj, callback, wait_for_post=False):
400 # Return a future which will be set with the result of the
401 # operation when it completes. The future's value is actually
402 # the value returned by callback().
403 f = _OverlappedFuture(ov, loop=self._loop)
404 if ov.pending or wait_for_post:
405 # Register the overlapped operation for later. Note that
406 # we only store obj to prevent it from being garbage
407 # collected too early.
408 self._cache[ov.address] = (f, ov, obj, callback)
409 else:
410 # The operation has completed, so no need to postpone the
411 # work. We cannot take this short cut if we need the
412 # NumberOfBytes, CompletionKey values returned by
413 # PostQueuedCompletionStatus().
414 try:
415 value = callback(None, None, ov)
416 except OSError as e:
417 f.set_exception(e)
418 else:
419 f.set_result(value)
420 return f
421
422 def _get_accept_socket(self, family):
423 s = socket.socket(family)
424 s.settimeout(0)
425 return s
426
427 def _poll(self, timeout=None):
428 if timeout is None:
429 ms = INFINITE
430 elif timeout < 0:
431 raise ValueError("negative timeout")
432 else:
Victor Stinnerf2e17682014-01-31 16:25:24 +0100433 # GetQueuedCompletionStatus() has a resolution of 1 millisecond,
434 # round away from zero to wait *at least* timeout seconds.
435 ms = math.ceil(timeout * 1e3)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700436 if ms >= INFINITE:
437 raise ValueError("timeout too big")
438 while True:
439 status = _overlapped.GetQueuedCompletionStatus(self._iocp, ms)
440 if status is None:
441 return
442 err, transferred, key, address = status
443 try:
444 f, ov, obj, callback = self._cache.pop(address)
445 except KeyError:
446 # key is either zero, or it is used to return a pipe
447 # handle which should be closed to avoid a leak.
448 if key not in (0, _overlapped.INVALID_HANDLE_VALUE):
449 _winapi.CloseHandle(key)
450 ms = 0
451 continue
452 if obj in self._stopped_serving:
453 f.cancel()
454 elif not f.cancelled():
455 try:
456 value = callback(transferred, key, ov)
457 except OSError as e:
458 f.set_exception(e)
459 self._results.append(f)
460 else:
461 f.set_result(value)
462 self._results.append(f)
463 ms = 0
464
465 def _stop_serving(self, obj):
466 # obj is a socket or pipe handle. It will be closed in
467 # BaseProactorEventLoop._stop_serving() which will make any
468 # pending operations fail quickly.
469 self._stopped_serving.add(obj)
470
471 def close(self):
472 # Cancel remaining registered operations.
Victor Stinnerfea6a102014-07-25 00:54:53 +0200473 for address, (fut, ov, obj, callback) in list(self._cache.items()):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700474 if obj is None:
475 # The operation was started with connect_pipe() which
476 # queues a task to Windows' thread pool. This cannot
477 # be cancelled, so just forget it.
478 del self._cache[address]
479 else:
480 try:
Victor Stinnerfea6a102014-07-25 00:54:53 +0200481 fut.cancel()
482 except OSError as exc:
483 if self._loop is not None:
484 context = {
485 'message': 'Cancelling a future failed',
486 'exception': exc,
487 'future': fut,
488 }
489 if fut._source_traceback:
490 context['source_traceback'] = fut._source_traceback
491 self._loop.call_exception_handler(context)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700492
493 while self._cache:
494 if not self._poll(1):
Guido van Rossumfc29e0f2013-10-17 15:39:45 -0700495 logger.debug('taking long time to close proactor')
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700496
497 self._results = []
498 if self._iocp is not None:
499 _winapi.CloseHandle(self._iocp)
500 self._iocp = None
Guido van Rossum59691282013-10-30 14:52:03 -0700501
Victor Stinnerfea6a102014-07-25 00:54:53 +0200502 def __del__(self):
503 self.close()
504
Guido van Rossum59691282013-10-30 14:52:03 -0700505
506class _WindowsSubprocessTransport(base_subprocess.BaseSubprocessTransport):
507
508 def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
509 self._proc = windows_utils.Popen(
510 args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
511 bufsize=bufsize, **kwargs)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700512
Guido van Rossum59691282013-10-30 14:52:03 -0700513 def callback(f):
514 returncode = self._proc.poll()
515 self._process_exited(returncode)
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700516
Guido van Rossum59691282013-10-30 14:52:03 -0700517 f = self._loop._proactor.wait_for_handle(int(self._proc._handle))
518 f.add_done_callback(callback)
Guido van Rossum0eaa5ac2013-11-04 15:50:46 -0800519
520
521SelectorEventLoop = _WindowsSelectorEventLoop
522
523
524class _WindowsDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
525 _loop_factory = SelectorEventLoop
526
527
528DefaultEventLoopPolicy = _WindowsDefaultEventLoopPolicy