Skip to content

Commit

Permalink
Fix or deadlock issue. (#517)
Browse files Browse the repository at this point in the history
* Fix `or` should not create future with OwnCancelSchedule flag set.

* Fix `CancelledError` missing from raises list when both futures has empty raises list.

* Fix macros tests.
  • Loading branch information
cheatfate authored Mar 5, 2024
1 parent 5dfa3fd commit 1eb834a
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 6 deletions.
9 changes: 4 additions & 5 deletions chronos/internal/asyncfutures.nim
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ template orImpl*[T, Y](fut1: Future[T], fut2: Future[Y]): untyped =
fut2.addCallback(cb)

retFuture.cancelCallback = cancellation
return retFuture
retFuture

proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## Returns a future which will complete once either ``fut1`` or ``fut2``
Expand All @@ -801,7 +801,7 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## completed, the result future will also be completed.
##
## If cancelled, ``fut1`` and ``fut2`` futures WILL NOT BE cancelled.
var retFuture = newFuture[void]("chronos.or")
var retFuture = newFuture[void]("chronos.or()")
orImpl(fut1, fut2)


Expand Down Expand Up @@ -1665,10 +1665,9 @@ proc `or`*[T, Y, E1, E2](
fut1: InternalRaisesFuture[T, E1],
fut2: InternalRaisesFuture[Y, E2]): auto =
type
InternalRaisesFutureRaises = union(E1, E2)
InternalRaisesFutureRaises = union(E1, E2).union((CancelledError,))

let
retFuture = newFuture[void]("chronos.wait()", {FutureFlag.OwnCancelSchedule})
let retFuture = newFuture[void]("chronos.or()", {})
orImpl(fut1, fut2)

proc wait*(fut: InternalRaisesFuture, timeout = InfiniteDuration): auto =
Expand Down
13 changes: 13 additions & 0 deletions tests/testbugs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ suite "Asynchronous issues test suite":
await server.closeWait()
return true

proc testOrDeadlock(): Future[bool] {.async.} =
proc f(): Future[void] {.async.} =
await sleepAsync(2.seconds) or sleepAsync(1.seconds)
let fx = f()
try:
await fx.cancelAndWait().wait(2.seconds)
except AsyncTimeoutError:
return false
true

test "Issue #6":
check waitFor(issue6()) == true

Expand All @@ -152,3 +162,6 @@ suite "Asynchronous issues test suite":

test "IndexError crash test":
check waitFor(testIndexError()) == true

test "`or` deadlock [#516] test":
check waitFor(testOrDeadlock()) == true
2 changes: 1 addition & 1 deletion tests/testmacro.nim
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ suite "Exceptions tracking":
proc testit2 {.async: (raises: [IOError]).} =
raise (ref IOError)()

proc test {.async: (raises: [ValueError, IOError]).} =
proc test {.async: (raises: [CancelledError, ValueError, IOError]).} =
await testit() or testit2()

proc noraises() {.raises: [].} =
Expand Down

0 comments on commit 1eb834a

Please sign in to comment.