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()

`