yieldAsync() (#120)

This commit is contained in:
Ștefan Talpalaru 2020-08-10 15:31:21 +02:00 committed by GitHub
parent d0a17d551f
commit 284d677815
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 0 deletions

View File

@ -242,6 +242,53 @@ other specific exception, in order to avoid catching by mistake
`CancelledError` (object of `Exception`, used internally to propagate
cancellation).
### Yielding
If you need to give control back to the event loop, but there's no future you
need to `await` at that point, you can call `yieldAsync()` - a simple wrapper
around `sleepAsync()`:
```nim
template yieldAsync*() =
await sleepAsync(0.seconds)
```
This comes in handy when splitting a blocking task into smaller parts:
```nim
proc showMsg(after: int) {.async.} =
echo "begin showMsg()"
await sleepAsync(after.seconds)
echo "msg: after = ", after
let maxDuration = 10
proc heavy() {.async.} =
echo "begin showMsg(", after, ")"
let start = Moment.now()
var s: uint64
while true:
s += 1
# try commenting this out and see what happens
if s mod 1000000 == 0:
yieldAsync()
if (Moment.now() - start).seconds >= (maxDuration + 1):
break
echo "sum: ", s
proc p1() {.async.} =
var futures: seq[Future[void]]
for after in 1..maxDuration:
futures.add(showMsg(after))
futures.add(heavy())
await allFutures(futures)
waitFor p1()
```
## TODO
* Pipe/Subprocess Transports.
* Multithreading Stream/Datagram servers

View File

@ -891,6 +891,10 @@ proc sleepAsync*(ms: int): Future[void] {.
inline, deprecated: "Use sleepAsync(Duration)".} =
result = sleepAsync(ms.milliseconds())
# This can't be an async proc, because its `await` needs to modify the caller.
template yieldAsync*() =
await sleepAsync(0.seconds)
proc withTimeout*[T](fut: Future[T], timeout: Duration): Future[bool] =
## Returns a future which will complete once ``fut`` completes or after
## ``timeout`` milliseconds has elapsed.

View File

@ -969,6 +969,38 @@ suite "Future[T] behavior test suite":
proc testCancellationRace(): bool =
waitFor(testCancellationRaceAsync())
proc testYieldAsync(): Future[bool] {.async.} =
proc showMsg(after: int, results: ptr seq[string]) {.async.} =
await sleepAsync(after.seconds)
results[].add("after " & $after)
let maxDuration = 2
proc heavy(results: ptr seq[string]) {.async.} =
let start = Moment.now()
var s: uint64
while true:
s += 1
if s mod 1000000 == 0:
yieldAsync()
if (Moment.now() - start).seconds >= (maxDuration + 1):
break
results[].add("heavy")
var
futures: seq[Future[void]]
results: seq[string]
for after in 1..maxDuration:
futures.add(showMsg(after, results.addr))
futures.add(heavy(results.addr))
await allFutures(futures)
return results == @["after 1", "after 2", "heavy"]
proc testYield(): bool =
waitFor(testYieldAsync())
test "Async undefined behavior (#7758) test":
check test1() == true
test "Immediately completed asynchronous procedure test":
@ -1022,3 +1054,6 @@ suite "Future[T] behavior test suite":
check testWithTimeout() == true
test "Cancellation race test":
check testCancellationRace() == true
test "Yielding test":
check testYield() == true