mirror of
https://github.com/status-im/nim-chronos.git
synced 2025-01-10 03:15:55 +00:00
c25fa1f6cd
When `write` is called on a `StreamTransport`, the current sequence of operations is: * copy data to queue * register for "write" event notification * return unfinished future to `write` caller * wait for "write" notification (in `poll`) * perform one `send` * wait for notification again if there's more data to write * complete the future In this PR, we introduce a fast path for writing: * If the queue is empty, try to send as much data as possible * If all data is sent, return completed future without `poll` round * If there's more data to send than can be sent in one go, add the rest to queue * If the queue is not empty, enqueue as above * When notified that write is possible, keep writing until OS buffer is full before waiting for event again The fast path provides significant performance benefits when there are many small writes, such as when sending gossip to many peers, by avoiding the poll loop and data copy on each send. Also fixes an issue where the socket would not be removed from the writer set if there were pending writes on close.
116 lines
4.3 KiB
Nim
116 lines
4.3 KiB
Nim
#
|
|
# Chronos SendFile
|
|
# (c) Copyright 2018-Present
|
|
# Status Research & Development GmbH
|
|
#
|
|
# Licensed under either of
|
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
|
# MIT license (LICENSE-MIT)
|
|
|
|
## This module provides cross-platform wrapper for ``sendfile()`` syscall.
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
when defined(nimdoc):
|
|
proc sendfile*(outfd, infd: int, offset: int, count: var int): int =
|
|
## Copies data between file descriptor ``infd`` and ``outfd``. Because this
|
|
## copying is done within the kernel, ``sendfile()`` is more efficient than
|
|
## the combination of ``read(2)`` and ``write(2)``, which would require
|
|
## transferring data to and from user space.
|
|
##
|
|
## ``infd`` should be a file descriptor opened for reading and
|
|
## ``outfd`` should be a descriptor opened for writing.
|
|
##
|
|
## The ``infd`` argument must correspond to a file which supports
|
|
## ``mmap(2)``-like operations (i.e., it cannot be a socket).
|
|
##
|
|
## ``offset`` the file offset from which ``sendfile()`` will start reading
|
|
## data from ``infd``.
|
|
##
|
|
## ``count`` is the number of bytes to copy between the file descriptors.
|
|
## On exit ``count`` will hold number of bytes actually transferred between
|
|
## file descriptors. May be >0 even in the case of error return, if some
|
|
## bytes were sent before the error occurred.
|
|
##
|
|
## If the transfer was successful, the number of bytes written to ``outfd``
|
|
## is stored in ``count``, and ``0`` returned. Note that a successful call
|
|
## to ``sendfile()`` may write fewer bytes than requested; the caller should
|
|
## be prepared to retry the call if there were unsent bytes.
|
|
##
|
|
## On error, ``-1`` is returned.
|
|
|
|
when defined(linux) or defined(android):
|
|
|
|
proc osSendFile*(outfd, infd: cint, offset: ptr int, count: int): int
|
|
{.importc: "sendfile", header: "<sys/sendfile.h>".}
|
|
|
|
proc sendfile*(outfd, infd: int, offset: int, count: var int): int =
|
|
var o = offset
|
|
let res = osSendFile(cint(outfd), cint(infd), addr o, count)
|
|
if res >= 0:
|
|
count = res
|
|
0
|
|
else:
|
|
count = 0
|
|
-1
|
|
|
|
elif defined(freebsd) or defined(openbsd) or defined(netbsd) or
|
|
defined(dragonflybsd):
|
|
import posix, os
|
|
type
|
|
SendfileHeader* {.importc: "sf_hdtr",
|
|
header: """#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>""",
|
|
pure, final.} = object
|
|
|
|
proc osSendFile*(outfd, infd: cint, offset: uint, size: uint,
|
|
hdtr: ptr SendfileHeader, sbytes: ptr uint,
|
|
flags: int): int {.importc: "sendfile",
|
|
header: """#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>""".}
|
|
|
|
proc sendfile*(outfd, infd: int, offset: int, count: var int): int =
|
|
var o = 0'u
|
|
let res = osSendFile(cint(infd), cint(outfd), uint(offset), uint(count), nil,
|
|
addr o, 0)
|
|
if res >= 0:
|
|
count = int(o)
|
|
0
|
|
else:
|
|
let err = osLastError()
|
|
count =
|
|
if int(err) == EAGAIN: int(o)
|
|
else: 0
|
|
-1
|
|
|
|
elif defined(macosx):
|
|
import posix, os
|
|
type
|
|
SendfileHeader* {.importc: "struct sf_hdtr",
|
|
header: """#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>""",
|
|
pure, final.} = object
|
|
|
|
proc osSendFile*(fd, s: cint, offset: int, size: ptr int,
|
|
hdtr: ptr SendfileHeader,
|
|
flags: int): int {.importc: "sendfile",
|
|
header: """#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>""".}
|
|
|
|
proc sendfile*(outfd, infd: int, offset: int, count: var int): int =
|
|
var o = count
|
|
let res = osSendFile(cint(infd), cint(outfd), offset, addr o, nil, 0)
|
|
if res >= 0:
|
|
count = int(o)
|
|
0
|
|
else:
|
|
let err = osLastError()
|
|
count =
|
|
if int(err) == EAGAIN: int(o)
|
|
else: 0
|
|
-1
|