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 ()
0 commit comments