From 2ecc5500c295d41f1722f5292c80c1e7dfa9543e Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Tue, 21 Apr 2020 07:07:49 +0300 Subject: [PATCH] Undeprecate `or` operation. (#93) * Undeprecate `or` operation. Fix `or` for already finished futures. Add tests. * Bump version to 2.3.9. --- chronos.nimble | 2 +- chronos/asyncfutures2.nim | 33 +++++++++++++---- tests/testfut.nim | 75 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 8 deletions(-) diff --git a/chronos.nimble b/chronos.nimble index 73936d8..f67945e 100644 --- a/chronos.nimble +++ b/chronos.nimble @@ -1,5 +1,5 @@ packageName = "chronos" -version = "2.3.8" +version = "2.3.9" author = "Status Research & Development GmbH" description = "Chronos" license = "Apache License 2.0 or MIT" diff --git a/chronos/asyncfutures2.nim b/chronos/asyncfutures2.nim index 4e52a4d..c222788 100644 --- a/chronos/asyncfutures2.nim +++ b/chronos/asyncfutures2.nim @@ -490,13 +490,15 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {. retFuture.cancelCallback = cancellation return retFuture -proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {. - deprecated: "Use one[T](varargs[Future[T]])".} = +proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = ## Returns a future which will complete once either ``fut1`` or ``fut2`` ## 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. - var retFuture = newFuture[void]("chronos.`or`") + var retFuture = newFuture[void]("chronos.or") proc cb(udata: pointer) {.gcsafe.} = if not(retFuture.finished()): var fut = cast[FutureBase](udata) @@ -504,16 +506,33 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] {. fut2.removeCallback(cb) else: fut1.removeCallback(cb) - if fut.failed(): retFuture.fail(fut.error) - else: retFuture.complete() - fut1.callback = cb - fut2.callback = cb + if fut.failed(): + retFuture.fail(fut.error) + else: + retFuture.complete() proc cancellation(udata: pointer) {.gcsafe.} = # On cancel we remove all our callbacks only. fut1.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 return retFuture diff --git a/tests/testfut.nim b/tests/testfut.nim index ec76bb3..a82b9ce 100644 --- a/tests/testfut.nim +++ b/tests/testfut.nim @@ -684,6 +684,76 @@ suite "Future[T] behavior test suite": result = fut1.finished() and not(fut1.failed()) and fut1.read() == f10 and 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 = var completed = 0 @@ -907,6 +977,11 @@ suite "Future[T] behavior test suite": test "one() already completed test": check testOneCompleted() == true + test "or() test": + check testOr() == true + test "or() already completed test": + check testOrCompleted() == true + test "cancel() async procedure test": check testCancelIter() == true test "cancelAndWait() test":