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:
parent
62e1b3e2e3
commit
72b4912c84
3 changed files with 74 additions and 20 deletions
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue