| 351 | if hasattr(os, 'sendfile'): |
| 352 | |
| 353 | def _sendfile_use_sendfile(self, file, offset=0, count=None): |
| 354 | # Lazy import to improve module import time |
| 355 | import selectors |
| 356 | |
| 357 | self._check_sendfile_params(file, offset, count) |
| 358 | sockno = self.fileno() |
| 359 | try: |
| 360 | fileno = file.fileno() |
| 361 | except (AttributeError, io.UnsupportedOperation) as err: |
| 362 | raise _GiveupOnSendfile(err) # not a regular file |
| 363 | try: |
| 364 | fsize = os.fstat(fileno).st_size |
| 365 | except OSError as err: |
| 366 | raise _GiveupOnSendfile(err) # not a regular file |
| 367 | if not fsize: |
| 368 | return 0 # empty file |
| 369 | # Truncate to 1GiB to avoid OverflowError, see bpo-38319. |
| 370 | blocksize = min(count or fsize, 2 ** 30) |
| 371 | timeout = self.gettimeout() |
| 372 | if timeout == 0: |
| 373 | raise ValueError("non-blocking sockets are not supported") |
| 374 | # poll/select have the advantage of not requiring any |
| 375 | # extra file descriptor, contrarily to epoll/kqueue |
| 376 | # (also, they require a single syscall). |
| 377 | if hasattr(selectors, 'PollSelector'): |
| 378 | selector = selectors.PollSelector() |
| 379 | else: |
| 380 | selector = selectors.SelectSelector() |
| 381 | selector.register(sockno, selectors.EVENT_WRITE) |
| 382 | |
| 383 | total_sent = 0 |
| 384 | # localize variable access to minimize overhead |
| 385 | selector_select = selector.select |
| 386 | os_sendfile = os.sendfile |
| 387 | try: |
| 388 | while True: |
| 389 | if timeout and not selector_select(timeout): |
| 390 | raise TimeoutError('timed out') |
| 391 | if count: |
| 392 | blocksize = min(count - total_sent, blocksize) |
| 393 | if blocksize <= 0: |
| 394 | break |
| 395 | try: |
| 396 | sent = os_sendfile(sockno, fileno, offset, blocksize) |
| 397 | except BlockingIOError: |
| 398 | if not timeout: |
| 399 | # Block until the socket is ready to send some |
| 400 | # data; avoids hogging CPU resources. |
| 401 | selector_select() |
| 402 | continue |
| 403 | except OSError as err: |
| 404 | if total_sent == 0: |
| 405 | # We can get here for different reasons, the main |
| 406 | # one being 'file' is not a regular mmap(2)-like |
| 407 | # file, in which case we'll fall back on using |
| 408 | # plain send(). |
| 409 | raise _GiveupOnSendfile(err) |
| 410 | raise err from None |