Undeprecate or operation. (#93)

* Undeprecate `or` operation.
Fix `or` for already finished futures.
Add tests.

* Bump version to 2.3.9.
This commit is contained in:
Eugene Kabanov 2020-04-21 07:07:49 +03:00 committed by GitHub
parent 9ea1017a06
commit 2ecc5500c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 8 deletions

View File

@ -1,5 +1,5 @@
packageName = "chronos" packageName = "chronos"
version = "2.3.8" version = "2.3.9"
author = "Status Research & Development GmbH" author = "Status Research & Development GmbH"
description = "Chronos" description = "Chronos"
license = "Apache License 2.0 or MIT" license = "Apache License 2.0 or MIT"

View File

@ -490,13 +490,15 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {.
retFuture.cancelCallback = cancellation retFuture.cancelCallback = cancellation
return retFuture return retFuture
proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {. proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
deprecated: "Use one[T](varargs[Future[T]])".} =
## Returns a future which will complete once either ``fut1`` or ``fut2`` ## Returns a future which will complete once either ``fut1`` or ``fut2``
## complete. ## complete.
## ##
## If ``fut1`` or ``fut2`` will fail, result future will also fail with an
## error stored in ``fut1`` or ``fut2`` respectively.
##
## If cancelled, ``fut1`` and ``fut2`` futures WILL NOT BE cancelled. ## If cancelled, ``fut1`` and ``fut2`` futures WILL NOT BE cancelled.
var retFuture = newFuture[void]("chronos.`or`") var retFuture = newFuture[void]("chronos.or")
proc cb(udata: pointer) {.gcsafe.} = proc cb(udata: pointer) {.gcsafe.} =
if not(retFuture.finished()): if not(retFuture.finished()):
var fut = cast[FutureBase](udata) var fut = cast[FutureBase](udata)
@ -504,16 +506,33 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {.
fut2.removeCallback(cb) fut2.removeCallback(cb)
else: else:
fut1.removeCallback(cb) fut1.removeCallback(cb)
if fut.failed(): retFuture.fail(fut.error) if fut.failed():
else: retFuture.complete() retFuture.fail(fut.error)
fut1.callback = cb else:
fut2.callback = cb retFuture.complete()
proc cancellation(udata: pointer) {.gcsafe.} = proc cancellation(udata: pointer) {.gcsafe.} =
# On cancel we remove all our callbacks only. # On cancel we remove all our callbacks only.
fut1.removeCallback(cb) fut1.removeCallback(cb)
fut2.removeCallback(cb) fut2.removeCallback(cb)
if fut1.finished():
if fut1.failed():
retFuture.fail(fut1.error)
else:
retFuture.complete()
return retFuture
if fut2.finished():
if fut2.failed():
retFuture.fail(fut2.error)
else:
retFuture.complete()
return retFuture
fut1.addCallback(cb)
fut2.addCallback(cb)
retFuture.cancelCallback = cancellation retFuture.cancelCallback = cancellation
return retFuture return retFuture

View File

@ -684,6 +684,76 @@ suite "Future[T] behavior test suite":
result = fut1.finished() and not(fut1.failed()) and fut1.read() == f10 and result = fut1.finished() and not(fut1.failed()) and fut1.read() == f10 and
fut2.finished() and not(fut2.failed()) and fut2.read() == f21 fut2.finished() and not(fut2.failed()) and fut2.read() == f21
proc waitForNeLocal[T](fut: Future[T]): Future[T] =
## **Blocks** the current thread until the specified future completes.
while not(fut.finished()):
poll()
result = fut
proc testOr(): bool =
proc client1() {.async.} =
await sleepAsync(200.milliseconds)
proc client2() {.async.} =
await sleepAsync(300.milliseconds)
proc client3() {.async.} =
await sleepAsync(100.milliseconds)
if true:
raise newException(ValueError, "")
proc client4() {.async.} =
await sleepAsync(400.milliseconds)
if true:
raise newException(KeyError, "")
var f1 = waitForNeLocal(client1() or client2())
var f2 = waitForNeLocal(client2() or client1())
var f3 = waitForNeLocal(client1() or client4())
var f4 = waitForNeLocal(client2() or client4())
var f5 = waitForNeLocal(client1() or client3())
var f6 = waitForNeLocal(client3() or client1())
var f7 = waitForNeLocal(client2() or client4())
var f8 = waitForNeLocal(client4() or client2())
var f9 = waitForNeLocal(client3() or client4())
var f10 = waitForNeLocal(client4() or client3())
result = (f1.finished() and not(f1.failed())) and
(f2.finished() and not(f2.failed())) and
(f3.finished() and not(f3.failed())) and
(f4.finished() and not(f4.failed())) and
(f5.finished() and f5.failed()) and
(f6.finished() and f6.failed()) and
(f7.finished() and not(f7.failed())) and
(f8.finished() and not(f8.failed())) and
(f9.finished() and f9.failed()) and
(f10.finished() and f10.failed())
proc testOrCompleted(): bool =
proc client1(): Future[int] {.async.} =
result = 1
proc client2(): Future[int] {.async.} =
if true:
raise newException(ValueError, "")
proc client3(): Future[int] {.async.} =
await sleepAsync(100.milliseconds)
result = 3
var f1 = client1() or client2()
var f2 = client1() or client3()
var f3 = client2() or client3()
var f4 = client2() or client1()
var f5 = client3() or client1()
var f6 = client3() or client2()
result = (f1.finished() and not(f1.failed())) and
(f2.finished() and not(f2.failed())) and
(f3.finished() and f3.failed()) and
(f4.finished() and f4.failed()) and
(f5.finished() and not(f5.failed())) and
(f6.finished() and f6.failed())
proc testCancelIter(): bool = proc testCancelIter(): bool =
var completed = 0 var completed = 0
@ -907,6 +977,11 @@ suite "Future[T] behavior test suite":
test "one() already completed test": test "one() already completed test":
check testOneCompleted() == true check testOneCompleted() == true
test "or() test":
check testOr() == true
test "or() already completed test":
check testOrCompleted() == true
test "cancel() async procedure test": test "cancel() async procedure test":
check testCancelIter() == true check testCancelIter() == true
test "cancelAndWait() test": test "cancelAndWait() test":