Add async IO operations with buffers on files and sockets

This commit is contained in:
Anatoly Galiulin 2016-08-31 11:18:36 +07:00
commit e4c46e6fba
4 changed files with 373 additions and 23 deletions

View file

@ -749,7 +749,7 @@ when defined(windows) or defined(nimdoc):
retFuture.complete("")
return retFuture
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
proc recvBuffer*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must
## at least be of that size. Returned future will complete once all the
@ -769,11 +769,11 @@ when defined(windows) or defined(nimdoc):
verifyPresence(socket)
assert SocketFlag.Peek notin flags, "Peek not supported on Windows."
var retFuture = newFuture[int]("recvInto")
var retFuture = newFuture[int]("recvBuffer")
#buf[] = '\0'
var dataBuf: TWSABuf
dataBuf.buf = buf
dataBuf.buf = cast[cstring](buf)
dataBuf.len = size.ULONG
var bytesReceived: Dword
@ -784,10 +784,7 @@ when defined(windows) or defined(nimdoc):
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
if bytesCount == 0 and dataBuf.buf[0] == '\0':
retFuture.complete(0)
else:
retFuture.complete(bytesCount)
retFuture.complete(bytesCount)
else:
if flags.isDisconnectionError(errcode):
retFuture.complete(0)
@ -819,6 +816,51 @@ when defined(windows) or defined(nimdoc):
retFuture.complete(bytesReceived)
return retFuture
proc sendBuffer*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all
## data has been sent.
## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object, you must use GC_ref/GC_unref calls
## to avoid early freeing of the buffer
verifyPresence(socket)
var retFuture = newFuture[void]("send")
var dataBuf: TWSABuf
dataBuf.buf = cast[cstring](buf)
dataBuf.len = size.ULONG
var bytesReceived, lowFlags: Dword
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
retFuture.complete()
else:
if flags.isDisconnectionError(errcode):
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
let ret = WSASend(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
lowFlags, cast[POVERLAPPED](ol), nil)
if ret == -1:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
GC_unref(ol)
if flags.isDisconnectionError(err):
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(err)))
else:
retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture
proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to ``socket``. The returned future will complete once all
@ -828,9 +870,9 @@ when defined(windows) or defined(nimdoc):
var dataBuf: TWSABuf
dataBuf.buf = data
dataBuf.len = data.len.ULONG
GC_ref(data) # we need to protect data until send operation is completed
# or failed.
dataBuf.len = data.len.ULONG
var bytesReceived, lowFlags: Dword
var ol = PCustomOverlapped()
@ -1403,9 +1445,9 @@ else:
addRead(socket, cb)
return retFuture
proc recvInto*(socket: AsyncFD, buf: cstring, size: int,
proc recvBuffer*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[int] =
var retFuture = newFuture[int]("recvInto")
var retFuture = newFuture[int]("recvBuffer")
proc cb(sock: AsyncFD): bool =
result = true
@ -1427,6 +1469,38 @@ else:
addRead(socket, cb)
return retFuture
proc sendBuffer*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send")
var written = 0
proc cb(sock: AsyncFD): bool =
result = true
let netSize = size-written
var d = cast[cstring](buf)
let res = send(sock.SocketHandle, addr d[written], netSize.cint,
MSG_NOSIGNAL)
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
if flags.isDisconnectionError(lastError):
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
else:
written.inc(res)
if res != netSize:
result = false # We still have data to send.
else:
retFuture.complete()
# TODO: The following causes crashes.
#if not cb(socket):
addWrite(socket, cb)
return retFuture
proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): Future[void] =
var retFuture = newFuture[void]("send")