From e42f877664bf19408a7b7438480ec0feba4d909f Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 31 Jul 2013 23:27:07 +0100 Subject: [PATCH] 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) --- lib/pure/asyncio.nim | 23 ++++++++++++++--------- tests/compile/tasyncitermacro.nim | 9 +++++++-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index ef3d1df71..3010e48ff 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -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.} = diff --git a/tests/compile/tasyncitermacro.nim b/tests/compile/tasyncitermacro.nim index faa383f1c..4b9b7b5b2 100644 --- a/tests/compile/tasyncitermacro.nim +++ b/tests/compile/tasyncitermacro.nim @@ -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: