Introduce FutureVar[T] to make recvLineInto safer.

FutureVar[T] is a new distinct Future type which is designed to be used
for situations where the highest performance is needed. It reduces the
number of Future allocations needed. It acts as a replacement for
'var' params in async procs.

This commit modifies @def-'s PR in order to make it safer. The recvLineInto
procedure has been modified to take a ``FutureVar[string]`` param instead of a
``ptr string`` param.
This commit is contained in:
Dominik Picheta 2015-04-24 17:56:04 +01:00
commit 72b4912c84
3 changed files with 74 additions and 20 deletions

View file

@ -419,10 +419,13 @@ proc accept*(socket: AsyncSocket,
retFut.complete(future.read.client)
return retFut
proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
flags = {SocketFlag.SafeDisconn}) {.async.} =
## Reads a line of data from ``socket`` into ``resString``.
##
## The ``resString`` future and the string value contained within must both
## be initialised.
##
## If a full line is read ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line``
## will be set to it.
@ -438,16 +441,23 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the
## protocol uses ``\r\L`` to delimit a new line.
assert SocketFlag.Peek notin flags ## TODO:
assert(not resString.mget.isNil(),
"String inside resString future needs to be initialised")
result = newFuture[void]("asyncnet.recvLineInto")
# TODO: Make the async transformation check for FutureVar params and complete
# them when the result future is completed.
# Can we replace the result future with the FutureVar?
template addNLIfEmpty(): stmt =
if resString[].len == 0:
resString[].add("\c\L")
if resString.mget.len == 0:
resString.mget.add("\c\L")
if socket.isBuffered:
if socket.bufLen == 0:
let res = socket.readIntoBuf(flags)
if res == 0:
resString.complete()
return
var lastR = false
@ -455,7 +465,8 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
if socket.currPos >= socket.bufLen:
let res = socket.readIntoBuf(flags)
if res == 0:
resString[].setLen(0)
resString.mget().setLen(0)
resString.complete()
return
case socket.buffer[socket.currPos]
@ -465,13 +476,15 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
of '\L':
addNLIfEmpty()
socket.currPos.inc()
resString.complete()
return
else:
if lastR:
socket.currPos.inc()
resString.complete()
return
else:
resString[].add socket.buffer[socket.currPos]
resString.mget.add socket.buffer[socket.currPos]
socket.currPos.inc()
else:
var c = ""
@ -479,18 +492,23 @@ proc recvLineInto*(socket: AsyncSocket, resString: ptr string,
let recvFut = recv(socket, 1, flags)
c = recvFut.read()
if c.len == 0:
resString[].setLen(0)
resString.mget.setLen(0)
resString.complete()
return
if c == "\r":
let recvFut = recv(socket, 1, flags) # Skip \L
c = recvFut.read()
assert c == "\L"
addNLIfEmpty()
resString.complete()
return
elif c == "\L":
addNLIfEmpty()
resString.complete()
return
resString[].add c
resString.mget.add c
resString.complete()
proc recvLine*(socket: AsyncSocket,
flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} =
@ -516,8 +534,10 @@ proc recvLine*(socket: AsyncSocket,
result.add("\c\L")
assert SocketFlag.Peek notin flags ## TODO:
result = ""
await socket.recvLineInto(addr result, flags)
# TODO: Optimise this.
var resString = newFutureVar[string]("asyncnet.recvLine")
await socket.recvLineInto(resString, flags)
result = resString.mget()
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} =
## Marks ``socket`` as accepting connections.