From 774269f2f0468598f89133a9a76c5c67234acfdc Mon Sep 17 00:00:00 2001 From: cheatfate Date: Thu, 25 Oct 2018 22:59:40 +0300 Subject: [PATCH] Fix `sendfile` behavior on BSD/MacOSX. --- asyncdispatch2/sendfile.nim | 51 +++++++++++++++++++--------- asyncdispatch2/transports/stream.nim | 20 +++++------ tests/teststream.nim | 3 +- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/asyncdispatch2/sendfile.nim b/asyncdispatch2/sendfile.nim index f9709f5..e6c38a2 100644 --- a/asyncdispatch2/sendfile.nim +++ b/asyncdispatch2/sendfile.nim @@ -10,7 +10,7 @@ ## This module provides cross-platform wrapper for ``sendfile()`` syscall. when defined(nimdoc): - proc sendfile*(outfd, infd: int, offset: int, count: int): int = + 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 @@ -26,11 +26,13 @@ when defined(nimdoc): ## 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. ## ## If the transfer was successful, the number of bytes written to ``outfd`` - ## is 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. + ## 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. @@ -39,13 +41,16 @@ when defined(linux) or defined(android): proc osSendFile*(outfd, infd: cint, offset: ptr int, count: int): int {.importc: "sendfile", header: "".} - proc sendfile*(outfd, infd: int, offset: int, count: int): int = + proc sendfile*(outfd, infd: int, offset: int, count: var int): int = var o = offset result = osSendFile(cint(outfd), cint(infd), addr o, count) + if result >= 0: + count = result + result = 0 elif defined(freebsd) or defined(openbsd) or defined(netbsd) or defined(dragonflybsd): - + import posix, os type SendfileHeader* = object {.importc: "sf_hdtr", header: """#include @@ -60,16 +65,23 @@ elif defined(freebsd) or defined(openbsd) or defined(netbsd) or #include #include """.} - proc sendfile*(outfd, infd: int, offset: int, count: int): int = + proc sendfile*(outfd, infd: int, offset: int, count: var int): int = var o = 0'u - if osSendFile(cint(infd), cint(outfd), uint(offset), uint(count), nil, - addr o, 0) == 0: - result = int(o) + result = osSendFile(cint(infd), cint(outfd), uint(offset), uint(count), nil, + addr o, 0) + if result >= 0: + count = int(o) + result = 0 else: - result = -1 + let err = osLastError() + if int(err) == EAGAIN: + count = int(o) + result = 0 + else: + result = -1 elif defined(macosx): - import posix + import posix, os type SendfileHeader* = object {.importc: "sf_hdtr", header: """#include @@ -84,9 +96,16 @@ elif defined(macosx): #include #include """.} - proc sendfile*(outfd, infd: int, offset: int, count: int): int = + proc sendfile*(outfd, infd: int, offset: int, count: var int): int = var o = count - if osSendFile(cint(infd), cint(outfd), offset, addr o, nil, 0) == 0: - result = o + result = osSendFile(cint(infd), cint(outfd), offset, addr o, nil, 0) + if result >= 0: + count = int(o) + result = 0 else: - result = -1 + let err = osLastError() + if int(err) == EAGAIN: + count = int(o) + result = 0 + else: + result = -1 diff --git a/asyncdispatch2/transports/stream.nim b/asyncdispatch2/transports/stream.nim index 0a0aaac..4bb0a88 100644 --- a/asyncdispatch2/transports/stream.nim +++ b/asyncdispatch2/transports/stream.nim @@ -28,6 +28,7 @@ type buf: pointer # Writer buffer pointer buflen: int # Writer buffer size offset: uint # Writer vector offset + size: int # Original size writer: Future[int] # Writer vector completion Future TransportKind* {.pure.} = enum @@ -752,12 +753,6 @@ when defined(windows): else: - template getVectorBuffer(v: untyped): pointer = - cast[pointer](cast[uint]((v).buf) + uint((v).boffset)) - - template getVectorLength(v: untyped): int = - cast[int]((v).buflen - int((v).boffset)) - template initBufferStreamVector(v, p, n, t: untyped) = (v).kind = DataBuffer (v).buf = cast[pointer]((p)) @@ -793,14 +788,17 @@ else: else: vector.writer.fail(getTransportOsError(err)) else: + var nbytes = cast[int](vector.buf) let res = sendfile(int(fd), cast[int](vector.buflen), int(vector.offset), - cast[int](vector.buf)) + nbytes) if res >= 0: - if cast[int](vector.buf) - res == 0: - vector.writer.complete(cast[int](vector.buf)) + if cast[int](vector.buf) - nbytes == 0: + vector.size += nbytes + vector.writer.complete(vector.size) else: - vector.shiftVectorFile(res) + vector.size += nbytes + vector.shiftVectorFile(nbytes) transp.queue.addFirst(vector) else: let err = osLastError() @@ -948,7 +946,7 @@ else: else: asyncCheck server.function(server, newStreamSocketTransport(sock, server.bufferSize, nil)) - break + break else: let err = osLastError() if int(err) == EINTR: diff --git a/tests/teststream.nim b/tests/teststream.nim index 7a1d515..2419005 100644 --- a/tests/teststream.nim +++ b/tests/teststream.nim @@ -684,8 +684,7 @@ when isMainModule: else: discard else: - test prefixes[i] & m4: - check waitFor(testSendFile(addresses[i])) == FilesCount + check waitFor(testSendFile(addresses[i])) == FilesCount test prefixes[i] & m15: var address: TransportAddress if addresses[i].family == AddressFamily.Unix: