Optimised `await` on custom async procs.

* The async proc is now invoked immediately after the 'await' request
is processed. This means that the iterator is executed until it yields
with a request which cannot be immediately satisfied (e.g. readLine)
after which point a
select() call will need to be made. Previously the async proc would only
be added to the list of requests, the event loop would need to start
over and thus would need to go
through each request again before invoking the iterator and reaching a
request which could not be immediately satisfied.
* This was implemented by recursively calling the processWorker
procedure in asyncio.nim. In the future it may be of benefit to also
perform this for reqReg.
* When the awaited user-defined async proc finishes, the parent worker
will be executed, the same optimisation has been used for that case.
* The way that this optimisation is implemented means that even multiple
levels of awaited user-defined procs will be quickly processed. (Look at
the tasyncitermacro test, the auth procs are arranged this way to test
this)
This commit is contained in:
Dominik Picheta 2013-07-31 23:27:07 +01:00
commit e42f877664
2 changed files with 21 additions and 11 deletions

View file

@ -671,9 +671,7 @@ proc processWorkers(d: PDispatcher) =
var newRequests: array[TRequestKind, seq[PWorker]] = d.requests
newRequests[reqNil] = @[]
for idle in d.requests[reqNil]:
proc processWorker(idle: PWorker) =
let req = idle.worker(idle.x)
if req != nil:
echo("Process workers, after exec: ", req.kind)
@ -684,13 +682,16 @@ proc processWorkers(d: PDispatcher) =
x: req)
newRequests[reqNil].add(newWorker)
of reqAwait:
# For efficiency lets execute this async proc now.
let awaitReq = req.worker(req)
let newWorker = PWorker(worker: req.worker, lastReq: awaitReq,
let newWorker = PWorker(worker: req.worker, lastReq: PRequest(kind: reqNil),
x: req, hasParent: true, parent: idle)
# The worker which ``await``-ed this user-defined async proc;
# will be re-added to ``d.requests`` when ``newWorker`` finishes.
newRequests[awaitReq.kind].add(newWorker)
# We call this proc recursively so that the execution of this
# worker begins and we can immediately satisfy it's async request.
# We do not need to add newWorker to newRequests manually as it will be
# added by ``processWorker`` if necessary automatically.
processWorker(newWorker)
else:
idle.lastReq = req
newRequests[req.kind].add(idle)
@ -698,10 +699,14 @@ proc processWorkers(d: PDispatcher) =
assert idle.worker.finished
if idle.hasParent:
# Re-add the parent worker, which is the worker which awaited this
# user-defined async proc which just finished.
newRequests[reqNil].add(idle.parent)
# user-defined async proc which just finished. Do this by calling
# processWorker recursively. Same way as above.
processWorker(idle.parent)
echo("Await finish: ", idle.parent.lastReq.kind)
for idle in d.requests[reqNil]:
processWorker(idle)
d.requests = newRequests
template popu(req) {.immediate, dirty.} =

View file

@ -1,8 +1,14 @@
import sockets, asyncio, strutils
proc auth(client: PAsyncSocket) {.async.} =
proc auth3(client: PAsyncSocket) {.async.} =
await send(client, "Auth\c\L")
proc auth2(client: PAsyncSocket) {.async.} =
await auth3(client)
proc auth(client: PAsyncSocket) {.async.} =
await auth2(client)
proc processRequest(client: PAsyncSocket, test: string, closeSock: bool = true) {.async.} =
assert test == "ahha"
assert client != nil
@ -11,7 +17,6 @@ proc processRequest(client: PAsyncSocket, test: string, closeSock: bool = true)
for i in 0 .. 10:
await auth(client)
#await send(client, "Auth\c\L")
await send(client, "Goodbye.\c\L")
if closeSock: