Strict Future value / error access

Strict future access is now always enabled, making it a panic to access
value/error when the Future is still pending - `read` and `readError`
raise catchable exceptions instead which may be preferable to use.

* remove `chronosStrictFutureAccess` enabling its functionality by
default
* disallow setting a cancellation callback on a non-pending future
This commit is contained in:
Jacek Sieka 2023-11-15 13:51:16 +01:00
parent 1306170255
commit e1c26a9cc9
No known key found for this signature in database
GPG Key ID: A1B09461ABB656B8
3 changed files with 14 additions and 25 deletions

View File

@ -4,10 +4,6 @@
## in transition periods leading up to a breaking release that starts enforcing ## in transition periods leading up to a breaking release that starts enforcing
## them and removes the option. ## them and removes the option.
## ##
## `chronosPreviewV4` is a preview flag to enable v4 semantics - in particular,
## it enables strict exception checking and disables parts of the deprecated
## API and other changes being prepared for the upcoming release
##
## `chronosDebug` can be defined to enable several debugging helpers that come ## `chronosDebug` can be defined to enable several debugging helpers that come
## with a runtime cost - it is recommeneded to not enable these in production ## with a runtime cost - it is recommeneded to not enable these in production
## code. ## code.
@ -26,8 +22,6 @@ const
## ##
## `Exception` handling may be removed in future chronos versions. ## `Exception` handling may be removed in future chronos versions.
chronosStrictFutureAccess* {.booldefine.}: bool = defined(chronosPreviewV4)
chronosStackTrace* {.booldefine.}: bool = defined(chronosDebug) chronosStackTrace* {.booldefine.}: bool = defined(chronosDebug)
## Include stack traces in futures for creation and completion points ## Include stack traces in futures for creation and completion points

View File

@ -209,31 +209,27 @@ func value*[T: not void](future: Future[T]): lent T =
## ##
## See `read` for a version that raises a catchable error when future ## See `read` for a version that raises a catchable error when future
## has not completed. ## has not completed.
when chronosStrictFutureAccess: if not future.completed():
if not future.completed(): raiseFutureDefect("Future not completed while accessing value", future)
raiseFutureDefect("Future not completed while accessing value", future)
future.internalValue future.internalValue
func value*(future: Future[void]) = func value*(future: Future[void]) =
## Return the value in a completed future - raises Defect when ## Raises Defect when `fut.completed()` is `false`.
## `fut.completed()` is `false`.
## ##
## See `read` for a version that raises a catchable error when future ## See `read` for a version that raises a catchable error when future
## has not completed. ## has not completed.
when chronosStrictFutureAccess: if not future.completed():
if not future.completed(): raiseFutureDefect("Future not completed while accessing value", future)
raiseFutureDefect("Future not completed while accessing value", future)
func error*(future: FutureBase): ref CatchableError = func error*(future: FutureBase): ref CatchableError =
## Return the error of `future`, or `nil` if future did not fail. ## Return the error of `future`, or `nil` if future did not fail.
## ##
## See `readError` for a version that raises a catchable error when the ## See `readError` for a version that raises a catchable error when the
## future has not failed. ## future has not failed.
when chronosStrictFutureAccess: if not future.failed() and not future.cancelled():
if not future.failed() and not future.cancelled(): raiseFutureDefect(
raiseFutureDefect( "Future not failed/cancelled while accessing error", future)
"Future not failed/cancelled while accessing error", future)
future.internalError future.internalError

View File

@ -283,10 +283,9 @@ proc tryCancel(future: FutureBase, loc: ptr SrcLoc): bool =
if not(isNil(future.internalChild)): if not(isNil(future.internalChild)):
# If you hit this assertion, you should have used the `CancelledError` # If you hit this assertion, you should have used the `CancelledError`
# mechanism and/or use a regular `addCallback` # mechanism and/or use a regular `addCallback`
when chronosStrictFutureAccess: doAssert future.internalCancelcb.isNil,
doAssert future.internalCancelcb.isNil, "futures returned from `{.async.}` functions must not use " &
"futures returned from `{.async.}` functions must not use " & "`cancelCallback`"
"`cancelCallback`"
tryCancel(future.internalChild, loc) tryCancel(future.internalChild, loc)
else: else:
if not(isNil(future.internalCancelcb)): if not(isNil(future.internalCancelcb)):
@ -353,9 +352,9 @@ proc `cancelCallback=`*(future: FutureBase, cb: CallbackFunc) =
## This callback will be called immediately as ``future.cancel()`` invoked and ## This callback will be called immediately as ``future.cancel()`` invoked and
## must be set before future is finished. ## must be set before future is finished.
when chronosStrictFutureAccess: doAssert not future.finished(),
doAssert not future.finished(), "cancellation callback must be set before finishing the future"
"cancellation callback must be set before finishing the future"
future.internalCancelcb = cb future.internalCancelcb = cb
{.push stackTrace: off.} {.push stackTrace: off.}