bpo-32622: Native sendfile on windows (GH-5565) · python/cpython@632c1cb (original) (raw)
`@@ -6,11 +6,14 @@
`
6
6
``
7
7
`all = 'BaseProactorEventLoop',
`
8
8
``
``
9
`+
import io
`
``
10
`+
import os
`
9
11
`import socket
`
10
12
`import warnings
`
11
13
``
12
14
`from . import base_events
`
13
15
`from . import constants
`
``
16
`+
from . import events
`
14
17
`from . import futures
`
15
18
`from . import protocols
`
16
19
`from . import sslproto
`
`@@ -107,6 +110,11 @@ def _fatal_error(self, exc, message='Fatal error on pipe transport'):
`
107
110
`self._force_close(exc)
`
108
111
``
109
112
`def _force_close(self, exc):
`
``
113
`+
if self._empty_waiter is not None:
`
``
114
`+
if exc is None:
`
``
115
`+
self._empty_waiter.set_result(None)
`
``
116
`+
else:
`
``
117
`+
self._empty_waiter.set_exception(exc)
`
110
118
`if self._closing:
`
111
119
`return
`
112
120
`self._closing = True
`
`@@ -327,13 +335,19 @@ class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport,
`
327
335
``
328
336
`_start_tls_compatible = True
`
329
337
``
``
338
`+
def init(self, *args, **kw):
`
``
339
`+
super().init(*args, **kw)
`
``
340
`+
self._empty_waiter = None
`
``
341
+
330
342
`def write(self, data):
`
331
343
`if not isinstance(data, (bytes, bytearray, memoryview)):
`
332
344
`raise TypeError(
`
333
345
`f"data argument must be a bytes-like object, "
`
334
346
`f"not {type(data).name}")
`
335
347
`if self._eof_written:
`
336
348
`raise RuntimeError('write_eof() already called')
`
``
349
`+
if self._empty_waiter is not None:
`
``
350
`+
raise RuntimeError('unable to write; sendfile is in progress')
`
337
351
``
338
352
`if not data:
`
339
353
`return
`
`@@ -393,6 +407,8 @@ def _loop_writing(self, f=None, data=None):
`
393
407
`self._maybe_pause_protocol()
`
394
408
`else:
`
395
409
`self._write_fut.add_done_callback(self._loop_writing)
`
``
410
`+
if self._empty_waiter is not None and self._write_fut is None:
`
``
411
`+
self._empty_waiter.set_result(None)
`
396
412
`except ConnectionResetError as exc:
`
397
413
`self._force_close(exc)
`
398
414
`except OSError as exc:
`
`@@ -407,6 +423,17 @@ def write_eof(self):
`
407
423
`def abort(self):
`
408
424
`self._force_close(None)
`
409
425
``
``
426
`+
def _make_empty_waiter(self):
`
``
427
`+
if self._empty_waiter is not None:
`
``
428
`+
raise RuntimeError("Empty waiter is already set")
`
``
429
`+
self._empty_waiter = self._loop.create_future()
`
``
430
`+
if self._write_fut is None:
`
``
431
`+
self._empty_waiter.set_result(None)
`
``
432
`+
return self._empty_waiter
`
``
433
+
``
434
`+
def _reset_empty_waiter(self):
`
``
435
`+
self._empty_waiter = None
`
``
436
+
410
437
``
411
438
`class _ProactorWritePipeTransport(_ProactorBaseWritePipeTransport):
`
412
439
`def init(self, *args, **kw):
`
`@@ -447,7 +474,7 @@ class _ProactorSocketTransport(_ProactorReadPipeTransport,
`
447
474
`transports.Transport):
`
448
475
`"""Transport for connected sockets."""
`
449
476
``
450
``
`-
_sendfile_compatible = constants._SendfileMode.FALLBACK
`
``
477
`+
_sendfile_compatible = constants._SendfileMode.TRY_NATIVE
`
451
478
``
452
479
`def _set_extra(self, sock):
`
453
480
`self._extra['socket'] = sock
`
`@@ -556,6 +583,47 @@ async def sock_connect(self, sock, address):
`
556
583
`async def sock_accept(self, sock):
`
557
584
`return await self._proactor.accept(sock)
`
558
585
``
``
586
`+
async def _sock_sendfile_native(self, sock, file, offset, count):
`
``
587
`+
try:
`
``
588
`+
fileno = file.fileno()
`
``
589
`+
except (AttributeError, io.UnsupportedOperation) as err:
`
``
590
`+
raise events.SendfileNotAvailableError("not a regular file")
`
``
591
`+
try:
`
``
592
`+
fsize = os.fstat(fileno).st_size
`
``
593
`+
except OSError as err:
`
``
594
`+
raise events.SendfileNotAvailableError("not a regular file")
`
``
595
`+
blocksize = count if count else fsize
`
``
596
`+
if not blocksize:
`
``
597
`+
return 0 # empty file
`
``
598
+
``
599
`+
blocksize = min(blocksize, 0xffff_ffff)
`
``
600
`+
end_pos = min(offset + count, fsize) if count else fsize
`
``
601
`+
offset = min(offset, fsize)
`
``
602
`+
total_sent = 0
`
``
603
`+
try:
`
``
604
`+
while True:
`
``
605
`+
blocksize = min(end_pos - offset, blocksize)
`
``
606
`+
if blocksize <= 0:
`
``
607
`+
return total_sent
`
``
608
`+
await self._proactor.sendfile(sock, file, offset, blocksize)
`
``
609
`+
offset += blocksize
`
``
610
`+
total_sent += blocksize
`
``
611
`+
finally:
`
``
612
`+
if total_sent > 0:
`
``
613
`+
file.seek(offset)
`
``
614
+
``
615
`+
async def _sendfile_native(self, transp, file, offset, count):
`
``
616
`+
resume_reading = transp.is_reading()
`
``
617
`+
transp.pause_reading()
`
``
618
`+
await transp._make_empty_waiter()
`
``
619
`+
try:
`
``
620
`+
return await self.sock_sendfile(transp._sock, file, offset, count,
`
``
621
`+
fallback=False)
`
``
622
`+
finally:
`
``
623
`+
transp._reset_empty_waiter()
`
``
624
`+
if resume_reading:
`
``
625
`+
transp.resume_reading()
`
``
626
+
559
627
`def _close_self_pipe(self):
`
560
628
`if self._self_reading_future is not None:
`
561
629
`self._self_reading_future.cancel()
`